作业要求:

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

要求:写一篇博客,将分析解决过程描述清楚,提交作业时附上链接地址,并上传改好的项目文件。

首先下载作业要求的项目,导入Android studio,由于该项目使用的gradle版本是2.0,版本比较低打不开。可以修改项目的的全局gradle文件,把版本改到最新版,修改方法如下:

    dependencies {
classpath 'com.android.tools.build:gradle:2.1.2' // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}

打开项目以后运行在模拟器,看到如下界面:

有两个按钮,分别是"StartActivityB"和"StartAllocation",

经过分析第一个按钮主要运行的是这段代码:

    private void startB() {
finish();
startActivity(new Intent(this, ActivityB.class));
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
System.out.println("post delayed may leak");
}
}, 5000);
Toast.makeText(this, "请注意查看通知栏LeakMemory", Toast.LENGTH_SHORT).show();
}

代码意义关闭当前的Activity页面,跳转至另一个Activity,同时启动一个延时Handler子线程,打印一段log。

第二个按钮的主要代码是:

    private void startAllocationLargeNumbersOfObjects() {
Toast.makeText(this, "请注意查看MemoryMonitor 以及AllocationTracker", Toast.LENGTH_SHORT).show();
for (int i = 0; i < 10000; i++) {
Rect rect = new Rect(0, 0, 100, 100);
System.out.println("-------: " + rect.width());
}
}

意义为一个toast菜单和一段循环代码,用以创建10000个Rect对象。

好了,下面开始分析存在的内存问题:

一、使用Memory Monitor进行分析。

首先打开项目,启动MemoryMonitor进行检测,检测图如下:

首次打开以后

可以看到蓝色的部分表示已分配的内存,淡蓝色的部分表示未被使用的内存。蓝色部分稳定在2.75MB,点击第二个按钮以后的图形如下:

可以看到点击以后出现了好几次内存比较明显的起伏波动,说明程序的内存使用是有问题的,下面继续分析。

二、Allocation Tracker分析

通过这个工具的分析就可以进一步定位出问题的位置到底在哪儿。

通过这张分析图可以看到,在mainactivity当中,新建了大量的rect和StringBuilder对象,导致出现了大量的内存占用。

找到了问题所在,就是因为第二个按钮执行的代码通过一个for循环,创建了一万个rect对象引起的。现在就来解决这个问题,代码如下:

    private void startAllocationLargeNumbersOfObjects() {
Toast.makeText(this, "请注意查看MemoryMonitor 以及AllocationTracker", Toast.LENGTH_SHORT).show();
Rect rect = null;
for (int i = 0; i < 10000; i++) {
if (rect==null) {
rect = new Rect(0, 0, 100, 100);
System.out.println("-------: " + rect.width());
}
}
}

通过在for循环里面加了一个if判断来解决了这个问题,如果对象已经存在,则不会再次创建,这样就只会创建一次对象。

三、LeakCanary分析

使用这种工具来分析,需要在gradle里面做一下依赖,这里就不再多说,下面看运行以后的提示:

根据这个页面的提示,MainActivity中的一个静态sTextView引用了这个页面的context,导致了内存泄露。

查看了相关代码发现sTextView是一个静态对象,这应该就是一个静态对象引用了外面的非静态类引起的内存泄露。

把这个对象的静态修饰符删掉即可解决问题。

private static TextView sTextView;//修改前

private TextView sTextView;//修改后

解决了内存问题以后,再来对视图进行优化。

首先在手机开发者选项当中打开“显示GPU过度绘制”选项,然后打开项目APP,效果如下:

首先对自定义的控件进行优化,分析一下代码:

    @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas); RectF rect = new RectF(0, 0, 100, 100);
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setStrokeWidth(4);
canvas.drawArc(rect, 0, 180, true, paint);
}

可以看到,在onDraw方法当中创建了两个新对象,由于onDraw方法是会反复刷新调用的,在这里创建对象,就会造成过度绘制,所以我把这两个创建对象的操作移动到构造方法里面进行,整个的代码如下:

 public class MyView extends View {

     private RectF mRect = null;
private Paint mPaint = null; public MyView(Context context) {
super(context);
init();
} public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
} public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
} private void init() {
if (mPaint == null || mRect == null) {
mRect = new RectF(0, 0, 100, 100);
mPaint = new Paint();
}
} @TargetApi(Build.VERSION_CODES.LOLLIPOP)
public MyView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
} @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setColor(Color.RED);
mPaint.setStrokeWidth(4);
canvas.drawArc(mRect, 0, 180, true, mPaint);
}
}

大致上针对这个小demo的优化就做完了,当然肯定还有很多不足的地方,希望同学们多多指教,指出不足。

