Android启动保护模式实践

来源:互联网 发布:数据挖掘 教学大纲 编辑:程序博客网 时间:2024/05/22 00:05

一般初始化app的第三方类库或者so库操作都会放在Application的onCreate()方法里,因为此时Application才算创建完成,在这里初始化是自然而然的。由于我们的应用是直播类,音视频操作和接口调用是在jni层处理,初始化这两个so库也是放在Application的onCreate里,但前段时间发生了一次偶发性但非常严重的bug。当时是服务端接口升级出现问题,导致处理接口的so库一初始化就崩溃。正式发布的应用出现这种问题可以说是致命的,用户打开你的应用,连首页都没见到就crash,影响特别不好。且由于检测App升级是在首页初始化才调用,导致后台配置接口让用户强制升级解决问题都没办法,束手无此。

通常来讲,再烂的应用在正常的条件下都能保证正常打开,但在一些特殊条件下是有可能导致应用重复崩溃的,例如:

  • 数据库损坏:在日常使用如异常退出、断电,或者错误的操作
  • 文件损坏:处理文件时如果没有 @try…catch,损坏文件会抛出 IOException导致crash
  • 网络返回数据处理异常:比如预期返回数组,但实际返回了字典,java.lang.ClassCastException
  • 代码 bug:当必 crash 的代码出现在启动关键路径中,就会导致连续闪退。

以上这些条件如果测试到位,能大大减少出现的概率,只不过程序的世界并不存在100%的情况,我们要做的事是当出现异常时,我们能及时甚至无感的将bug修复掉。

经分析,由于我们app使用数据库及文件的地方不多,媒体和接口的so库导致问题的概率比较大,我们重点考虑修复这种情况,当多次初始化so库崩溃时,能让app去下载新的so库并加载新的so库。

想要做到以上这点,涉及到两个问题,检测及更新。

检测

检测有两种办法:

1、 崩溃记录法:使用bugly之类的检测奔溃的sdk,在特定时间里(比如5s),检测到崩溃后就记录一次,连续三次都在5s内崩溃就延迟所有初始化,走修复流程;期间有一次正常运行超过5s计数清0;

2、时间记录法:在特定的时间内(比如5s),如果app退出,就记录一次,然后设定一个阀值(比如3次),达到阀值就走修复流程。如果达到阀值前有一次超过5s,计数就清0;

其实这两者有点相似,区别是第1种需要检测崩溃,第2种不用,相应的第1种更复杂但不会误触发,第2种简单但有概率误触发。

我首先用Bugly实践第一种方法,结果发现虽然Bugly有检测到崩溃回调的函数,但这个回调处理是在Java层,而jni有些崩溃会导致jvm直接退出,如果在java层接受回调肯定是收不到的。jni层的崩溃只能在jni层检测,由于我们的so库是多个平台公用,专为Android端增加此功能的阻力较大,这种方式只好放弃。

于是我们采用了第2种方式,时间记录法,下面是流程图:

更新

更新的前提是进入修复流程,修复流程是不初始化任何第三方so库及其他正常需要初始化的类,等到修复完才回到正常流程。下面是流程图:

更新 .png

上面的流程是比较简单的,不过需要解决两个问题:

1、 下载so库时由于欢迎页都没初始化,怎么显示交互,总不能一片白色,让用户以为ARN吧?

我们在下载so库时,动态把ProgressBar加进android.R.id.content

 //添加进度条mDownloadPb = (ProgressBar)LayoutInflater.from(mContext).inflate(R.layout.pb_update_so,(ViewGroup) ((Activity)mContext).findViewById(android.R.id.content)).findViewById(R.id.pb_update_so);

以下是下载so库时的截图:

下载.png

2、下载成功后怎么让程序使用最新的so库

首先本地得保存你每次发布时so库的版本号,以及每次你更新完需要保存更新下来so库的版本号,每次load之前都先对比一下,如果下载下来的版本号更新及文件存在的话,就判定为新的so库,加载此库即可。

if (BuildConfig.LIB_VERSION < PreferenceHelper.getInstance().getLibVersion()) {    File soPath = new File(FileUtil.getSoLibPath(),LIB_FILE_NAME));    if (FileUtil.fileIsExists(soPath)) {        System.load(soPath.getAbsolutePath());    } else {        System.loadLibrary(PROTOCOL_LIB_NAME);    }} else {        System.loadLibrary(PROTOCOL_LIB_NAME);}

注:本文的设计思想参考自微信读书的文章

0 0
原创粉丝点击