码迷,mamicode.com
首页 > 移动开发 > 详细

android 性能分析案例

时间:2016-05-18 01:40:10      阅读:357      评论:0      收藏:0      [点我收藏+]

标签:

    本章以实际案例分析在android开发中,性能方面的优化和处理。设计到知识点有弱引用,memory monitor,Allocation Tracker和leakcanary插件。

1.测试demo

    下载bug项目:https://github.com/lzyzsd/MemoryBugs,请注意配合使用MemoryMonitor, AllocationTracker以及HeapDump,LeakCanary等工具来查找潜在的内存问题,并尝试解决。

2.测试工具介绍

(1)memory monitor简介

    AndroidStudio提供了Memory Monitor来实时显示应用运行时内存占用情况,下边蓝色部分是现在占用的内存,上面灰色的部分显示是已回收的内存。如果在图上看到尖峰,也就是快速分配内存又被回收,也就是发生了内存抖动,这里就是需要优化的地方。在模拟器或者真机中Debug项目,可实时监看Memory,CPU,NetWork等的资源占用情况。

技术分享

(2)Allocation Tracker简介

    单击Allocation tracker标签,就会打开一个新的窗口,单击“Start Tracing”按钮;然后,让应用运行你想分析的代码。运行完毕后,单击“Get Allocations”按钮,一个已分配对象的列表就会出现第一个表格中。

单击第一个表格中的任何一项,在表格二中就会出现导致该内存分配的栈跟踪信息。通过allocation tracker,不仅知道分配了哪类对象,还可以知道在哪个线程、哪个类、哪个文件的哪一行。查看方式如下:

技术分享

  • Group by Method:用方法来分类我们的内存分配
  • Group by Allocator:用内存分配器来分类我们的内存分配

统计模式

    轮胎图是以圆心为起点,最外层是其内存实际分配的对象,每一个同心圆可能被分割成多个部分,代表了其不同的子孙,每一个同心圆代表他的一个后代,每个分割的部分代表了某一带人有多人,双击某个同心圆中某个分割的部分,会变成以你点击的那一代为圆心再向外展开。如果想回到原始状态,双击圆心就可以了。

技术分享

    圆心是起点处,如果你把鼠标放到我图中标注的区域,会在右边显示当前指示的是什么线程(Thread1)以及具体信息(分配了8821次,分配了 564.18k的内存),但是红框标注的区域并不代表Thread1,而是第一个同心圆中占比最大的那个线程,所以现在把鼠标放到第一个同心圆上,可 以看出来,划过同心圆的轨迹时可以看到右边的树枝的变化情况。

3.案例分析

(1)内存波动

    内存波动指不合理的设计,在一瞬间创建了多个对象,有及时释放时产生影响内存性能的现象。案例中的问题代码如下:

技术分享

    执行代码引起内存波动的分析图如下:

技术分享

改良方案

    不应该在for语句里面频繁的New对象。可先在外面创建好一个对象,在for里面直接调用对象。具体代码修改,请参考最后上传性能改良后的代码。改良后内存监控如同所示:

技术分享

(2)线程致使内存泄露

    Android中使用Handler造成内存泄露的例子代码如下:

1 Handler mHandler = new Handler() {
2     @Override
3     public void handleMessage(Message msg) {
4         mImageView.setImageBitmap(mBitmap);
5     }
6 }

 泄露原因分析

    上面是一段简单的Handler的使用。当使用内部类(包括匿名类)来创建 Handler的时候,Handler对象会隐式地持有一个外部类对象(通常是一个Activity)的引用(不然就不可能通过Handler来操作 Activity中的View)。而Handler通常会伴随着一个耗时的后台线程(例如从网络拉取图片)一起出现,这个后台线程在任务执行完毕(例如 图片下载完毕)之后,通过消息机制通知Handler,然后Handler把图片更新到界面。然而,如果用户在网络请求过程中关闭了Activity,正常情况下,Activity不再被使用,它就有可能在GC检查时被回收掉,但由于这时线程尚未执行完,而该线程持有Handler的引用,这个Handler又持有Activity的引用,就导致该Activity无法被回收(即内存泄露),直到网络请求结束(例如图片下载完毕)。另外,如果你执行了Handler的postDelayed()方法,该方法会将你的Handler装入一个Message,并把这条 Message推到MessageQueue中,那么在你设定的delay到达之前,会有一条MessageQueue -> Message -> Handler -> Activity的链,导致你的Activity被持有引用而无法被回收。

内存泄露的危害
     内存泄露会出现虚拟机占用内存过高的危害,导致OOM(内存溢出),程序出错。对于Android应用来说,用户打开一个Activity,使用完之后关闭它,内存泄露;又打开,又关闭,又泄露;几次之后,程序占用内存超过系统限制,致使程序崩溃。使用Handler导致内存泄露的解决方法如下:

  • 通过程序逻辑来进行保护

     1.在关闭Activity的时候停掉你的后台线程。线程停掉了,就相当于切断了Handler和外部连接的线,Activity自然会在合适的时候被回收。
     2.如果Handler是被delay的Message持有了引用,使用相应的Handler的removeCallbacks()方法,把消息对象从消息队列移除就行了。

  • 将Handler声明为静态类

     静态类不持有外部类的对象,所以Activity可以随意被回收。代码如下:

1 static class MyHandler extends Handler {
2     @Override
3     public void handleMessage(Message msg) {
4         mImageView.setImageBitmap(mBitmap);
5     }
6 }

     声明静态类后,由于Handler不再持有外部类对象的引用,导致程序不允许该Handler去操作Activity中的对象了。解决方法是在Handler中增加一个对Activity的弱引用(WeakReference)。参考代码如下:

 1 static class MyHandler extends Handler {
 2     WeakReference<Activity > mActivityReference;
 3     MyHandler(Activity activity) {
 4         mActivityReference= new WeakReference<Activity>(activity);
 5     }
 6 
 7     @Override
 8     public void handleMessage(Message msg) {
 9         final Activity activity = mActivityReference.get();
10         if (activity != null) {
11             mImageView.setImageBitmap(mBitmap);
12         }
13     }
14 
15 }

WeakReference简介
     WeakReference 弱引用,与强引用(即我们常说的引用)相对,它的特点是,GC在回收时会忽略掉弱引用,即就算有弱引用指向某对象,但只要该对象没有被强引用指向(实际上多数时候还要求没有软引用,但此处软引用的概念可以忽略),该对象就会在被GC检查到时回收掉。对于上面的代码,用户在关闭Activity之后,就算后台线程还没结束,但由于仅有一条来自Handler的弱引用指向Activity,所以GC仍然会在检查的时候把Activity回收掉。这样,内存泄露的问题就不避免了。

    案例中存在hander内存泄露的代码截图如下:

技术分享

技术分享

    通过上一章介绍的leakcanary插件,可检查到内存泄露相关的代码,leakcanary检查运行截图如下:

技术分享

4.性能改良后源代码

本项目源代码在360云盘上,开发环境为 Android Studio 2.0 。

https://yunpan.cn/cPSA8uyeL2CwY  访问密码 dc98。文件名称:android性能改良代码。

 

android 性能分析案例

标签:

原文地址:http://www.cnblogs.com/wlandwl/p/android_13.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!