记一次APP卡顿问题的解决历程

       大约在2017年的7、8月份,我开发的一款APP突然出现了页面跳转卡顿,掉帧,然后崩溃的现象,我查看日志,只看到了一堆掉帧的提示,而没有错误点报错提示,也非逻辑错误,总之就是“我就是卡顿,但我就是不告诉你哪卡顿了,是谁导致我卡顿了”!

      由于之前没有出现过这个问题,于是我开始查找APP卡顿的问题解释,然后网友们说的最多的是在主线程里有严重的耗时操作,页面刷新频率低于16帧/ms导致了卡顿,然后我在每个Activity的每个onCreate( )方法里的语句前后加入了程序运行时间输出语句,以此作为推测卡顿区域的工具,随后发现一些onCreate( )的执行时间过长,超过了16ms,而Android系统每隔16ms会发出VSYNC信号重绘我们的界面(Activity)。为什么是16ms, 因为Android设定的刷新率是60FPS(Frame Per Second), 也就是每秒60帧的刷新率, 约合16ms刷新一次。于是,我重新深入的学习了Activity的生命周期,其生命周期官方版示意图如下所示:

记一次APP卡顿问题的解决历程

       随后自己总结了生命周期中各个阶段或者方法对应所包括的操作,具体如下:

onCreate:表示activity正在创建,可以做一些初始化的工作,比如加载界面布局资源。

onRestart:表示activity正在重新启动,一般情况下当当前Activity从不可见到可以见的时候,该方法会被调用。

onStart:表示Activity正在被启动,activity可见但是没有出现在前台,不可以操作。

onResume:表示activity已经可见了,并且出现在前台活动,可以做一些储存数据的操作,但不能做太耗时操作。

onPause:表示activity正在停止,处于可见但是不可以操作。

onStop:表示Activity即将停止,activity变得不可见,可以做稍微重点的回收工作。

onDestroy:表示Activity即将被销毁,我们可以做一些回收工作和资源的最终释放。

       学习完生命周期后,我开始将onCreate(中出了页面绘制以外的代码分散到onStart和onResume中去,到这里相信onCreate中的负担已经降到了最轻,高高兴兴的重新打包,真机实验运行,然后从主界面进入功能模块界面--正常!然后在模块操作完跳回主界面时,啊吘,出问题了,还是卡顿,极其严重的卡顿,人生最卡吧!!!万般无奈之下,我再次使用了程序运行时间输出,无解。。。后来我就开始逐行查看Activity中的代码,看呀看,看呀看,好像有点眉目了,在临下班时将问题定位到“首页onResume()函数之后或同时有耗时操作,该操作极有可能是照片选择并上传后未关闭的流或者位图缓存或者线程。。。”,第二天上班后,我在照片选择上传模块进行地毯式搜索,发现输入输出流都在使用完毕后进行了,也没有多余的线程在额外消耗系统资源,在深思了许久之后,我出实验室休息了一下,转了一圈,心情稍稍舒畅了一些后,回到了工位,对模块功能代码进行再一次梳理,在细心地查看下,我发现了一个全局变量,它贯穿了各个调用选择照片并上传的Activity,它的名字叫做“public static ArrayList<ImageItem> tempSelectBitmap=new ArrayList<ImageItem>(); // 已选图片列表”,是它,就是它,它彻彻底底的卡顿了我的APP,在每次调用照片功能模块的代码后,我都对它进行了.clear()操作,但是它依旧不消停,给人添堵,究其原因还是因为其对象实例存在于内存的时间太久,真的太久了,几乎贯穿了APP所有的运行阶段,于是我一狠心,一跺脚,在onDestroy里加写成了这样:

 if (Bimp.tempSelectBitmap != null) {
try {
Bimp.tempSelectBitmap.clear();
Bimp.tempSelectBitmap = null;//这句话很重要
} catch (Exception e) {
e.printStackTrace();
}
}
,再次运行APP,终于流畅了,毫无压力的流畅,鄙人内心回归平静.....
  那么接下来咱们总结一下APP卡顿的情况吧:
(1)主线程内有严重耗时的操作;
(2)有输入输出流、子线程在使用完毕后忘记关闭;
(3)Activity的布局层次太深,导致界面过度绘制,从而引起卡顿;
(4)有占用较大内存的全局变量在作祟,比如我遇到的这个。
  开发建议:
(1)APP布局设计时尽量减少层级,同时在能使用LinearLayout或者RelativityLayout解决的布局就不要用其他复杂繁琐的布局;
(2)子线程使用完后一定要及时关闭,输入输出流也一样;
(3)不用过度使用全局变量,如果使用,请及时回收,有时JVM不能满足实际开发需求。

补充一下,上次解决卡顿问题虽然成功了,但却引起了偶发的ANR,随后查看程序日志发现是Bimp.tempSelectBitmap成为了null对象,导致引用他的程序语句形成空指针异常,于是我又进一步研究了所选照片列表清理办法,在另一个类里面找到了答案,在那个类里面,有一个Bimp.max与Bimp.tempSelectBitmap.size()在每个使用照片选择功能的界面都会进行比较,然后进行相应情况下的操作,因此在清理所选照片的时候还需要把Bimp.max也清理一下---即Bimp.max=0;这一语句需要加上,而之前的Bimp.tempSelectBitmap = null;这句就要拉掉了,至此,页面卡顿与偶发ANR解决完毕。