这几天突然发现红米Note手机只要调用系统相机进行拍照时,我的应用必定会崩溃。这个问题折腾了好久才解决,现在记录下问题跟踪解决的过程和方法。(红米Note手机的系统太坑爹了%>_<%)
####解决办法
请直接从第7条开始看,1~5条为我的问题处理过程,第6条为问题原因分析。
####问题原因分析
问题刚出现的时候,当然是想调试,在调用系统相机拍照的前后代码出打好断点准备调试,结果发现这段代码没有任何异常(其实想想也是正常的,因为其他手机都是好的,唯独红米Note有问题,要是这段代码有问题,其他手机应该也有问题,这一步真是多余)
既然暂时找不到系统崩溃时代码的出错行,那只有去查看系统崩溃日志了(这里提一下,应用开发时一定要通过UncaughtExceptionHandler捕获系统未处理的系统,并在uncaughtException(Thread thread, Throwable ex)方法中记录异常日志,不然应用运行中崩溃了完全无法快速准确的定位错误信息)。
当我打开日志文件一看,傻眼了,这日志记录完全无法定位错误信息(日志记录不完全,也没有准确记录,没有记录出错的代码行以及相关的方法运行栈信息)。没办法,我只能想办法重新改写记录日志的方式,下面是我记录异常信息日志的方法,可以准确的定位到出错的代码行和方法,以及其运行前后的方法栈信息:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47public void uncaughtException(Thread thread, Throwable ex) {
String logdir = logPath ;
File file = new File(logdir);
boolean mkSuccess;
if (!file.isDirectory()) {
mkSuccess = file.mkdirs();
if (!mkSuccess) {
mkSuccess = file.mkdirs();
}
}
StringBuffer sb = new StringBuffer();
DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
Writer writer = new StringWriter();
PrintWriter printWriter = new PrintWriter(writer);
ex.printStackTrace(printWriter);
Throwable cause = ex.getCause();
while (cause != null) {
cause.printStackTrace(printWriter);
cause = cause.getCause();
}
printWriter.close();
String result = writer.toString();
sb.append(result);
try {
String time = formatter.format(new Date());
String logFile = logdir + File.separator + time + ".log";
FileOutputStream fos = new FileOutputStream(logFile);
fos.write(sb.toString().getBytes());
fos.close();
} catch (Exception e) {
Log.e(TAG, "an error occured while writing file...", e);
}
if (!handleException(ex) && mDefaultHandler != null) {
mDefaultHandler.uncaughtException(thread, ex);
} else {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Log.e(TAG, "Error : ", e);
}
}
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(10);
}重新运行了几次,发现每次出错的地方竟然还不一样(但每次出错都是空指针异常引起的),有时候是调用系统相机的那个Activity的onCreate方法调用的时候出现空指针,有时候是调用系统相机的前面一个Activity的onCreate方法中出现空指针(假设界面A跳到界面B,在B中调用系统相机,出错的时候A和B中的onCreate方法里都有空指针异常信息),知道出现错误的代码行数这救你好办了,赶紧打好断点准备调试,结果竟然发现不管怎么操作,代码就是不进断点。
代码不进断点,就没法找到问题根源,只能分析代码寻求解决办法了,我在出现空指针错误的地方加上非空判断,重新运行发现这几个地方虽然不出错了,但是又在其他的地方出现空指针异常了,经过分析发现所有出现空指针的地方都是我的自定义Application里面的某几个static引用型变量为空了。分析到这里,在加上前面的代码不进断点以及空指针出现onCreate方法中,猜测是不是应用直接被系统回收了。
调用系统相机的时候,应用被系统回收(Application、处于后台和前台的Activity都被销毁,静态变量全部消失),此时拍照完成之后重新恢复创建Activity(重新创建的时候,手机与编辑器的调试状态肯定就断掉了,因此通过onCreate重新创建Activity时无法进到断点),重新调用onCreate时,因为该方法中有用到自定义Application里的static变量(此时已被销毁),所以会出现空指针异常。至于为什么界面A和B对应的onCreate方法中都会出现空指针,那是因为B调用系统相机完成之后会重新创建,创建失败之后按照Activity的栈顺序会接着创建界面A,而A中的onCreate方法里也用到了自定义Application里的static变量,所以也出现了空指针。
既然知道了问题原因,那就来寻求解决办法,我在onSaveInstanceState(Bundle outState)方法中保存当前Activity里操作过的所有变量信息,然后在重新创建该Activity时通过onRestoreInstanceState(Bundle savedInstanceState)方法来恢复这些数据信息(这两个方法的执行机制请参考我的另外一篇文章:Android基础篇之:Activity生命周期),同时在onCreate中用到自定义Application里的static变量的地方进行非空判断(为空的时候进行重新初始化),进过这一系列的代码处理之后,再次运行正常,完美解决问题。
不过在步骤7中解决问题时发现,在处理应用被回收重新创建时要注意如下几点:
- Activity里用户操作的所有数据全部需要保存
- Activity中第一次初始化时获取到的变量也需要保存
- 用户登陆后所有拥有的相关权限也需要进行处理保存
- 数据恢复时要考虑当前Activity引用的其他Activity或Application里面的变量的再次初始化
- 所有自定义对象都最好能被序列化,否则无法进行状态保存
- 尽量少用static类型的变量
- 有些服务能不在Application中初始化,最好不要在Application中初始化。
- 在Application中定义的变量最后都在Application中进行初始化创建,不要部分在Application进行初始化,部分在其他的Activity里进行初始化
- 应用开发时要考虑到应用被回收的情况,以便真正被回收后能方便的解决(虽然应用被回收的几率很低,但是不排除某些坑爹的手机很容易被回收,就像红米Note一样)。
write by laohu
2015年10月22日
原创文章,转载请出处注明。
下面是我的个人公众号,欢迎关注交流