[Android问答] 如何应对Activity进程被杀?

我们要了解Android手机开发与桌面开发有一个主要不同之处:通常在一部Android手机里同时运行着多个应用(app),每个app对应一个系统进程,当系统需要更多的资源(如内存)而空闲资源不足时,Android系统就会选择杀掉一些“低优先级”的进程以便释放所需资源。

Android系统是如何确定进程优先级的高低的呢?

  • 如果一个app正在与用户交互,那么它所在的进程具有最高优先级;
  • 其次,如果一个app是可见的,例如被一个对话框部分遮挡,它所在进程具有第二高的优先级;
  • 再次,如果app当前是不可见的,也就是被切换到了后台,则它所在进程具有第三高的优先级;这里要补充一点,如果这个后台app启动了一个service,则它比一般的后台app优先级高一些。
  • 最后,如果一个进程里没有包含任何app,这个进程的优先级是最低的。

当系统资源严重不足时,任何一个进程都有可能被杀掉,而当用户想回到一个已经不存在于内存中的Activity时,系统只得新建一个这样的Activity对象并调用它的onCreate()方法进行恢复。所以有时出现这种状况:一个app大部分时间运行很好,偶尔在切换Activity时出现空指针异常导致强制关闭,这多半是在onCreate()方法里使用了已经被重置为空的对象(例如intent里的变量)造成的。即使不出现异常,也会造成表单数据丢失等严重影响使用体验的问题。

要解决这类问题,切不可抱“现在手机内存大,进程一般不会被杀掉”这种侥幸心理,而应该以“应用随时都会被杀掉”的态度来谨慎处理,下面介绍Google建议的方式。

解决方法

由于Activity随时可能需要重建,所以我们要做的事情就是在适当的位置将Activity所需数据进行持久化(从ram复制到rom或sd卡),并在onCreate()方法里利用这些数据恢复现场。

Activity有两类数据需要进行持久化处理:“文档类型数据”和“内部状态类型数据”,前者例如用户正在编辑的表单,后者如用户偏好。

一、为了持久化文档类型的数据,Google建议使用”即时生效”(edit in place)的编辑策略,具体的方式如下:

  • 用户新建文档时,在SQLite数据库(根据需要也可以使用preference)里也立即新建一条记录。(与此相对的方式是:为用户提供一个“保存”按钮,只有当用户按下按钮时才将文档保存到数据库。)
  • 当用户离开当前Activity时,onPause()方法会被触发,在这个方法里将当前正在编辑的文档持久化到数据库。这样一来,如果用户是从这个Activity切换到另一个相关Activity,仍然可以看到刚刚保存的内容。

这种方式可以最大限度避免数据丢失,只要onPause()方法被触发执行,即使Activity所在进程被系统kill掉也不会造成数据丢失。唯一要注意的是,界面上最好能提供一个“取消”按钮或菜单,以便让用户可以选择不保存对文档的更改。

二、为了持久化内部状态类型的数据,可以在onPause()里使用Activity#getPreferences(int)方法,这个方法返回SharedPreferences类型的对象,利用它可以记录用户对这个Activity的偏好信息。例如一个日历应用,用户可以选择显示为周视图或月视图,这样的信息作为内部状态记录到SharedPreferences对象以后,下次再打开这个Activity时就可以按用户上次的选择来显示日历了。

以下代码来自官方文档,演示了如何持久化并恢复一个Activity的当前显示模式:

 public class CalendarActivity extends Activity {
     ...

     static final int DAY_VIEW_MODE = 0;
     static final int WEEK_VIEW_MODE = 1;

     private SharedPreferences mPrefs;
     private int mCurViewMode;

     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         //取回之前持久化的日历显示模式
         SharedPreferences mPrefs = getSharedPreferences();
         mCurViewMode = mPrefs.getInt("view_mode", DAY_VIEW_MODE);
     }

     protected void onPause() {
         super.onPause();

         //持久化日历显示模式 
         SharedPreferences.Editor ed = mPrefs.edit();
         ed.putInt("view_mode", mCurViewMode);
         ed.commit();
     }
 }

是有点麻烦,但为了能让app稳定运行也值了。

有些同学要问了,为什么是在onPause()里持久化而不是在onSaveInstanceState()里?官方文档有下面一段话简要解释了原因,即前者比后者更可靠,因为onSaveInstanceState()不属于Activity生命周期的一部分。——既然如此,我想不出onSaveInstanceState()还有什么其他用途了,大家干脆忘了它吧,还有onRestoreInstanceState()。

Note that it is important to save persistent data in onPause() instead of onSaveInstanceState(Bundle) because the latter is not part of the lifecycle callbacks, so will not be called in every situation as described in its documentation.

注1:关于上面这段话,网上存有一些争议,我个人还是比较倾向在onPause()里做持久化——既可靠又好记,唯一的缺点是调用次数稍多。

注2:从Android 3.0(HoneyComb)版本开始,Activity进程在被系统杀掉之前,将保证onStop()方法先执行完成,因此如果我们开发的应用只运行在3.0以上,可以把持久化工作放在onStop()里以减少持久化的次数。

最佳实践

不要抱侥幸心理,你的Activity随时可能被销毁。

解决方法:在onPause()里持久化Activity数据,在onCreate()里恢复现场

参考资料:

ActivitySaving Activity state in Android

(搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2012/11/08/2759948.html

[Android问答] 如何理解Activity生命周期?

Android官方文档里对Activity的生命周期有比较详尽的描述,但由于资源回收机制带来不确定性,我们的程序运行结果常常与预期的不符,而调试这类问题又十分消耗时间和精力。解决的根本办法还是要理解透Activity的生命周期及相关内容,这篇帖子着重介绍Activity生命周期本身,之后会用一两篇帖子来介绍如何处理异常的状态变化。

下图是官方文档里的Activity生命周期图,其中彩色标出的四个框是Activity的四种状态,当Activity的状态改变时会触发一个或多个onXXX()方法。

file

onCreate()

当Acitivity第一次被创建时触发,一般在这里要做的事情包括创建视图(setContentView())、向视图填充必要的数据等等。

onRestart()

这个我比较少用到,按文档上的介绍,如果Activity之前被stop过,那么下一次onStart()方法之前会先触发这个方法。

onStart()

只要Activity从不可见变成可见,就会触发到这个方法,但被AlertDialog遮挡/显示的情况不算在内。

onResume()

当Activity来到最上层的时候,也就是开始与用户直接交互时,触发这个方法。例如本来Activity被一个AlertDialog遮挡,当这个AlertDialog消失时,onResume()方法就被触发。

onPause()

和onResume()的触发条件刚好相反,如果Activity本来在最上层,当它要让出最上层的位置时会触发这个方法。onPause()和onResume()是被触发最频繁的两个方法,所以在这里不应该执行过于消耗资源的方法。

onStop()

当有其他Activity覆盖了当前Activity时,不论另一个Activity是新开始的还是从下层移至最上层的,当前Activity的onStop()方法都会被触发。

onDestroy()

Activity生命周期的终点。有两种情况会导致它被触发:1)执行了Activity#finish()方法;2)Android系统由于资源不足等原因决定杀掉Activity所在进程。通过isFinishing()方法可以判断出是哪种情况。在这个方法里,我们一般要做的事情是释放Activity占有的资源,例如后台正在进行的下载线程等等。

最后,举个实际例子来说明,假设你有一个“首页Activity”和一个“编辑页Activity”。

  • 当用户点击首页里的“开始编辑”按钮时,首页的onPause()->onStart()onStop()依次触发,编辑页的onCreate()->onStart()->onResume()依次触发;(感谢James.H.Fu指出的错误)
  • 当用户在编辑页按下“返回”按钮时,编辑页的onPause()->onStop()依次触发,之后首页的onStart() -> onResume()依次触发;
  • 这时用户在首页按下“返回”按钮,首页的onPause()->onStop()->onDestroy()依次触发。

参考资料:

Activity Lifecycle
Simplest Android Activity Lifecycle
Activity lifecycle explained in details

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2012/11/07/2758276.html

[Android问答] px、dp和sp,这些单位有什么区别?

相信每个Android新手都会遇到这个问题,希望这篇帖子能让你不再纠结。

px:

即像素,1px代表屏幕上一个物理的像素点;

px单位不被建议使用,因为同样100px的图片,在不同手机上显示的实际大小可能不同,如下图所示(图片来自android developer guide,下同)。

file

偶尔用到px的情况,是需要画1像素表格线或阴影线的时候,用其他单位如dp会显得模糊。

dp:

