分析项目中预览大图片滑动卡顿问题

分析项目中预览大图片滑动卡顿问题

最近在项目中实现一个较流行的图片预览功能,列表中有多张小图,点击小图时,图片以小图大小尺寸从原小图位置动画放大,退出时缩小到原小图大小尺寸位置,效果如下图:

效果图

实现方式是打开一个透明的Activity,控制ImageView的出、入场动画。但接入到项目中发现,打开图片较大时(图片大小约900k,载入内存消耗约20M,大图约3-4张),左右滑动会非常卡顿,分析后发现是系统绘制函数耗时严重(当然,在Demo中同样加载大图是没有问题得,项目应用本身消耗得内存也要大得多)。

最后发现在不使用透明Activity时滑动要流畅得多,因为没有研究过系统对Transparent Activity得实现原理,所以并不清楚具体是什么原因导致得,最后得解决方案是预览启动前,截取当前屏幕,做为预览界面背景图,实现假透明启动效果,而且加载及滑动效果较为流畅。

分析时使用得是Android Studio 3.0 下的 Androd Profiler 工具,下面是网上一个相同问题的文章,使用的是TraceView分析,贴在这里,以总结归纳。

原文:Android学习之性能工具traceView使用

【2016年3月30】

这几天分析一个滑动卡顿的问题,使用了traceView工具

问题点:

写邮件添加图片附件时,滑动编辑界面会出现卡顿现象,测试时发现只有特定图片才会卡顿

分析项目中预览大图片滑动卡顿问题_第1张图片
image

有两种方法获取trace文件:

方法一

1、打开DDMS,点击某一进程,点击Start Method Profiling
image

,然后操作界面,再点击Stop Method Profiling
image
,便会生出***.trace文件,包含时间轴和分析面板。


时间轴面板(Timeline Panel),包含这段时间内各个线程的执行,这里我截取了1840ms间的操作

分析项目中预览大图片滑动卡顿问题_第2张图片
image

分析面板(Profile Panel)


分析项目中预览大图片滑动卡顿问题_第3张图片
image
列名 描述
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类似,只不过统计单位换成了真实时间

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,按函数运行的真实时间排列,得出下图结果


分析项目中预览大图片滑动卡顿问题_第4张图片
image

这里发现,滑动界面时,基本的耗时操作都在nSyncAndDrawFrame这个函数这里,为什么这样断定?因为我觉得1034.763ms和110.666ms这个时间间隔太大了,值得怀疑,于是点开这个函数,发现大部分时间都耗在context switch上

image

到这里没什么思路了,于是点击它的parent一级级找出它的调用栈

分析项目中预览大图片滑动卡顿问题_第5张图片
image

看这个调用栈,搜了一下 ThreadedRenderer,了解到这是一个android绘图的过程,看了一下ThreadedRenderer.javadraw的代码,这是一个同步过程,咨询了一下UI FW的同事,了解到nSyncAndDrawFrame会阻塞在这里,等待UI threadRenderThread同步,如果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同步时花费的时间大大减少了

分析项目中预览大图片滑动卡顿问题_第6张图片
image

【2016年4月7日】

这段时间公司在进行samsung app启动速度优化的工作,抓取了一下app启动时的traceView,根据Incl Cpu Time,发现启动过程有个地方消耗了200多毫秒,这对所有app都是额外的消耗

分析项目中预览大图片滑动卡顿问题_第7张图片
image

根据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的同事去调查

方法二

有时使用方法一只能在总体上查看时间,如果想知道更小范围的时间消耗,可以在代码中使用这个方法

android.os.Debug.startMethodTracing("EmailTest");
android.os.Debug.stopMethodTracing();

在需要查看的代码段中插入上面的代码,运行后就可以生成trace文件了,使用adb命令取出来adb pull /sdcard/EmailTest.trace D:\

你可能感兴趣的:(分析项目中预览大图片滑动卡顿问题)