【2016年3月30】
这几天分析一个滑动卡顿的问题,使用了traceView工具
问题点:
写邮件添加图片附件时,滑动编辑界面会出现卡顿现象,测试时发现只有特定图片才会卡顿
1、打开DDMS,点击某一进程,点击Start Method Profiling,然后操作界面,再点击Stop Method Profiling,便会生出***.trace文件,包含时间轴和分析面板。
时间轴面板(Timeline Panel),包含这段时间内各个线程的执行,这里我截取了1840ms间的操作
分析面板(Profile Panel)
2、分析面板每一列的含义(来源互联网),默认按照Incl Cpu Time排列
列名 |
描述 |
Name |
该线程运行过程中所调用的函数名 |
Incl Cpu Time |
某函数占用的CPU时间,包含内部调用其它函数的CPU时间 |
Excl Cpu Time |
某函数占用的CPU时间,但不含内部调用其它函数所占用的CPU时间 |
Incl Real Time |
某函数运行的真实时间(以毫秒为单位),内含调用其它函数所占用的真实时间 |
Excl Real Time |
某函数运行的真实时间(以毫秒为单位),不含调用其它函数所占用的真实时间 |
Call+Recur Calls/Total |
某函数被调用次数以及递归调用占总调用次数的百分比 |
Cpu Time/Call |
某函数调用CPU时间与调用次数的比。相当于该函数平均执行时间 |
Real Time/Call |
同CPU Time/Call类似,只不过统计单位换成了真实时间 |
3、分析过程
这里我点击Incl Real Time,按函数运行的真实时间排列,得出下图结果
这里发现,滑动界面时,基本的耗时操作都在nSyncAndDrawFrame这个函数这里,为什么这样断定?因为我觉得1034.763ms和110.666ms这个时间间隔太大了,值得怀疑,于是点开这个函数,发现大部分时间都耗在context switch上
到这里没什么思路了,于是点击它的parent一级级找出它的调用栈
看这个调用栈,搜了一下ThreadedRenderer,了解到这是一个android绘图的过程,看了一下ThreadedRenderer.java中draw的代码,这是一个同步过程,咨询了一下UI FW的同事,了解到nSyncAndDrawFrame会阻塞在这里,等待UI thread和RenderThread同步,如果APP有自己创建或者维护的bitmap时,RenderThread必须等Bitmap上传到GPU之后才会释放掉UI Thread, 如果APP没有提供或维护UI相关的Bitmap时, UI Thread不会阻塞,调用nSyncAndDrawFrame时瞬间就会返回。这里包含了一个重要信息,这个函数会阻塞UI thread,所以应该可以定位到是这里造成卡顿现象了。既然是bitmap相关的,这个滑动操作确实会把图片的缩略图显示出来,所以就去代码中寻找和bitmap相关的。
获取图片附件缩略图的代码是这样的
orgImage = MediaStore.Images.Thumbnails.getThumbnail(mContext.getContentResolver(), id, MediaStore.Images.Thumbnails.MINI_KIND, null); if(orgImage != null && degree != 0 ) { Matrix m = new Matrix(); m.postRotate(degree); orgImage = Bitmap.createBitmap(orgImage, 0, 0, orgImage.getWidth(), orgImage.getHeight(), m, true); } return orgImage;
这里发现,直接获取图片后把根据orgImage的宽和高创建bitmap,单步调试到这里的时候,发现发生问题的bitmap的宽和高分别是4800和4233,天啊,这数据量也太大了,所以怀疑是图片数据太大导致RenderThread处理不过来,堵塞UI thread,从而造成卡顿,于是把代码改成下面这样,把宽和高按照需要显示的宽高等比例缩小
orgImage = MediaStore.Images.Thumbnails.getThumbnail(mContext.getContentResolver(), id, MediaStore.Images.Thumbnails.MINI_KIND, null); Matrix matrix = new Matrix(); float scaleWidth = ((float) width ) / orgImage.getWidth(); float scaleHeight = ((float) height ) / orgImage.getHeight(); matrix.postScale(scaleWidth, scaleHeight); orgImage = Bitmap.createBitmap(orgImage, 0, 0, orgImage.getWidth(), orgImage.getHeight(), matrix, true); if(orgImage != null && degree != 0 ) { Matrix m = new Matrix(); m.postRotate(degree); orgImage = Bitmap.createBitmap(orgImage, 0, 0, orgImage.getWidth(), orgImage.getHeight(), m, true); } return orgImage;
编出来一试,终于迎刃而解,几天下来的烦恼和压力也瞬间消失了,累觉不爱。
修改后,再抓一次traceView,发现RenderThread同步时花费的时间大大减少了
【2016年4月7日】
这段时间公司在进行samsung app启动速度优化的工作,抓取了一下app启动时的traceView,根据Incl Cpu Time,发现启动过程有个地方消耗了200多毫秒,这对所有app都是额外的消耗
根据parent和children的关系,找出这里的调用栈,如下
ActivityThread.parseCSCAppResource
ActivityThread.getCSCAppStringMap
ApplicationPackageManager.getCSCPackageItemText
ComponentInfo.loadLabel
ActivityThread.performLaunchActivity
查看ActivityThread.java的源码,最终parserCSCAppResource这个函数会去读取/system/csc/appresource/CSCAppResource.xml这个文件,文件操作会耗费大量时间,对于samsung app来说,启动应该是不需要读取运营商的内容的,所以这里去掉可对所有app的启动速度都能节省200多毫秒,最后交给app FW的同事去调查