这个是最常用但也最难理解的尺寸单位。它与“像素密度”密切相关,所以首先我们解释一下什么是像素密度。假设有一部手机,屏幕的物理尺寸为1.5英寸x2英寸,屏幕分辨率为240x320,则我们可以计算出在这部手机的屏幕上,每英寸包含的像素点的数量为240/1.5=160dpi(横向)或320/2=160dpi(纵向),160dpi就是这部手机的像素密度,像素密度的单位dpi是Dots Per Inch的缩写,即每英寸像素数量。横向和纵向的这个值都是相同的,原因是大部分手机屏幕使用正方形的像素点。

不同的手机/平板可能具有不同的像素密度,例如同为4寸手机,有480x320分辨率的也有800x480分辨率的,前者的像素密度就比较低。Android系统定义了四种像素密度:低(120dpi)、中(160dpi)、高(240dpi)和超高(320dpi),它们对应的dp到px的系数分别为0.75、1、1.5和2,这个系数乘以dp长度就是像素数。例如界面上有一个长度为“80dp”的图片,那么它在240dpi的手机上实际显示为80x1.5=120px,在320dpi的手机上实际显示为80x2=160px。如果你拿这两部手机放在一起对比,会发现这个图片的物理尺寸“差不多”,这就是使用dp作为单位的效果,见下图。

file

更新20140701: 是不是所有android手机的屏幕宽度用dp衡量都是固定值(例如320dp)呢?答案是否定的,如果写一个程序画宽度等于320dp的横线,在不同手机上运行,会发现在有些手机上横线比手机屏幕短,有些则比屏幕长,在平板上与手机上相比差别则更加明显。

dip:

与dp完全相同,只是名字不同而已。在早期的Android版本里多使用dip,后来为了与sp统一就建议使用dp这个名字了。

sp:

与缩放无关的抽象像素(Scale-independent Pixel)。sp和dp很类似但唯一的区别是,Android系统允许用户自定义文字尺寸大小(小、正常、大、超大等等),当文字尺寸是“正常”时1sp=1dp=0.00625英寸,而当文字尺寸是“大”或“超大”时,1sp>1dp=0.00625英寸。类似我们在windows里调整字体尺寸以后的效果——窗口大小不变,只有文字大小改变。

还有几个比较少用到的尺寸单位:

mm:

即毫米;

in:

即英寸,1英寸=2.54厘米(约);

pt:

1pt=1/72英寸=0.035厘米;

最佳实践:

文字的尺寸一律用sp单位,非文字的尺寸一律使用dp单位。例如textSize="16sp"、layout_width="60dp";偶尔需要使用px单位,例如需要在屏幕上画一条细的分隔线时:

<View layout_width="match_parent" layout_height="1px"/>

最后,推荐一张Android UI设计参考图:Android Design Cheat Sheet

参考资料:

Difference of px, dp, dip and sp in Android?
Supporting Multiple Screens

DisplayMetrics

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2012/11/06/2757300.html

Win7下禁止鼠标唤醒电脑

file

我的笔记本Win7系统连了一个无线鼠标,电脑待机后若不小心碰到鼠标就会被唤醒。

网上有一些文章说在“鼠标硬件->电源选项”里清除“允许鼠标唤醒电脑”选项,但在我这里完全不起作用。

看到其他人也有类似的问题(例如这个链接里),但没有发现进一步的解决方法。自己乱试了一通,最后当我清除掉“键盘”的“允许唤醒电脑”选项后,鼠标居然可以不唤醒电脑了——晕,但是目的达到了。

我用的是罗技的M215无线鼠标,猜测Windows7系统把鼠标的无线接收器当作键盘设备了。

有同样问题的不妨试试。

Amazon EC2配置步骤和一些问题

file

上次使用Amazon EC2的步骤没有记下来,导致这次配置新帐号时比较麻烦,这里把配置云服务器的常用操作记录在一起提高效率。2015/5/19注:最早我使用的是Amazon EC2,后来由于价格和SSD的原因改为使用DigitalOcean的服务,再后来由于国内访问速度原因改为使用阿里云的服务,但一直用CentOS 6.5 64bit的环境,所以很多步骤是相同的。

注册EC2

1、注册amazon aws帐号,需要一张信用卡和一个固定电话,过程不再赘述。此过程中可得到一个.pem文件。

2、进入aws management console,在EC2部分,点击launch instance按钮启动一个ec2 instance。综合价格和国内访问速度,我建议选择US-WEST, Oregon区域。使用cloudping这个在线工具可以实测连接速度。

3、 用puttygen(随putty安装)选择conversions->import key菜单项导入前面获得的.pem文件,然后点击save private key按钮即得到.ppk文件。(参考链接)

接下来是按需要配置instance,这里以免费的AMI为例,先运行“sudo su”进入root身份: 

配置基本环境

1、创建用户

> useradd xxx
> passwd xxx

2、设置时区

> cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime 

不过上面的方式重启后好像会失去效果,建议用下面的方式:

> ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime 

设置完成后最好重启服务器,否则一些服务(例如crond)日志里仍然使用原来的时区。参考链接

20150318注:还要注意/etc/sysconfig/clock文件(此文件表示硬件时钟使用的时区)里配置的内容,曾经有过一台机器是ZONE="America NewYork",导致tomcat里的时间不正确,改为ZONE=Etc/UTC后解决。

3、设置环境变量

/etc/profile.d下建立一个.sh文件,内容如下:

my_var=xxx
export my_var

注:阿里云服务器初始化和挂载数据盘的方法见这个链接

安装和配置MySQL

1、安装:

> yum install mysql mysql-server mysql-libs

数据库文件位于/var/lib/mysql, 配置文件是/etc/my.cnf

设置数据库的缺省字符集:在/etc/my.cnf里加入character_set_server=utf8(命令show variables like 'char%';可以查看当前值)。

为了避免8小时自动关闭连接,在my.cnf里的[mysqld]下(注意不要放在[mysqld_safe]下)增加interactive_timeout=288000wait_timeout=288000(两个必须同时改否则不生效),即把默认8小时改为80小时,一般够用了。  

若要修改表格列的编码方式,用下面命令:

alter table 'my_table' modify column 'my_column' varchar(45) character set utf8 not null;

启动MySQL服务:

> service mysqld start

2、创建数据库和导入数据:

先进入mysql命令行:

> mysql -uroot

在mysql命令行下,创建一个空的数据库:

> create database mydatabase;

创建所需用户同时授予权限: 

> grant all privileges ON mydatabase.* TO 'username'@'localhost' identified by 'mypassword' with grant option;
> flush privileges;

向新建的数据库里导入所需数据(先退出mysql命令环境回到bash下,输入文件一般由mysqldump命令导出得到):

> mysql -uroot mydatabase < myexportedfile.sql

附导出mysql数据库的命令行:

> mysqldump -uroot -p mydb | gzip > my_dumped_file.sql.gz

若导出时想排除某些个表:

> mysqldump -uroot -p mydb --ignore-table=mydb.table1 --ignore-table=mydb.table2 | gzip > my_dumped_file.sql.gz

另,在mysql命令行里使用select ... into outfile和load data命令也可实现数据的导入导出,示例如下:

SELECT * INTO OUTFILE 'c:/temp/my_file.txt' FROM my_table;

恢复时(注意要加LOCAL关键字,否则会提示Can't get stat of 'xxx'错误,参考链接):

LOAD DATA LOCAL INFILE 'c:/temp/my_file.txt' REPLACE INTO TABLE my_table character set utf8;

3、如果想在浏览器里管理数据,一般使用phpMyAdmin,步骤如下:

创建远程用户admin并授予所有权限:

> GRANT ALL PRIVILEGES ON *.* TO admin@"%" IDENTIFIED BY 'mypassword' WITH GRANT OPTION;

安装phpMyAdmin(需添加额外yum源):

> yum install phpmyadmin

配置ftp服务

这里我们使用vsftp。首先是安装,缺省安装目录在/etc/vsftpd

> yum install vsftpd

修改配置文件:

> vi /etc/vsftpd/vsftpd.conf

anonymous_enable=YES改为NO,在文件最后部分加上下面内容(防火墙里要开对应端口范围):

pasv_enable=YES
pasv_min_port=62222
pasv_max_port=63333

以上配置让vsftpd只接受pasv模式的连接,所以ftp客户端需要注意一下相应配置。 

有时上传大文件结束时提示timeout错误,文件越大出现概率也越大,调整了与timeout有关的参数后依然无效。暂时的解决方法是将文件分卷压缩,上传先合并(cat xxx.*>yyy)再解压缩。注意,分卷解压缩之前要保持各分卷文件的顺序,我曾遇到一个情况是当分卷多于100个时,因为xxx.z100排在了xxx.z11之前,导致合并后的.zip文件无法解压缩。这时,要手工把xxx.z11改名为xxx.z011,其余文件类似处理,可以用excel等工具批量生成命令行(从z01到z99共99个)。

配置Apache

安装:

> yum install httpd

启动Apache服务:

> service httpd start

设置Apache https

1、先安装mod_ssl模块

> yum install mod_ssl

注意:根据linux环境(mod_ssl环境?)不同,这里至少有两种情况:

a) mod_ssl安装后会在/etc/httpd/conf.d/下生成ssl.conf文件,里面已经有LoadModule语句,所以在httpd.conf里不用重复添加;

