红米Note调用系统相机拍照后应用崩溃问题分析解决

这几天突然发现红米Note手机只要调用系统相机进行拍照时,我的应用必定会崩溃。这个问题折腾了好久才解决,现在记录下问题跟踪解决的过程和方法。(红米Note手机的系统太坑爹了%>_<%)

####解决办法
请直接从第7条开始看,1~5条为我的问题处理过程,第6条为问题原因分析。

####问题原因分析

  1. 问题刚出现的时候,当然是想调试,在调用系统相机拍照的前后代码出打好断点准备调试,结果发现这段代码没有任何异常(其实想想也是正常的,因为其他手机都是好的,唯独红米Note有问题,要是这段代码有问题,其他手机应该也有问题,这一步真是多余)

  2. 既然暂时找不到系统崩溃时代码的出错行,那只有去查看系统崩溃日志了(这里提一下,应用开发时一定要通过UncaughtExceptionHandler捕获系统未处理的系统,并在uncaughtException(Thread thread, Throwable ex)方法中记录异常日志,不然应用运行中崩溃了完全无法快速准确的定位错误信息)。

  3. 当我打开日志文件一看,傻眼了,这日志记录完全无法定位错误信息(日志记录不完全,也没有准确记录,没有记录出错的代码行以及相关的方法运行栈信息)。没办法,我只能想办法重新改写记录日志的方式,下面是我记录异常信息日志的方法,可以准确的定位到出错的代码行和方法,以及其运行前后的方法栈信息:

    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
    47
    public 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);
    }
  4. 重新运行了几次,发现每次出错的地方竟然还不一样(但每次出错都是空指针异常引起的),有时候是调用系统相机的那个Activity的onCreate方法调用的时候出现空指针,有时候是调用系统相机的前面一个Activity的onCreate方法中出现空指针(假设界面A跳到界面B,在B中调用系统相机,出错的时候A和B中的onCreate方法里都有空指针异常信息),知道出现错误的代码行数这救你好办了,赶紧打好断点准备调试,结果竟然发现不管怎么操作,代码就是不进断点。

  5. 代码不进断点,就没法找到问题根源,只能分析代码寻求解决办法了,我在出现空指针错误的地方加上非空判断,重新运行发现这几个地方虽然不出错了,但是又在其他的地方出现空指针异常了,经过分析发现所有出现空指针的地方都是我的自定义Application里面的某几个static引用型变量为空了。分析到这里,在加上前面的代码不进断点以及空指针出现onCreate方法中,猜测是不是应用直接被系统回收了。

  6. 调用系统相机的时候,应用被系统回收(Application、处于后台和前台的Activity都被销毁,静态变量全部消失),此时拍照完成之后重新恢复创建Activity(重新创建的时候,手机与编辑器的调试状态肯定就断掉了,因此通过onCreate重新创建Activity时无法进到断点),重新调用onCreate时,因为该方法中有用到自定义Application里的static变量(此时已被销毁),所以会出现空指针异常。至于为什么界面A和B对应的onCreate方法中都会出现空指针,那是因为B调用系统相机完成之后会重新创建,创建失败之后按照Activity的栈顺序会接着创建界面A,而A中的onCreate方法里也用到了自定义Application里的static变量,所以也出现了空指针。

  7. 既然知道了问题原因,那就来寻求解决办法,我在onSaveInstanceState(Bundle outState)方法中保存当前Activity里操作过的所有变量信息,然后在重新创建该Activity时通过onRestoreInstanceState(Bundle savedInstanceState)方法来恢复这些数据信息(这两个方法的执行机制请参考我的另外一篇文章:Android基础篇之:Activity生命周期),同时在onCreate中用到自定义Application里的static变量的地方进行非空判断(为空的时候进行重新初始化),进过这一系列的代码处理之后,再次运行正常,完美解决问题。

  8. 不过在步骤7中解决问题时发现,在处理应用被回收重新创建时要注意如下几点:

  • Activity里用户操作的所有数据全部需要保存
  • Activity中第一次初始化时获取到的变量也需要保存
  • 用户登陆后所有拥有的相关权限也需要进行处理保存
  • 数据恢复时要考虑当前Activity引用的其他Activity或Application里面的变量的再次初始化
  • 所有自定义对象都最好能被序列化,否则无法进行状态保存
  • 尽量少用static类型的变量
  • 有些服务能不在Application中初始化,最好不要在Application中初始化。
  • 在Application中定义的变量最后都在Application中进行初始化创建,不要部分在Application进行初始化,部分在其他的Activity里进行初始化
  • 应用开发时要考虑到应用被回收的情况,以便真正被回收后能方便的解决(虽然应用被回收的几率很低,但是不排除某些坑爹的手机很容易被回收,就像红米Note一样)。

write by laohu

2015年10月22日


原创文章,本文采用知识共享署名 2.5(中国大陆许可协议)进行许可,欢迎转载,但转载请注明来自ittiger.cn,并保证转载后文章内容的完整性。本人(laohu)保留所有版权相关权利。



评论