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

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

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

效果图

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

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

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

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

【2016年3月30】

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

问题点:

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

image

有两种方法获取trace文件:

方法一

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

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


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

image

分析面板(Profile Panel)


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


image

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

image

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

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同步时花费的时间大大减少了

image

【2016年4月7日】

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

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:\

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,026评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,655评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,726评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,204评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,558评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,731评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,944评论 2 314
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,698评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,438评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,633评论 2 247
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,125评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,444评论 3 255
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,137评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,103评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,888评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,772评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,669评论 2 271

推荐阅读更多精彩内容