b) mod_ssl安装后得到的文件是/apache/conf/httpd-ssl.conf,可能需要在httpd.conf里手动添加LoadModule语句,并添加Include "/apache/conf/httpd-ssl.conf"语句。参考链接

2、创建证书

#生成1024位密钥(至少使用1024否则提示RSA_sign:digest too big for rsa key错误,参考链接)
>openssl genrsa 1024 > server.key 
#生成证书请求文件,这一步要按要求回答若干问题并设置一个challenge password
>openssl req -new -key server.key > server.csr 
#生成证书,365是有效天数
>openssl req -x509 -days 365 -key server.key -in server.csr > server.crt

3、配置apache支持https

修改ssl.conf(或有些linux版本的httpd-ssl.conf)文件,当然上一步骤里得到的server.keyserver.crt文件要先拷贝到对应目录。:

SSLCertificateFile /etc/httpd/conf.d/server.crt
SSLCertificateKeyFile/etc/httpd/conf.d/server.key 

最后,在ssl.conf里把DocumentRoot、ServerName按实际修改(和httpd.conf内容相同)。如果与tomcat集成过,记得把JkMount也加上。

配置Tomcat

安装,缺省安装目录在/usr/share/tomcat6

> yum install tomcat6

提醒一下:如果还没有安装过jdk就启动tomcat,会直接在命令行里报Error Code 4错误,yum安装java-1.7.0-openjdk即可解决。

如果你的webapp里有连接池等资源,需要在/usr/share/tomcat6/conf/server.xml里配置好,以下是一个例子:

<Context path="" docBase="/var/www/html/mywebapp" debug="0" reloadable="true" crossContext="true">
        <Resource name="jdbc/myresourcename" auth="Container" type="javax.sql.DataSource"
               maxActive="100" maxIdle="30" maxWait="10000"
               removeAbandoned="true" removeAbandonedTimeout="60" logAbandoned="true"
               username="myusername" password="mypassword" driverClassName="com.mysql.jdbc.Driver"
               url="jdbc:mysql://localhost:3306/mydatabase?characterEncoding=utf8"/>
</Context>

