标签:
+ 可用工具:Profile GPU Rendering查看渲染的表现性能;Show GPU view updates查看视图更新的操作;HierarchyViewer查看界面布局结构;
+ 目标/要点:使布局扁平化,移除非必要组件,避免使用嵌套layout(尤其是根节点方向的RelativeLayout和有weight的LinearLayout)
+ S1E7: Overdraw, Cliprect, QuickReject
+ 标准组件(或其组合)的Overdraw可以使用工具来检测、消除;同时系统也会避免绘制完全不可见的组件;
+ 完全自定义组件(重写onDraw方法),上述方案将无法实现;可通过canvas.clipRect()
来帮助系统识别可见区域,完全在矩形外面的内容(组件)将不会绘制、渲染,但有部分在矩形内的仍会绘制、渲染;
+ canvas.quickreject()
函数可以判断是否和某个矩形相交,如果不相交,则可以直接跳过;
+ 性能优化总原则之一:先测量效果,发现问题再寻找根源,尝试改进后要再次测量效果进行对比
+ S1E8: Memory Churn and performance
+ Android的堆内存分代回收模型如下图示
+ GC时会暂停所有其他线程,导致GC频繁的可能原因
+ Memory Churn(内存抖动),大量对象被创建,然后立即被销毁,例如在onDraw等函数中创建对象
+ 瞬间产生大量的对象会严重占用Young Generation的内存区域,当达到阀值,剩余空间不够的时候,也会触发GC。同时可能触发其他区域(代)的GC
+ 使用Android studio的Memory Monitor,可以观察是否存在内存抖动,Memory monitor集成了Allocation Tracker,使用Allocation Tracker可以查看每个线程的内存分配情况,可以具体到某个函数的某行代码
+ 平常需要注意的是:onDraw等这样会被高频率反复调用的函数、循环体内部等,避免创建新对象
+ S1E9: Garbage Collection in Android
+ 安卓系统使用的虚拟机是三代模型:Young, old, permanent;越往后每代GC时间越长;
+ 应该避免频繁GC
+ S1E10: Performance Cost of Memory Leaks
+ 一个典型的Activity泄露场景:Activity类内部的非静态Handler子类(或匿名类)实例
+ LeakCanary工具是检测内存泄漏的好工具
+ S1E11: Memory Performance
+ 工具集:Memory Monitor:观察是否存在内存抖动;Allocation Tracker:追踪内存分配情况;Heap Tool:查看内存快照,分析可能泄露的对象;
+ S1E12: Tool - Memory Monitor,无甚可记
+ S1E13: Battery Performance
+ 尽量减少唤醒屏幕的次数与持续的时间,使用WakeLock来处理唤醒的问题,能够正确执行唤醒操作并根据设定及时关闭操作进入睡眠状态。
+ 某些非必须马上执行的操作,例如上传歌曲,图片处理等,可以等到设备处于充电状态或者电量充足的时候才进行。
+ 触发网络请求的操作,每次都会保持无线信号持续一段时间,我们可以把零散的网络请求打包进行一次操作,避免过多的无线信号引起的电量消耗。
+ Battery Historian Tool:查看APP电量消耗情况
+ JobScheduler API:对任务进行定时处理,例如等到手机处于充电状态,或连接到WiFi时处理任务
+ JobScheduler在API 21引入,Google play service有backport: GcmNetworkManager,也有第三方backport
+ S1E14: Understanding Battery Drain on Android
+ 使用WakeLock或者JobScheduler唤醒设备处理定时的任务之后,一定要及时让设备回到初始状态。
+ 每次唤醒无线信号进行数据传递,都会消耗很多电量,它比WiFi等操作更加的耗电
+ S1E15: Battery Drain and WakeLocks
+ WakeLock使用非精准定时器,允许系统为不同应用的wake lock请求进行打包处理,节约电量消耗
+ JobScheduler API还能做更多的事情,例如等到充电,或者连接上wifi时处理任务
adb shell dumpsys batterystats > xxx.txt
, python historian.py xxx.txt > xxx.html
BatteryManager
在代码中检测是否正在充电(也可使用JobScheduler来做)S2E6: To Index or Iterate?
int size = list.size();
for (int i = 0; i < size; i++) {
Object object = list.get(i);
...
}
for (Iterator it = list.iterator(); it.hasNext(); ) {
Object object = it.next();
...
}
for (Object object : list) {
...
}
Fcn | Time taken(ms) |
---|---|
for index (ArrayList) | 2603 |
for index (Vector) | 4664 |
for simple (ArrayList) | 5133 |
Iterator (ArrayList) | 5142 |
for simple (Vector) | 11783 |
Iterator (Vector) | 11778 |
+ S2E7: The Magic of LRU Cache
+ 安卓系统提供了LruCache类,已实现LRU算法
+ 慎用,手动释放缓存的对象,谨防内存泄漏
+ S2E8: Using LINT for Performance Tips,无甚可记
+ S2E9: Hidden Cost of Transparency
+ 通常来说,对于不透明的View,显示它只需要渲染一次即可,可是如果这个View设置了alpha值,会至少需要渲染两次
+ 在某些情况下,一个包含alpha的View有可能会触发该View在HierarchyView上的父View都被额外重绘一次
+ 在动画开始时,setLayerType(View.LAYER_TYPE_HARDWARE, null)
,动画结束后,setLayerType(View.LAYER_TYPE_NONE, null)
;在API >= 16时,可以只调用ViewPropertyAnimator.alpha(0.0f).withLayer()
接口即可;
+ 使用shadow时,重写View的hasOverlappingRendering()
接口,返回false;
+ 只有当确定瓶颈是这部分view的渲染时,才有必要这样优化;
+ S2E10: Avoiding Allocations in onDraw(),记住,实践即可
+ S2E11: Tool: Strict Mode
+ 开发者选项Strict Mode,如果存在潜在ANR隐患(主线程磁盘IO,网络请求等),屏幕会闪现红色
+ 也有代码的API,StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()...build());
和StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()...build());
,如果有隐患,屏幕会闪烁,并且有log输出
+ S2E12: Custom Views and Performance,避免以下问题:
+ Useless calls to onDraw(),仅在View内容发生变化时才调用View.invalidate()
;尽量使用ClipRect等方法来提高绘制的性能;
+ Useless pixels,减少绘制时不必要的元素,对于那些不可见的元素不要绘制;
+ Wasted CPU cycles,不在屏幕上的元素,可以使用Canvas.quickReject把他们给剔除;另外尽量使用GPU来进行UI的渲染,这样能够极大的提高程序的整体表现性能;
+ S2E13: Batching Background Work Until Later,上文有涉及
+ S2E14: Smaller Pixel Formats
+ dalvik虚拟机不会自动进行内存整理
+ Android为图片提供了4种解码格式:ARGB_8888, RGB_565, ARGB_4444, ALPHA_8,但是实际测试的时候,如果图片不变,显示图片的View区域不变,也不限制Bitmap只有View那么大,单纯设置bitmapOption.inPreferredConfig
好像没什么用
+ 可以通过将解码的Bitmap大小设置为View大小的两倍(单位是dp),实测内存基本可以降到1/16(当然前提是图片比View大很多),而且视觉效果基本一样
+ S2E15: Smaller PNG Files,以下建议结合微信安卓团队的分享
+ 没有透明度的文件,都可以用jpg
+ 对于体积特别大(超过50k)的图片资源可以考虑有损压缩,jpg采用优图压缩,png尝试采用pngquant压缩,输出视觉判断是否可行;
+ 对assets中的图片资源也使用aapt的crunch做图片预处理;
+ crunch有可能会使图片变大,在这种情况,我们可以替换成原图。需要注意的是对于.9.png,由于crunch过程中去除了黑边,所以不能替换;
+ 对于没有透明区域的png图片,可以转成jpg格式。
+ Webp:既保留png格式的优点,又能够减少图片大小
+ S2E16: Pre-scaling Bitmaps
+ bitmapOption.inSampleSize
属性,可等比例缩放图片,而且不会加载原图到内存
+ inScaled,inDensity,inTargetDensity的属性来对解码图片做处理
+ inJustDecodeBounds,只会先读取图片尺寸,不会加载到内存
+ S2E17: Re-using Bitmaps
+ 设置了inbitmapOption.Bitmap
后,系统会尝试使用已有的Bitmap来保存新的图片,避免内存的反复创建,但是这一方法有些不足:
+ 在SDK 11 -> 18之间,重用的bitmap大小必须是一致的
+ 从SDK 19开始,新申请的bitmap大小必须小于或者等于已经赋值过的bitmap大小
+ 新申请的bitmap与旧的bitmap必须有相同的解码格式
+ 可以结合对象池,给不同解码格式,不同大小的图片准备不同的Bitmap并进行复用,不过这样做难度较大
+ 开源图片加载库Glide,Fresco
+ S2E18: The Performance Lifecycle
+ Gather:测试收集数据
+ Insight:分析数据
+ Action:解决问题
+ 再次进行测试,验证效果
+ S2E19: Tools not Rules,上述建议需要灵活应用
+ S2E20: Memory Profiling 101,上文已述
onLowMemory()
,onTrimMemory()
java
for (int x = 0; x < valueCount; x++) {
cleanFiles[x] = new File(directory, key + "." + x);
dirtyFiles[x] = new File(directory, key + "." + x + ".tmp");
}
java
StringBuilder b = new StringBuilder(key).append(".");
int truncateTo = b.length();
for (int x = 0; x < valueCount; x++) {
b.append(x);
cleanFiles[x] = new File(directory, b.toString());
b.append(".tmp");
dirtyFiles[x] = new File(directory, b.toString());
b.setLength(truncateTo);
}
for (int i = 0, size = list.size(); i < size; i++)
标签:
原文地址:http://blog.csdn.net/zhoulei0623/article/details/51113514