第十二章作业 MemoryBugs-master项目优化笔记的更多相关文章

  1. <构建之法>第十一章、十二章有感

    十一章:软件设计与实现 工作时要懂得平衡进度和质量.我一直有一个困扰:像我们团队这次做 男神女神配 社区交友网,我负责主页的设计及内容模块,有个队友负责网站的注册和登录模块,有个队友负责搜索模块,有个 ...

  2. 《Linux命令行与shell脚本编程大全》 第二十二章 学习笔记

    第二十二章:使用其他shell 什么是dash shell Debian的dash shell是ash shell的直系后代,ash shell是Unix系统上原来地Bourne shell的简化版本 ...

  3. [CSAPP笔记][第十二章并发编程]

    第十二章 并发编程 如果逻辑控制流在时间上是重叠,那么它们就是并发的(concurrent).这种常见的现象称为并发(concurrency). 硬件异常处理程序,进程和Unix信号处理程序都是大家熟 ...

  4. perl5 第十二章 Perl5中的引用/指针

    第十二章 Perl5中的引用/指针 by flamephoenix 一.引用简介二.使用引用三.使用反斜线(\)操作符四.引用和数组五.多维数组六.子程序的引用  子程序模板七.数组与子程序八.文件句 ...

  5. 第十二章——SQLServer统计信息(1)——创建和更新统计信息

    原文:第十二章--SQLServer统计信息(1)--创建和更新统计信息 简介: 查询的统计信息: 目前为止,已经介绍了选择索引.维护索引.如果有合适的索引并实时更新统计信息,那么优化器会选择有用的索 ...

  6. JavaScript DOM编程艺术-学习笔记(第十二章)

    第十二章 1.本章是综合前面章节的所有东西的,一个综合实例 2.流程:①项目简介:a.获取原始资料(包括文本.图片.音视频等) b.站点结构(文件目录结构) c.页面(文件)结构 ②设计(切图) ③c ...

  7. 《Django By Example》第十二章 中文 翻译 (个人学习,渣翻)

    书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者注:第十二章,全书最后一章,终于到这章 ...

  8. 第二十二章 Django会话与表单验证

    第二十二章 Django会话与表单验证 第一课 模板回顾 1.基本操作 def func(req): return render(req,'index.html',{'val':[1,2,3...]} ...

  9. Gradle 1.12用户指南翻译——第五十二章. Maven 插件

    本文由CSDN博客貌似掉线翻译,其他章节的翻译请参见:http://blog.csdn.net/column/details/gradle-translation.html翻译项目请关注Github上 ...

随机推荐

  1. Unity NGUI 网络斗地主 -界面制作

    Unity NGUI 网络斗地主 -界面制作 源文件在群(63438968群共享!) @灰太龙 这一节说一下NGUI的界面摆放,并且教会大家使用NGUI的自适应功能! 在这里感谢@Gamer,是他给我 ...

  2. codeforces D. Long Path

    http://codeforces.com/contest/408/problem/D 题意:有一排房间每个房间有两扇门,一扇通往第i+1个房间,另一扇通往第p[i]个房间,(p[i]<=i)然 ...

  3. 转一篇NGINX+UWSGI+PYTHON+DJANGO部署文档

    高远弄的,,专业,明晓..感谢哈哈. http://blog.csdn.net/tmpbook/article/details/42873667

  4. C# yield return 用法与解析

    原文:C# yield return 用法与解析 C# yield return 用法与解析 本文参考自:http://www.jb51.net/article/54810.htm 当初没有认真理解 ...

  5. 9.DataPager

    ListView搭配DataPager控件实现分页.有两种使用方式:一是将DataPager声明到ListView中:一种是DataPager\ListView没有嵌套关系,然后将DataPager的 ...

  6. Mac开发者必备实用工具推荐

    最近一个师兄给我推荐了一些Mac上的实用工具,用起来非常顺手,能提高不少开发效率.于是就想着把自己之前用过的其他工具也整理一下,一块推荐给大家,希望能对大家有帮助. Alfred 目前Mac下最好用的 ...

  7. COJ 0346 WZJ的旅行(二)更新动态树分治版本

    WZJ的旅行(二) 难度级别:D: 运行时间限制:3000ms: 运行空间限制:51200KB: 代码长度限制:2000000B 试题描述 时隔多日,WZJ又来到了幻想国旅行.幻想国由N个城市组成,由 ...

  8. Linux Kernel 释放后重用内存损坏漏洞

    漏洞名称: Linux Kernel 释放后重用内存损坏漏洞 CNNVD编号: CNNVD-201307-305 发布时间: 2013-07-18 更新时间: 2013-07-18 危害等级:    ...

  9. Threading Module源码概述(二)

    在threading 模块中,提供了列举当前所有子线程的操作.threading.enumerate.这个操作很简单,就是将_active和_limbo中维护的线程集合的信息输出. def enume ...

  10. poj2245

    题目分析: 抽奖 在德国乐透你需要从49个数(1,2,3......49)里面选择6个数,玩德国乐透一个流行的策略-尽管他不会增加你获奖的机会-选择一个子集S包含k(k>6)个数从这49个数字里 ...