注:linux下使用tomcat6 dbcp会报classnotfound的问题。暂时解决方法:复制tomcat-dbcp.jar到/usr/share/tomcat6/lib。(参考链接

如果用连接池,还需要把数据库的驱动程序复制到/usr/share/tomcat6/lib下,仅仅在webapp里包含驱动是不行的。

此外,当url里会有中文出现时,在server.xml里的<Connector>标签需要加上URIEncoding="UTF-8"属性。如果是与Apache整合的情况,注意给8009也设置这个属性。

要开启压缩功能,可以按下面的方式设置:

<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" 
      redirectPort="8443" URIEncoding="UTF-8" 
      compressableMimeType="text/html,text/xml,text/plain,application/json" 
      compression="on" compressionMinSize="256" />

另注:若启动tomcat时发现catalina.out里提示UnknownHostException,可修改/etc/hosts文件,把/etc/sysconfig/network里定义的HOSTNAME映射为localhost。

整合apache与tomcat

Apache可以通过jk模块与n个位于相同或不同主机上的tomat实例配合,达到负载均衡的目的,缺省情况下这些tomcat监听8009端口。具体的配置方法如下:

首先在apache网站下载mod_jk(文件名如mod_jk-1.2.31-httpd-2.2.x.so,注意32位与64位有区别), 放到/etc/httpd/modules目录下,并chmod 755,下载地址32位/64位供参考。

进入/etc/httpd/conf目录,创建一个workers.properties文件,内容如下:

workers.tomcat_home=/usr/share/tomcat6
workers.java_home=/usr/lib/jvm/jre
ps=/
worker.list=ajp13
worker.ajp13.port=8009
worker.ajp13.host=localhost
worker.ajp13.type=ajp13
worker.ajp13.lbfactor=1

可参考:The Apache Tomcat Connector - Generic HowTo

编辑/etc/httpd/conf/httpd.conf文件,把下面的内容放在所有LoadModule命令的最后,其中JkMount命令后面的参数根据需要调整:

LoadModule jk_module modules/mod_jk-1.2.31-httpd-2.2.x.so
JkWorkersFile /etc/httpd/conf/workers.properties
JkLogFile "logs/mod_jk.log"
JkLogLevel info
JkMount /*.jsp ajp13
JkMount /*.do ajp13

发布web应用(webapp)时应直接放到apache页面目录下(/var/www/html/mywebapp),而不需要在tomcat/webapps下发布。注意apache不会像tomcat那样自动解压缩.war文件,所以需要手动解压缩,命令是:unzip mywebapp.war -d mywebapp

要让你的域名能直接访问到这个web应用,有两处需要注意。1)在/etc/httpd/conf/httpd.conf里增加一个VirtualHost,内容见下面代码(DocumentRoot不需要修改);2)在/usr/share/tomcat6/conf/server.xml里该web应用的<context>的path属性应设置为""(不能设置为"/",否则tomcat里request.getContextPath()将返回"/"而非空字符串,可能导致一些URL失效,参考链接)。Update 2010/10/10: 在httpd.conf里还应该指定ServerName为你的域名,否则启动httpd时会提示“无法可靠获取域名”的警告。

<VirtualHost *:80>
    DocumentRoot /var/www/html/myapp
    ServerName www.mydomain.com
    JkMount /* ajp13
</VirtualHost>

配置虚拟主机

Apache里的配置(注意使用ServerAlias使得www.mydomain.cnmydomain.cn都可访问。“The ServerAlias directive sets the alternate names for a host, for use with name-based virtual hosts. The ServerAlias may include wildcards” 参考链接):

<VirtualHost *:80>
    DocumentRoot /var/www/html/myapp
    ServerName www.mydomain.cn
    ServerAlias mydomain.cn
    JkMount /* ajp13
</VirtualHost>

Tomcat里的配置(同样注意Alias的使用。 “A common use case for this scenario is a corporate web site, where it is desireable that users be able to utilize either www.mycompany.com or company.com to access exactly the same content and applications.” 参考链接):

<Host name="www.mydomain.cn" appBase="webapps" unpackWARs="true" autoDeploy="true">
    <Alias>mydomain.cn</Alias>
    <Context ...>...</Context>
</Host>

配置smtp/pop服务

使用postfix实现smtp,用cyrus实现pop和imap,过程可参考《PostFix安装配置详解》。 

需要注意的,cyradm登录时要指定--auth plain

安装Discuz!论坛

这里安装的是Discuz! 7.2版本,方法可参考此链接。 其中可能需要yum install php,php-mysql,php安装以后会自动配置apache的参数(配置文件在conf.d下),重启apache即可。

配置好以后,进入http://hostname/install/index.php即可完成后续配置,包括config.inc.php文件都可以在向导里完成了。

服务器启动时自动启动服务

chkconfig vsftpd on
chkconfig httpd on
...

检查是否已设定成功(完整服务列表在/etc/rc.d/init.d/下)

chkconfig --list

给micro instance增加swap分区

micro版本的虚拟机只分配600m左右的内存,如果无法支持应用所需,可以考虑使用swap分区。

方法参考此链接 ,但是要注意增加swap后,EBS的I/O次数会上升很多,导致额外的费用,经验教训参考此链接

增加EBS分区

如果EC2的磁盘容量不够用,可以在aws console里创建新的EBS分区,然后挂载到EC2上使用。方法也比较简单见此链接,要注意的是在aws console里attach后,到EC2里别忘了要先mkfs一下才能mount,最好在fstab里设置为自动挂载。

定时启动/停止ec2 instance以节省费用

如果服务器只在白天有人访问,可以让它晚上关闭,这样可以节约大约1/3的费用。

需要在另外一台ec2服务器上设定定时命令,参考这个链接。当然,在停止期间是无法继续对外提供服务的。

注意,在crontab里直接运行ec2-start-instances这样的命令可能因为环境问题而失败,最好把这些命令写成单独的脚本文件,然后在crontab里调用。参考链接 

另外注意服务器的时区是否与任务执行的时间匹配。

安装SVN服务

用以下命令安装subversion并在指定目录创建一个repository。

> yum install subversion
> svnadmin create /root/svn/repo1

然后分别修改/root/svn/repo1/conf目录下的svnserve.confpasswdauthz这三个文件就可以了。具体的配置方法不复杂,可参考这个链接

启动svn服务:

svnserve -d -r /root/svn

开机自动启动svn服务:编辑start-svn.sh脚本内容如下并添加执行权限(chmod +x):

#!/bin/bash
svnserve -d -r /root/svn

然后在/etc/rc.local文件最后一行增加对start-svn.sh的调用。

注:svn实际是安装在DigitalOcean提供的云主机上,而非amazon的ec2上,环境为CentOS 6.5 x64。

安装pptpd服务(VPN)

可参考这篇帖子,如果连接出现问题,可tail /var/log/messages查看具体原因,然后google解决。

另一篇帖子

使用中遇到的问题和解决

1、instance运行正常但无法访问(ssh, http, ftp)

这种情况应该比较少遇到,forum给的答案是host环境出了些问题,解决方法是强制停止并重启,这时aws会在其他host上部署这个instance,问题也就随之解决。原话引用:

The instance is on a host that is experiencing some issues, at this point the best way to recover your instance would be to perform a force stop (adding --force to ec2-stop-instances command or by using the "force stop" action in ElasticFox) in order to have the instance stopped and be able to start it again on a new host.

2、问题同上

2012/8/14夜间遇到和上面一样的问题,这次的原因是elastic IP被墙。解决方法是不停更换elastic IP并"telnet xxx 22"直到能连接,然后将域名指向新的IP地址。

3、Tomcat吃掉过多VIRT内存的问题

尚未找到解决方案。(网上有不少人问过这个问题,目前的结论是不需要过于担心VIRT的数字,以RES的值为准。)

4、httpd进程占用过多内存

服务器运行一段时间以后,top发现有多个httpd进程并且每个进程都占用不少内存。首先free -m查看实际可用内存还有多少(重点看-/+ buffers/cache这一行的值,Linux Ate My Ram,居然有专门的域名解释这个问题),如果还有很多则不用管,这只是linux管理内存的方式而已。如果确实没有多少可用内存了,试试网上查到的这个解决方法:在httpd.conf里将workers.c模块下的MaxRequestsPerChild值设置为一个较小的值,例如50(缺省为0,表示不限制数量)。参考链接

更新2015/10/21: 今天检查/var/log/httpd/access_log时发现,有大量对/xmlrpc.php的请求,来自三个ip,平均每秒两三次,应该是服务器被利用wordpress的xmlrpc漏洞了(aliyun的云盾没有提示)。

file

初步先删除wordpress/xmlrpc.php文件解决,稍后看效果是否需要进一步处理。备选方案链接

更新:觉得还是禁止ip的方式更好,方法很简单,在httpd.conf里加下面的语句(apache建议在能修改httpd.conf的情况下不要使用.htaccess方式,会拖慢服务器速度)。

<Directory "/var/www/html/wordpress">
    Deny from 159.253.151.106
    Deny from 159.253.151.108
    Deny from 159.253.151.110
</Directory>

Android开发问题汇总

注:Android 5.0的问题总结在这个帖子里。

1、用(SDK starter package)的installler安装Android SDK时提示无法找到JDK,但实际上机器上已经安装了JDK。

一个对不少人有效的解决方法是看到此提示时先点一下“回退”按钮,再点下一步,就会发现JDK被找到了。参考链接

但在我机器上这个方法不起作用。所以我选择不使用installer,而是下载zip格式的文件,解压缩后运行SDK Manager.exe即可。参考链接

2、在Eclipse里新建一个Android项目,运行/调试时提示Could not find xxx.apk!

网上提到的大部分解决方案是clean整个项目,或选fix project properties菜单项。但对我的环境不起作用。

升级到Eclipse 3.7后,新创建的android项目在Build阶段报告一个异常sun/security/x509/X500Name,怀疑与所用的jdk(IBM JDK6)有关。果然,卸载IBM JDK并安装SUN JDK后问题解决。

3、让程序适应不同的屏幕分辨率

可参考这个链接:Android屏幕分辨率问题

4、在android模拟器里用10.0.2.2可访问宿主机。

5、在setWidth()方法里指定的宽度是以pixel为单位的,如何转换为使用dip(device independent pixels)为单位?

使用下面的代码,参考链接 

// Converts 14 dip into its equivalent px
Resources r = getResources();
float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 14, r.getDisplayMetrics());

6、弹出式对话框的用法,这个链接介绍得比较详细。 

7、导出apk文件时需要签名,这个链接比较详细。 

8、android界面设计原则,参考这个链接

9、使用merge(而不是layout)可以达到在避免重复写layout的同时减少layout的数量。

10、真机USB调试比用AVD调试快得多,设置也很简单,见这个链接。不过10.0.2.2不能用了,真机可以通过wifi访问局域网内的服务器。

11、android-ui-utils,一个不错的在线Android图标生成器,地址在此

12、调试时如果出现莫名其妙的空指针错误,例如findViewByID()返回null,先试试clean一下整个project,通常都能解决。

13、让ListView里无数据时显示一行信息。

参考链接,注意ListView和TextView的id。

14、用自己的图标替换ListView里的RadioButton

在用作row的layout里添加一个图片,在java code里覆盖adapter的getView()方法,根据ListView的getCheckedItemPosition()结果设置图片的可见性。注意不要通过setOnClickListener()方法设置view里的图片可见性,因为ListView只维护可见的那些row控件,这样做会导致很奇怪的结果(点第一条记录结果第二条记录被选中,并且滚动ListView时选中状态还会随机变化,见此链接)。

另外一个办法是通过style设置checkMark为所需要的图标,可能是更简单的解决方案(还没试)。

Update: 以上结果基于对ListView实现不了解的情况,其实使用RadioButton是可以实现的,见#22。

15、ListView的selection和choice是完全不同的,所以不要指望ListView#clearChoice()能清楚你已经是checked状态的那些item。

16、屏幕方向变化时(横屏->竖屏,或反过来),ListView里发生变化的内容丢失,状态回到开始时的样子。

当屏幕方向发生变化时,android会重建整个Activity以便你构造更适合某个方向的UI。为了避免这种情况,在AndroidManifest.xml里的那个Activity声明上增加android:configChanges="orientation" 即可。详见参考链接

17、各手机平台仿真器/模拟器的下载链接

18、AlertDialog#show()方法是不会阻塞当前线程的。

19、让不同Activity之间进行通信,例如一个TabActivity里有多个Activity,它们之间需要传递一些消息。

可使用BroadcastReceiver机制。 参考链接。要注意的是,tab还未启动时是无法接收到消息的,所以要在tabhost所在的activity里也接收消息,当tab启动时传给它。

20、Android SDK里的style和theme文档

见此链接

21、Tab的样式。

Android的tab样式问题比较多,不同版本的样式也不一样。这里有一个自定义样式的参考链接

22、在ListView里使用单选/复选按钮。

问题很多,这个链接看起来解决了问题,但在我的环境里没试验成功。

Update: 以复选按钮为例,本质的问题在于ListView里的复选按钮不知道对应的model是哪个,需要事先用CheckBox#setTag(myModelObject)关联,onclick事件里用getTag()改变其选中状态。建议看这篇文档。 

23、strings.xml里定义的字符串里增加参数。

在字符串里用“%1$s”、“%2$d”表示参数的序号和类型,然后用String.format()方法赋值。参考这个链接

24、嵌入条码/二维码扫描功能

使用zxing。方法是在手机上先安装BarcodeScanner.apk,然后在程序里调用其提供的Activity,该Activity会返回扫描结果。 参考链接

25、改变ListView里每个Row的背景颜色

直接在getView()里写view.setBackgroundColor()是不行的。正确的方法是先在drawable目录里建一个xml文件,自己起名如my_row.xml,内容如下(关键是第一个和第四个<item>):

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_window_focused="false" android:state_selected="true" android:drawable="@android:color/transparent" />
    <item android:state_selected="true" android:drawable="@android:color/transparent" />
    <item android:state_pressed="true" android:state_selected="false" android:drawable="@android:color/transparent" />
    <item android:state_selected="false" android:drawable="@color/solid_red" />
</selector>

然后在getView()里这样写就可以了:

if (item.getStopId().equals(User.stopId)) {
    view.setBackgroundResource(R.drawable.my_row);
} else {
    view.setBackgroundResource(android.R.drawable.list_selector_background);
}

参考链接1 参考链接2 参考链接3

26、在一个TableLayout里让Button宽度相同并占满表格宽度(想象一个由按钮组成的九宫格)

如果Button上的文字不长,按一般的方法就可以实现,例如这个链接。当Button上的文字很长,还是会使布局变乱,每个列的长度将不一样。解决方案是将按钮的layout_width设为0,layout_height设置为所需要的值,需要换行时将singleLine设置为false,最后将按钮的gravity设置为center。

27、对切换屏幕方向的处理

参考stackoverflow上的一个典型讨论

当MyActivity位于一个TabActivity里时,我做了一些实验表明, TabActivity是否声明android:configChanges="keyboardHidden|orientation"与MyActivity无关,只有MyActivity做了上述声明后才会在改变方向时触发onConfigurationChanged()方法。

28、strings.xml里的字符串包含html格式标签时

需要用<Data><![CDATA[...]]></Data>把html代码包起来。参考链接

29、在AlertDialog里用ListAdapter(如ArrayAdapter)时,文字不显示。

view的resourceId要用select_dialog_singlechoice而不能用simple_list_item_single_choice,否则文字颜色会与背景颜色相同而无法显示。参考链接

30、在android程序里使用第三方包的配置方法

参考这个链接成功。

31、定制tabhost的外观

这方面的需求和问题网上都很多,主要的解决方案有基于tabhost的和基于radiobutton的两大类,这里有几个可以参考的链接。链接1链接2链接3

32、取得当前屏幕方向

有好几个方法可以得到orientation值,但一些得到的值不对,例如getWindowManager().getDefaultDisplay().getOrientation()getReqestedOrientation()。我实验下来比较靠谱的是getResources().getConfiguration().orientation。

33、Activity里第一个View是EditText的时候,软键盘自动弹出。

似乎只是一些机型会这样做。要阻止软键盘弹出,可在onCreate()里加一行代码。参考链接

this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);

34、Dialog的theme问题

使用Theme.Light主题时,Dialog显示不正常。相关讨论:链接1链接2

35、注意区分CheckBox和RadioButton的OnClickListener和OnCheckedChangeListner

使用前者时,当在代码里执行myCheckBox.setChecked(true),不会触发事件,而后者会触发事件。

36、监视GPS开启/关闭事件

用GpsStatus.Listener不可靠(事件不上来),这个链接的方法是监视Settings里的事件:参考链接

37、仿真器横屏

快捷键ctrl+f11。有人提到ctrl+f12和numpad 7也可以,但我这里不起作用。

38、用JAXB生成KML对应的Java Code时会遇到一些问题,解决方法如下:

1)写一个binding文件如bindings.xjb放在与ogckml22.xsd同一目录,内容可参考这个链接(根据错误提示要删除几行)

2)在命令行里加-extension

3)在命令行里加-target 2.1(否则生成的java类的annotation带有jdk6不识别的关键字"required")

完整的命令行如下:

xjc -xmlschema -verbose -extension -b bindings.xjb -d src -target 2.1 ogckml22.xsd

39、ListPreference的entryValues只能使用string-array

如果使用了integer-array,在点击这个preference项时会产生一个空指针异常,相关讨论见链接1链接2

Update: 又发现一个ListPreference的新问题,即使用string-arraydefaultValue值也不能取太大的(超过Integer.MAX_VALUE)整数,否则defaultValue不起作用(选项没有缺省被选中)。 真是问题多多。android版本2.2。

40、Android提供了方便的Share机制,但一般都是把文字share到微博或SMS,如何能"share"文字到SD卡文件呢?

这个链接描述了同样的问题,等待有人回答。 目前的想法是,在程序里自己实现一个接受ACTION_SEND的activity,做法可参考此链接、或此文章

41、一个在线查看kml文件的网站,供参考。

GPS Visualizer 

42、关于onSaveInstanceState()的使用

通常与onCreate()配合,而非onRestoreInstanceState(),参考这个链接

2015/5/18补充:一个误区是onSaveInstanceState()方法是当activity被系统销毁时才调用,其实是当activity“变得容易被系统销毁”时就会被调用,例如当按下home键时,此时activity不再在前台显示,也就变得容易被销毁了。参考链接参考链接2

43、ListView里点击一个item背景色不变为橘黄(缺省的反馈颜色),各种OnClick事件不被触发。

一种可能是在item的布局文件(如foo_list_row.xml)里使用了下面的这些属性(自动滚动显示文字时常会用到),去掉后即可恢复正常:

android:ellipsize="marquee" android:scrollHorizontally="true"
android:marqueeRepeatLimit="marquee_forever" android:focusable="true"
android:focusableInTouchMode="true"

44、实现不停向上滚动的ListView

假设你有一个长长的list需要自动展示,方式是每隔几秒向上滚动一行,有点类似TextView的marquee功能(跑马灯?)。这个需求可以通过Handler实现,具体参考这个链接;当列表滚动到最后一行时,直接滚回第一行显得很生硬,可以用这个链接里提供的方法解决。

45、在Button的文字旁加图片

<Button>里使用android:drawableLeft="@drawable/my_icon" android:gravity="left|center_vertical"即可,类似的可以加在右侧或上下方。但如果Button有其他状态时,需要用selector指定不同状态下的图片。此外,图片的大小是不会自动根据Button调整的。

46、旧版本兼容

参考sdk文档里的这个文章

47、轻松实现圆角背景

不需要做圆角图片,看看这个链接,很方便,注意把angle改为45的倍数否则运行时报错。

48、Android内存泄漏检测

在DDMS可以查看heap使用情况,大概了解是否有内存泄漏。DDMS还可以dump出.hprof文件,后者可以用Eclipse MAT打开,进一步分析错误原因。注意,startActivity()后要根据情况决定是否调用finish()方法(如果需要back则不finish(),在适当的时机用FLAG_ACTIVITY_CLEAR_TOP一并回收内存空间),未finish()的activity是会一直占用内存的。

49、Android的第三方library

这个链接总结了不少。 

50、Android UI设计模式

参考这个链接

51、Google Map扩展的使用。

参考这个链接

52、当Spinner是invisible状态时,貌似调用mySpinner.setSelection(i) 不会触发其onItemSelected()事件。

53、实现gzip压缩服务器返回的json对象时,注意要response.setContentType("application/json"),并且在server.xml里把application/json设置到compressableMimeType里才能实现。我因为前一个原因浪费了三四个小时。

54、对AlertDialog.Builder应用定制的theme

使用ContextThemeWrapper,用法参考这个链接。但后来发现这个方法不起作用,有一种说法是这个方法只对部分android版本有效,也有说其根本无效的(都是在so上)。用AlertDialog.Builder的setVIew()方法也是有问题的,因为你会发现在这个view的旁边一圈(假设你的view是浅色背景)还是黑色的,效果很不好。其实如果只是要将对话框设置为白色背景,可以使用AlertDialog.Builder的setInverseBackgroundForced(true)方法,很简单,很直接。

55、从gallery选择图片的一些可用参数

参考这个链接,和这个相关issue (文件得不到原始图片,只能得到缩小后的图片)。

56、从gallery取图片时要注意内存是有限的,而图片可能很大。

利用inSampleSize可以帮助解决,参考这个链接的代码。

57、当程序进程被系统kill掉(常常在内存紧张时发生):

HttpClient的session可以用persist方式保留;

其余数据建议保留在SharedPreferenes里;

onActivityResult()里是能够获得另一个activity传回来的intent里的参数的;

58、连接到真机时提示Unable to open sync connection!

原因未知,解决方法是在真机的设置里去掉usb调试,然后重新勾选。

59、得到GPS状态(寻星或已获得位置)

这个问题看似简单,但Android里的LocationListener#onStatusChanged()方法工作不正常,表现为在大多数版本下都不会被调用。这个链接里提供的方法试了一下好像是可以用的。

60、在Android里画统计图(柱图饼图等等)

这个链接里给出了不少解决方案,我暂时选择的是aChartEngine,用法和JFreechart比较类似,参数超多,目前的活跃度也不错。

61、进程经常被kill

可以考虑启动一个service(即使什么都不做),这样进程的“重要性”就变得很高,因此就不容易被kill了。

62、“Receiver not registered”错误

执行unregisterReceiver(myReceiver)方法时,如果之前没有注册过myReceiver,会抛出这个异常。参考链接

63、应用在后台运行,需要弹出对话框(例如触发自service产生的事件)时报错:“BadTokenException: Unable to add window”

利用isFinishing()测试activity是否在后台,参考链接

64、Emulator太慢了

试试Android x86,据说比官方的快很多(我还没试过,正在下载)。 参考链接

65、禁止自动切换到横屏模式

<activity android:name=".SomeActivity" android:label="@string/app_name"
          android:screenOrientation="portrait">
</acivity>

参考链接

66、几个众包模式的beta测试平台

iOS最著名的是testflight,也有其他一些类似的测试平台支持android,详见这个链接

67、实现锁机后黑屏但不出现锁机画面

getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);

还有一个功能类似的flag是FLAG_DISMISS_KEYGUARD,区别在于前者只对当前activity有效,后者全局有效。另外前者对安全锁屏也有效,后者则只对普通锁屏有效。附一个参考链接

但是前者有一个问题,就是在两个都设置了FLAG_SHOW_WHEN_LOCKED的activity间切换时,可能锁屏界面会闪一下。见此问题报告

68、一些可用于android应用性能测试和内存泄漏检测的工具

这个链接

69、进度条的一个bug

重设setMax()以后显示的进度百分比不正确,至少在Android 2.1 (API Level 7)里有这个问题。 见这个链接这个链接

70、当ListView的item里包含控件(如按钮、复选框)时 ,这个item无法被选中。

Android不允许选中ListView里带有focusable元素的item,解决方法是将该控件的focusable属性设置为false。参考链接

71、帮助做简单web测试的工具(构造并发送各种http请求)

这个链接里总结了不少。

72、Tab放在底部(仿iphone风格)

中文的教程看了好几个都不靠谱,so上的一个链接搞定,或者这个带有源码的教程

73、让ListView没有数据时也显示HeaderView/FooterView

技巧是让empty view包含headerview/footerview,见这个链接

74、在Google map上添加popup的方法

最简单的方法见这个链接

75、一个网站,可以搜索android相关项目的代码和资源。

链接在此

76、Android里的Search Activity不支持返回结果到调用其的Activity(因为onSearchRequested()方法没有调用startActivityForResult()启动search activity)

SO上有若干个提出此问题的帖子,例如链接1链接2等等,没有特别方便的办法解决,一个我没试过但看回复应该可行的方法见这个链接

77、查看apk文件内容的工具

推荐apktool,一个命令行工具,用法如下:

> apktool.bat d my.apk

可以还原所有的资源文件,但.java文件一般无法还原。

78、Android中使用的各类图标的标准尺寸

请参考sdk自带的guideline文档。 

79、Android的Searchable接口,无法让调用者获得查询结果。

参考这篇文档(抱歉链接已失效)可以实现,思路是自己用startActivityResult()启动搜索界面,然后在onActivityResult()里取出结果。我测试可行,但该文档有两处错误需要注意:

1) 是handleIntent()而非handleActivity()

2) 在startActivityResult()前最好intent.setAction(Intent.ACTION_SEARCH)一下。

另外,注意按该文章中提到的官方文档实现相应的newIntent()和onCreate()方法,以及在AndroidManifest.xml里设置调用者的android:launchMode="singleTop"。

80、很诡异的问题,有时EditText无法输入文字(软键盘正常弹出但字符进不去文本框),必须切换到另一个输入法才可以输入。

经测试,有些机型存在这个问题,具体原因还不详。以下链接可能与此有关:链接1

81、定时重复执行一段程序

要执行类似闹钟这样的功能,用AlarmManager配合BroadCastReceiver即可,网上有很多例子不再赘述。值得一提的是,在这个BroadCastReceiver里不要执行异步操作(例如异步访问一个远程服务、获取当前位置等等),因为onReceive()方法一旦执行结束,用于容纳BroadCastReceiver的进程随时可能被系统kill掉,导致异步操作结束后出现异常。解决的办法是在onReceive()方法里启动一个Service(我用的是startService,用bindService的方式可能也行),在Service里执行任何操作就可以了。参考BroadCastReceiver Life Cycle

2015/12/10注:AlarmManager也可以直接配合Service使用

2015/12/10注2:要查看手机上所有alarm,在命令行里执行adb shell dumpsys alarm > alarms.txt命令即可。

82、Monkey测试

> adb shell monkey -v -p com.my.app 100

MonkeyRunner可以进行更高级的测试。

83、用getIntent().getExtras().clear()无法清除掉extras里的数据

原因是getExtras()返回的是一个copy实例,用getIntent().removeExtra()可一个个清除。参考链接

84、用AlertDialog实现输入对话框时,若直接builder.setView(myEditText)文本框显得太长。

用dialog.setView()可以指定padding(注意是dialog.setView()而不是builder.setView()),具体见这个链接

85、在TextView里显示图片

通过Html.fromHtml()方法可以在TextView里显示HTML格式的文本,但只支持部分tag:

myTextVIew.setText(Html.fromHtml(myHtmlStr))

要在TextView里显示远程图片,必须向fromHtml()函数里提供一个ImageGetter对象,在它的getDrawable()方法里获取远程图像并转换为Drawable类型。示例代码请参考此链接

如果TextView设置了android:lineSpacingMultiplier属性,将导致图片显示的位置不正确(顶部多出一些空白),用android:lineSpacingExtra属性则没有这个问题。

如果图片加载比较慢将导致整个TextView空白很长时间,为解决这个问题需要异步加载图片,这样文字可以先出来,待图片下载完毕后再补充道文字中间。实现方法是扩展Drawable,具体方法参考这两个链接:链接1链接2 ,其中链接1的方案存在图片尺寸不正确的问题,原因是TextView#invalidate()没能起作用(原因不详),用链接2提供的方法可以解决,这个链接提到用textView.setText(textView.getText())也可以工作但我没试。

86、在TextView里显示列表(

  • 标签)

    Android的TextView只支持一小部分的html标签(见这个链接),缺省是不支持

      • 这样的列表标签的。通过TagHandler可以实现一个简单的列表效果(见此链接),但这个方法有个严重问题:当列表文字超过一行时,第二行的文字是顶头的没有缩进效果(见这个提问),而该问题暂时还没有好的解决方法。

        87、百度地图android sdk问题

        百度地图最大的问题:文档太烂!百度地图sdk版本比较多,网上充斥着各种版本的例子代码,官网上的例子代码也不清晰,在线文档含混不清。

        遇到过一个奇怪的问题,与这个帖子描述的情况相同,即第二次打开地图只能显示上次缓存过区域的地图,最后删除了所有onPause()onResume()onDestroy()里的百度地图相关方法才解决。

        88、启用Andoird设备的otg功能

        /system/etc/permissions目录里检查是否有名为android.hardware.usb.host.xml的文件,如果没有,新建一个内容如下:

        <permissions>
            <feature name="android.hardware.usb.host" />
        </permissions>

        89、命令行对一个未签名apk进行签名

        jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my-release-key.keystore my_application.apk alias_name

        参考连接

        90、Pull-to-refresh控件不显示内容问题

        将ListView替换为PullToRefreshListView,setAdapter()后发现列表中没有数据显示,调试发现adapter里是有数据的,而listView的getView()方法没有被执行到。最后发现原因是在布局文件的<com.handmark.pulltorefresh.library.PullToRefreshListView>元素中指定了android:visibility="gone"属性(在.java文件里在setAdapter()之前先调用了listView.setVisibility(View.VISIBLE)),在布局文件里去掉此属性,改为在activity的onCreate()里执行listView.setVisibility(View.GONE)后恢复正常,没有深入调查这两种方式对PullToRefreshListView有什么区别,对标准ListView是没有区别的。

        91、切换fragment时fragment显示不全

        fragmentTransaction.replace(R.id.layoutRoot, newTransaction)时,发现当layoutRoot是LinearLayout时,(有时)newTransaction的高度无法充满layoutRoot(虽然已经指定了高度为match_parent,指定高度为固定值也无效),后来将layoutRoot改为FrameLayout问题解决。

        提供了一些线索的参考链接

        92、利用Activity的android:process属性

        在开发一个视频播放器过程中,遇到一个问题,当视频还在缓冲时按返回键退出当前activity,应用失去反应长达数秒。后来在AndroidManifest.xml里对这个activity增加了android:process=":player"后问题解决,因为这个属性使得此activity在一个独立的进程运行,当activity被关闭时,此进程也被杀掉。这样,即使播放器有未完成的工作,也不会影响到主程序的UI线程了。

        需要注意,用PreferenceManager.getDefaultSharedPreferences()得到的SharedPreferences实例,是不支持跨进程访问的,因此在独立的进程里将无法向这样获得的SharedPreferences实例存取数据。解决的办法是改为使用context.getSharedPreferences(prefName, Context.MODE_MULTI_PROCESS)获取SharedPrefences实例。

        不过还是要特别注意,在多进程环境下,如果你用一个静态变量缓存了SharedPreference里的内容,由于静态变量在多个进程间不是共享的(Application对象也不是跨进程共享的),所以取出的结果可能不是你想要的。解决办法 1)去掉这个作为缓存的静态变量,每次都直接从SharedPreferences里取 2) 有人建议使用MemoryFile作为跨进程的机制,但我没有实际测试过,而且用起来应该会比Application对象繁琐。

        参考链接 参考链接2 参考链接3

        93、Service的START_STICKY与START_NOT_STICKY的区别

        参考这个链接:当系统由于资源不足可能会杀掉一个service,之后如果系统又有了足够资源,若被杀掉的service是START_STICKY的,则系统会调用其onStartCommand()方法恢复这个service;若被杀掉的service是START_NOT_STICKY的,则系统不会尝试恢复这个service。

        94、如何用纯java的方式获取apk文件版本信息?

        apk文件就是zip文件,解析并读取AndroidManifest.xml里的信息即可。开源项目android-apk-parser已经实现了这个功能,可以直接在java项目里使用。

        95、Android手机使用https代替http会带来明显的性能问题吗?

        对CPU消耗来讲,不会。见参考链接

        对网络消耗来讲,有一些影响。见参考链接

        关于在手机端“接受所有证书”做法的安全性,见参考链接

        注意:ssl处于tcp和http之间,使用https后,url也是加密过的,因此不需要担心api地址暴露(浏览器历史除外)。参考链接

        96、代码混淆

        project.properties里的#proguard.config=xxx这句前面的#去掉即可。会按照android sdk目录里的proguard-android.txt设置文件进行混淆,如果项目有特殊要求,在项目目录下的proguard-project.txt里进行设置。有两点注意:

        1) 只有在正式export apk时才会进行混淆,直接运行产生的bin/xxx.apk不会进行混淆;

        2) library工程里的混淆设置不起作用,以宿主工程的设置为准。

        97、Fragment里getActivity()有时返回为null

        一般当activity所在进程在后台被系统回收,然后用户重新回到这个进程时,activity重建后发生。解决方法是在activity的onSaveInstanceState()方法里保存fragment的状态:

        public void onSaveInstanceState(Bundle outState) {
            activity.getSupportFragmentManager().putFragment(outState, "current_fragment", currentFragment);
        }

        然后在activity的onCreate()方法里恢复:

        if (savedInstanceState != null) {
            currentFragment = (Fragment) activityBase.getSupportFragmentManager().getFragment(savedInstanceState, "current_fragment");
        }

        这样currentFragment所代表的fragment实例的getActivity()就不为null了。

        98、分级Preference界面布局的实现方式

        引用这篇文章的总结:Android 3.0之前:采用PreferenceScreen嵌套的方法;Android 3.0及之后:采用Preference Headers的方法。

        采用Preference Headers方法时,要求继承PreferenceActivity,但这样就无法同时继承ActionbarActivity了(问题链接)。这个第三方library可以基本解决这个问题。

        99、自定义进度条progressbar的风格

        这篇文章总结得很好,特记录在此:How-to: Customize Android progress bars

        100、Color State List不能用于android:background属性

        Android似乎不支持在android:background里指定Color State List资源(例如android:background="@color/bg_button_selector"会导致崩溃,但android:background="#ff0000"是可以的),得用Drawable State List才行,例如android:background="@drawable/bg_button_selector"参考链接

        搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2011/06/28/2092514.html

  • IBM x3950 M2服务器安装OpenSuse问题和解决记录

    file

    服务器配置:Xeon 2.93Ghz x4, 64G, OpenSuse Enterprise 11.0 64bit

    1、服务器安装基本过程

    开箱,把所有选件(内存、内存、光纤适配器、RAID卡ServeRAID 10k)插进相应的位置,搬上机架并扣好(因为机器很重,该步骤需要至少两个人)。最后把几个硬盘都插进去完事。

    2、配置RAID

    服务器自带的ServerGuide光盘可以启动系统并配置RAID,但好像只适用于安装windows操作系统。我选择的方式是在服务器开机后,看到<ctrl+h>提示后按Ctrl+H进入RAID卡的BIOS进行配置。配置过程比较简单,选择创建一个Virtual Drive即可,我选择的是把四块硬盘做成RAID5。

    3、安装Linux

    首先从opensuse网站下载映像,因为内存比较大,所以我选择的是64位企业版,然后刻录成DVD。用这张光盘启动服务器(在服务器启动时按F12可以选择启动顺序,缺省好像也会先以光盘启动),OpenSuse的安装是图形化的很容易,也不需要做太多改动。我只修改了缺省的分区,因为自动生成的分区给/只分配了20G空间,我改为了200G,/home分配了250G左右。修改分区大小时,要先删除一个分区,这个分区原来的空间才能分配给其他分区,这一点稍显麻烦。

    安装过程比较长,大约花了40分钟,中途断开了显示器去配置其他服务器,再连回去以后发现机器停在了Suse启动的画面很久。无奈取出光盘强行关机重启,这次顺利进入系统,系统又进行了一些配置工作,安装就算完成了。

    再次重启以后发现一个问题,可能是显示器没有被正确识别,桌面的分辨率超出了支持范围,导致无法看到桌面。解决的方法是按Ctrl+Alt+1进入命令行界面,修改/etc/X11/xorg.conf文件里的[Monitor]一节下的PreferredSize值,原来是1280x1024,改为1024x768以后,重启顺利进入gnome桌面。

    4、安装所需软件

    OpenSuse所带的YAsT能通过网络安装常用软件,不过这台服务器所处环境无法访问外网,所以要使用本地更新。具体方法参考这个链接

    使用Dojo Toolkit中的问题和解决

    file

    本文记录使用dojo toolkit进行前端开发过程中遇到的问题和解决方法。

    1、动态填充dijit.form.MultiSelect

    MultiSelect不支持dojo.data(即datastore),所以如果想把一个datastore动态填充进去,只能写一些javascript代码来动态创建那些

    2、强制一个datastore从url里取得数据

    myDataStore._forceLoad();

    3、当datastore含有嵌套结构的时候,datastore.fetch()报错“dojo _48 is undefined”

    原因是在得到的json串里包含了"identifier"的声明,但嵌套结构里有一些对象没有包含所声明的属性。解决的方式1、让所有对象都包含该属性,2、去掉"identifier"声明(推荐1,因为我猜去掉这个声明后,在一些控件里将无法映射id值到html代码)参考链接1 参考链接2

    4、在dojo中响应事件

    请参考:JavaScript events and Dojo

    5、刷新BorderContainer

    有时需要动态隐藏BorderContainer里的一些区域(如left),若近将该区域的style.display设置为"none",则该区域所在的部分将变为空白,其他区域(如center)不会自动占领它原来的屏幕空间。这时需要调用dojo.byId('myBorderContainer').layout()方法手动让dojo重新计算一次布局,其他布局类似。一个相关链接(仅针对旧版本dojo)

    6、用dojo画统计图(柱图,饼图等)

    见这个教程:A Beginner's Guide to Dojo Charting (Part1, Part2)

    7、程序改变dijit.dialog的标题

    dijit.dialog并没有提供一个"setTitle"方法来控制标题内容,但可以通过dijit.byId('myDialog').titleNode.innerHTML='My New Title'来实现。参考链接

    8、BorderContainer里各个pane的显示和隐藏

    利用dojox.layout.ExpandoPane(尚不确定是否为experimental),使用的例子见这个链接

    9、将ItemFileWriteStore序列化为JSON字符串

    var foo=dojo.data.ItemFileWriteStore._getNewFileContentString();

    10、dojo的XHR(XmlHttpRequest) 里,handleAs可以有以下取值:

    • text (default)
    • json
    • json-comment-optional
    • json-comment-filtered
    • javascript
    • xml

    参考链接

    11、将DataStore里的数据以XHR方式发给服务器,并处理返回值

    在js里这样写:

    var data = scenarioEntryStore._getNewFileContentString();
     dojo.rawXhrPost({
      url: "http://127.0.0.1:8080/myapp/myservlet",
      handleAs: "text",
      postData: data,
      headers: {"Content-Type": "application/json"},
      handle: function (data, args) { if (typeof(data) == "error") {
           alert("Error run analysis");
          } else {
           alert("Successed run analysis"); //do with result data
     }
      }
     });

    在myservlet里用下面的方法得到js传过来的json字符串,然后可以用任何java的json包来做解析和处理了:

    private String readJSONString(HttpServletRequest request) {
        StringBuffer json = new StringBuffer();
        String line = null; try {
            BufferedReader reader = request.getReader(); while ((line = reader.readLine()) != null) {
                json.append(line);
            }
        } catch (Exception e) {
            System.out.println(e.toString());
        } return json.toString();
    }

    12、使用xhr的时候,如果不是在本机访问,可能会遇到“Access to Restricted URI Denied”的提示。

    一个可能的原因是xhr里的url的domain与访问者浏览器(或其他什么,这里没搞很清楚)的domain不一致,如果是这个原因,只要在url里使用相对路径就可以了。比如原来url是http://127.0.0.1:8080/myapp/myservlet的,改为myservlet即可。如果不是这个原因,这个链接可以参考一下。

    13、dojo里的单击事件是“onClick”,写成“onclick”不认。(但onchange是全小写,够奇怪)

    14、把json格式的数据画到dojo chart

    下面是一个把json数据转换到数组的例子,来自这个链接,实际上是利用prototype的能力。

    //JSON:
    str = “{values: [1,2,3,4,5]}”; //using prototype
    obj = str.evalJSON(); //the array is then referenced by:
    chart1.addSeries(”Series 1″, obj.values);

    下面这种方式则直接使用了json格式(来自这里),但关于valueFn这个参数的使用方法我还没找到一个比较完整的介绍:

    <div dojoType="dojox.charting.widget.Chart2D" id="chart4" theme="dojox.charting.themes.PlotKit.green" style="width: 300px; height: 300px;">
      <div class="plot" name="default" type="Pie" radius="100" fontColor="black" labelOffset="-20"></div>
      <div class="series" name="Series C" store="tableStore" valueFn="Number(x)"></div>
      <div class="action" type="Tooltip"></div>
      <div class="action" type="MoveSlice" shift="2"></div>
    </div> 

    15、可伸缩的pane

    使用ExpandoPane(//测试链接TODO)即可实现。要注意的是,ExpandoPane在dojo 1.3之前的版本里有bug,现象是包含抽屉pane时在IE下最下面的抽屉会被挤掉,在Firefox里则正常。dojo 1.3解决了这个问题。

    (如无特别说明,以上均针对dojo 1.3版本。 )

    (以下为dojo 1.6版本,dojo总是让人抓狂,一些好不容易获得的经验不得不快记下来)

    16、 dojo chart的legend(图例)必须在chart.refresh()以后创建或refresh(),否则图例里不会正确显示颜色和形状,而是都为"X"。(参考链接

    17、 在tabcontainer里画的chart,refresh()的时候所在tab页是隐藏状态,切换过去后chart的尺寸不能充满tabcontainer区域。

    还不知道正确的方法是什么,一个权宜之计是在tab切换到chart所在tab时,调用一下chart的resize()方法,例如下面这样:

    dojo.subscribe("myTabContainer-selectChild", function(child){ if(child.id=="myTab")
            myChart.resize();
    });

    GIS相关问题记录

    本文记录使用Java处理GIS数据过程中遇到的问题和解决方法。

    1、uDig里创建新图层

    uDig is an open source (EPL and BSD) desktop application framework, built with Eclipse Rich Client (RCP) technology.

    我用uDig版本1.1.1,找了很久才找到怎样创建(而不是“添加”)一个新图层,感谢HXY,应该是在主菜单里选择Layer->Create。 主菜单File->New->New Layer打开的实际是“添加”图层对话框,而在Map的右键菜单里也只有Add...而没有Create。在Layer的右键菜单里倒是有Operations->Create Feature Type,但当时我没把它和Create Layer联系起来,而且它上面还有一个Add Feature Type菜单项,实现的是一模一样的功能,不知道为什么要设置两个不同名字。感觉uDig的UI在“User Friendly”方面还有很大的改进空间。

    2、uDig对已有的Layer添加(修改)Attributes

    可通过Layer的Operations->Reshape操作实现,具体步骤见uDig帮助的Users Guide: Adding a column to a shapefile参考链接

    3、在用OpenLayers展示WMS(Web Map Service)地图数据时指定所需要的样式(Style)

    一般我们用GeoServer作为服务器的时候,在后台已经为每个地图都指定了对应的样式。客户端如果需要改变这个缺省的风格,可以通过styles参数指定。对OpenLayers来说,就是在创建OpenLayers.Layer.WMS对象的时候指定这个参数,它的值必须是服务器上已经存在的风格的名称。下面是一个例子:

    var myLayer =new OpenLayers.Layer.WMS(  
                    "My Layer", "http://127.0.0.1:8080/geoserver/wms",
                    {
                        srs: 'EPSG:4326',
                        width: '800',
                        styles: 'another_style_registered_on_the_server',
                        height: '494',
                        layers: 'topp:CHN_water_areas_dcw',
                        format: 'image/png',
                        tiled: 'true',
                        tilesOrigin : "85.65425605773926,18.746308612823487",
                        transparent: true                },
                    {
                        'opacity': 0.50, 'isBaseLayer': true, 'wrapDateLine': true, 'buffer':0
                    }
    
                );

    暂时还没有找到不需要事先在服务器上注册样式实现这个目的(即完全在客户端控制图层显示)的方法,而OpenLayers的样式控制功能里的StyleMap只能实现对Vector类型图层的显示控制。另一种可能的方式是动态生成SLD(Dynamic SLD),似乎(因为在OpenLayers的文档里没有提到)WMS有一个"SLD"参数可以指定使用任意地址的SLD,这个地址有一些讨论,但我加上这个参数后没有试出任何效果。

    4、在GeoServer的样例页面里查看发布的地图时提示错误信息_The requested Style can not be used with this layer_. The _style_ specifies an attribute of XXX and the _layer_ is: topp:xxxx

    原因是图层topp:xxxx里不包含样式文件(SLD)里引用的"XXX"属性,例如样式里指定地图上根据人口对不同国家着色,但图层里没有人口这个属性,就会出现这个异常。多数情况是在GeoServer里发布数据的时候指定错了图层。

    JSF开发问题和解决

    file

    本文记录使用Java Server Faces开发web应用过程中遇到的问题和解决方法。

    1、在<f:subview>里的<h:commandLink>的action不执行

    很多时候<f:subview>是在包含页面的情况下被用到(例如包含一个导航页面),而被包含的页面里如果有非JSF标签(如<a>)的时候,必须额外使用<f:verbatim>包含它才不会报错。问题是<f:verbatim>包含的内容是不算在JSF的Component Tree里的,因此这里的<h:commandLink>的action就不会被执行了。解决的办法是不要在<f:verbatim>里用<h:commandLink>,即尽量全部使用JSF的标签比较不容易出问题。参考链接

    2、还是在<f:subview>里,action属性的方法虽然执行了,但不能转到faces-config里定义的目标页面

    检查faces-config.xml<from-view-id>,如果页面被包含的话,则<from-view-id>可能应为/*,而不是如/navigatorbar.jspx这样。

    3、结合EMF使用时,页面抛出找不到属性异常Error getting property 'xxx' from bean of type XXXX

    EMF生成的XXXImpl里的构造方法是protected修饰的,改为public即可。(注意修改@generated修饰,否则下次重新生成时会被覆盖回来)

    另(不仅限于EMF的情况):如果一个Bean里有两个同名但参数不同的方法,例如Customer有getRecords()getRecords(int year)这两个方法,则在JSF页面里用#{customer.value}会抛出同样的异常,我暂时还不确定是EL的问题还是JSF实现(我用的trinidad)的问题,部分异常stack如下所示:

    严重: Servlet.service() for servlet faces threw exception
    javax.faces.el.PropertyNotFoundException: Error getting property 'xxx' from bean of type XXXX
        at com.sun.faces.el.PropertyResolverImpl.getValue(PropertyResolverImpl.java:107)
        at com.sun.faces.el.impl.ArraySuffix.evaluate(ArraySuffix.java:167)
        at com.sun.faces.el.impl.ComplexValue.evaluate(ComplexValue.java:151)
        at com.sun.faces.el.impl.ExpressionEvaluatorImpl.evaluate(ExpressionEvaluatorImpl.java:243)
        at com.sun.faces.el.ValueBindingImpl.getValue(ValueBindingImpl.java:173)
        at com.sun.faces.el.ValueBindingImpl.getValue(ValueBindingImpl.java:154)
        at org.apache.myfaces.trinidad.bean.FacesBeanImpl.getProperty(FacesBeanImpl.java:66)
        at org.apache.myfaces.trinidad.component.UIXComponentBase.getProperty(UIXComponentBase.java:1100)
        at org.apache.myfaces.trinidad.component.UIXIterator.getValue(UIXIterator.java:415)
        at org.apache.myfaces.trinidad.component.UIXCollection._flushCachedModel(UIXCollection.java:1127)
        at org.apache.myfaces.trinidad.component.UIXCollection.encodeBegin(UIXCollection.java:511)
        at org.apache.myfaces.trinidadinternal.uinode.UIComponentUINode._renderComponent(UIComponentUINode.java:317)
        at org.apache.myfaces.trinidadinternal.uinode.UIComponentUINode.render(UIComponentUINode.java:279)

    解决的办法是把带参数的那个方法改名。

    4、从session里删除一个bean

    ValueBinding binding =FacesContext.getCurrentInstance().getApplication().createValueBinding("#{MyBean}");
    binding.setValue(context, null);

    参考链接

    搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2009/02/19/1394275.html