转载自:http://www.cnblogs.com/qianxudetianxia/p/3645106.html

Context作为最基本的上下文,承载着Activity,Service等最基本组件。当有对象引用到Activity,并不能被回收释放,必将造成大范围的对象无法被回收释放,进而造成内存泄漏。

下面针对一些常用场景逐一分析。

1. CallBack对象的引用

先看一段代码:

  1. @Override
  2. protectedvoid onCreate(Bundle state){
  3. super.onCreate(state);
  4.  
  5. TextView label =new TextView(this);
  6. label.setText("Leaks are bad");
  7.  
  8. setContentView(label);
  9. }

大家看看有什么问题吗?

没问题是吧,继续看:

  1. private static Drawable sBackground;
  2.  
  3. @Override
  4. protected void onCreate(Bundle state){
  5. super.onCreate(state);
  6.  
  7. TextView label =new TextView(this);
  8. label.setText("Leaks are bad");
  9.  
  10. if(sBackground ==null){
  11. sBackground = getDrawable(R.drawable.large_bitmap);
  12. }
  13. label.setBackgroundDrawable(sBackground);
  14.  
  15. setContentView(label);
  16. }

有问题吗?

哈哈,先Hold住一下,先来说一下android各版本发布的历史:

  1. /*
  2. 2.2 2010-3-20,Froyo
  3. 2.3 2010-12-6, Gingerbread
  4. 3.0 2011-2-22, Honeycomb
  5. 4.0 2011-10-11 Ice Cream Sandwich
  6. */

了解源码的历史,是很有益于我们分析android代码的。

好,开始分析代码。

首先,查看setBackgroundDrawable(Drawable background)方法源码里面有一行代码引起我们的注意:

  1. public void setBackgroundDrawable(Drawable background) {
  2. // ... ...
  3. background.setCallback(this);
  4. // ... ...
  5. }

所以sBackground对view保持了一个引用,view对activity保持了一个引用。

当退出当前Activity时,当前Activity本该释放,但是因为sBackground是静态变量,它的生命周期并没有结束,而sBackground间接保持对Activity的引用,导致当前Activity对象不能被释放,进而导致内存泄露。

所以结论是:有内存泄露!

这是Android官方文档的例子:http://android-developers.blogspot.com/2009/01/avoiding-memory-leaks.html

到此结束了吗?

我发现网上太多直接抄或者间接抄这篇文章,一搜一大片,并且吸引了大量的Android初学者不断的转载学习。

但是经过本人深入分析Drawable源码,事情发生了一些变化。

Android官方文档的这篇文章是写于2009年1月的,当时的Android Source至少是Froyo之前的。

Froyo的Drawable的setCallback()方法的实现是这样的:

  1. public final void setCallback(Callback cb) {
  2. mCallback = cb;
  3. }

在GingerBread的代码还是如此的。

但是当进入HoneyComb,也就是3.0之后的代码我们发现Drawable的setCallback()方法的实现变成了:

  1. public final void setCallback(Callback cb) {
  2. mCallback = new WeakReference<Callback>(cb);
  3. }

也就是说3.0之后,Drawable使用了软引用,把这个泄露的例子问题修复了。(至于软引用怎么解决了以后有机会再分析吧)

所以最终结论是,在android3.0之前是有内存泄露,在3.0之后无内存泄露!

如果认真比较代码的话,Android3.0前后的代码改进了大量类似代码,前面的Cursor篇里的例子也是在3.0之后修复了。

从这个例子中,我们很好的发现了内存是怎么通过回调泄露的,同时通过官方代码的update也了解到了怎么修复类似的内存泄露。

2. System Service对象

通过各种系统服务,我们能够做一些系统设计好的底层功能:

  1. //ContextImpl.java
  2. @Override
  3. public Object getSystemService(String name) {
  4. ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);
  5. return fetcher == null ? null : fetcher.getService(this);
  6. }
  7.  
  8. static {
  9. registerService(ACCESSIBILITY_SERVICE, new ServiceFetcher() {
  10. public Object getService(ContextImpl ctx) {
  11. return AccessibilityManager.getInstance(ctx);
  12. }});
  13.  
  14. registerService(CAPTIONING_SERVICE, new ServiceFetcher() {
  15. public Object getService(ContextImpl ctx) {
  16. return new CaptioningManager(ctx);
  17. }});
  18.  
  19. registerService(ACCOUNT_SERVICE, new ServiceFetcher() {
  20. public Object createService(ContextImpl ctx) {
  21. IBinder b = ServiceManager.getService(ACCOUNT_SERVICE);
  22. IAccountManager service = IAccountManager.Stub.asInterface(b);
  23. return new AccountManager(ctx, service);
  24. }});
  25. // ... ...
  26. }

这些其实就是定义在Context里的,按理说这些都是系统的服务,应该都没问题,但是代码到了各家厂商一改,事情发生了一些变化。

一些厂商定义的服务,或者厂商自己修改了一些新的代码导致系统服务引用了Context对象不能及时释放,我曾经碰到过Wifi,Storage服务都有内存泄露。

我们改不了这些系统级应用,我们只能修改自己的应用。

解决方案就是:使用ApplicationContext代替Context。

举个例子吧:

  1. // For example
  2. mStorageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
  3. 改成:
  4. mStorageManager = (StorageManager) getApplicationContext().getSystemService(Context.STORAGE_SERVICE);

3. Handler对象

先看一段代码:

  1. public class MainActivity extends QActivity {
  2. // lint tip: This Handler class should be static or leaks might occur
  3. class MyHandler extends Handler {
  4. ... ...
  5. }
  6. }

Handler泄露的关键点有两个:

1). 内部类

2). 生命周期和Activity不一定一致

第一点,Handler使用的比较多,经常需要在Activity中创建内部类,所以这种场景还是很多的。

内部类持有外部类Activity的引用,当Handler对象有Message在排队,则无法释放,进而导致Activity对象不能释放。

    如果是声明为static,则该内部类不持有外部Acitivity的引用,则不会阻塞Activity对象的释放。

    如果声明为static后,可在其内部声明一个弱引用(WeakReference)引用外部类。

  1. public class MainActivity extends Activity {
  2. private CustomHandler mHandler;
  3.  
  4. @Override
  5. protected void onCreate(Bundle savedInstanceState) {
  6. super.onCreate(savedInstanceState);
  7. mHandler = new CustomHandler(this);
  8. }
  9.  
  10. static class CustomHandlerextends Handler {
  11. // 内部声明一个弱引用,引用外部类
  12. private WeakReference<MainActivity > activityWeakReference;
  13. public MyHandler(MyActivity activity) {
  14. activityWeakReference= new WeakReference<MainActivity >(activity);
  15. }
  16. // ... ...
  17. }
  18. }

第二点,其实不单指内部类,而是所有Handler对象,如何解决上面说的Handler对象有Message在排队,而不阻塞Activity对象释放?

解决方案也很简单,在Activity onStop或者onDestroy的时候,取消掉该Handler对象的Message和Runnable。

通过查看Handler的API,它有几个方法:removeCallbacks(Runnable r)和removeMessages(int what)等。

  1. // 一切都是为了不要让mHandler拖泥带水
  2. @Override
  3. public void onDestroy() {
  4. mHandler.removeMessages(MESSAGE_1);
  5. mHandler.removeMessages(MESSAGE_2);
  6. mHandler.removeMessages(MESSAGE_3);
  7. mHandler.removeMessages(MESSAGE_4);
  8.  
  9. // ... ...
  10.  
  11. mHandler.removeCallbacks(mRunnable);
  12.  
  13. // ... ...
  14. }

上面的代码太长?好吧,出大招:

  1. @Override
  2. public void onDestroy() {
  3. // If null, all callbacks and messages will be removed.
  4. mHandler.removeCallbacksAndMessages(null);
  5. }

有人会问,当Activity退出的时候,我还有好多事情要做,怎么办?我想一定有办法的,比如用Service等等.

4. Thread对象

同Handler对象可能造成内存泄露的原理一样,Thread的生命周期不一定是和Activity生命周期一致。

而且因为Thread主要面向多任务,往往会造成大量的Thread实例。

据此,Thread对象有2个需要注意的泄漏点:

1). 创建过多的Thread对象

2). Thread对象在Activity退出后依然在后台执行

解决方案是:

1). 使用ThreadPoolExecutor,在同时做很多异步事件的时候是很常用的,这个不细说。

2). 当Activity退出的时候,退出Thread

第一点,例子太多,建议大家参考一下afinal中AsyncTask的实现学习。

第二点,如何正常退出Thread,我在之前的博文中也提到过。示例代码如下:

  1. // ref http://docs.oracle.com/javase/1.5.0/docs/guide/misc/threadPrimitiveDeprecation.html
  2. private volatile Thread blinker;
  3.  
  4. public void stop() {
  5. blinker = null;
  6. }
  7.  
  8. public void run() {
  9. Thread thisThread = Thread.currentThread();
  10. while (blinker == thisThread) {
  11. try {
  12. thisThread.sleep(interval);
  13. } catch (InterruptedException e){
  14. }
  15. repaint();
  16. }
  17. }

有人会问,当Activity退出的时候,我还有好多事情要做,怎么办?请看上面Handler的分析最后一行。

(未完待续)

[Android Memory] App调试内存泄露之Context篇(上)的更多相关文章

  1. [Android Memory] App调试内存泄露之Context篇(下)

    转载地址:http://www.cnblogs.com/qianxudetianxia/p/3655475.html 5. AsyncTask对象 我N年前去盛大面过一次试,当时面试官极力推荐我使用A ...

  2. Android学习系列(36)--App调试内存泄露之Context篇(上)

    Context作为最基本的上下文,承载着Activity,Service等最基本组件.当有对象引用到Activity,并不能被回收释放,必将造成大范围的对象无法被回收释放,进而造成内存泄漏. 下面针对 ...

  3. Android学习系列(37)--App调试内存泄露之Context篇(下)

    接着<Android学习系列(36)--App调试内存泄露之Context篇(上)>继续分析. 5. AsyncTask对象 我N年前去盛大面过一次试,当时面试官极力推荐我使用AsyncT ...

  4. Instruments指南:如何调试内存泄露

    Instruments指南:如何调试内存泄露 开篇 现在,你应该使用的ARC,而不是原来我们使用的MRC或者其他.但是我们在使用ARC的时候也会出现内存泄露的情况. 幸运的是,苹果为我们提供了Inst ...

  5. Android handler 可能会造成内存泄露

    Android handler 可能会造成内存泄露 Android Studio 使用 Handler 时: private Handler handler = new Handler(){ @Ove ...

  6. Android使用Handler造成内存泄露的分析及解决方法

    一.什么是内存泄露? Java使用有向图机制,通过GC自动检查内存中的对象(什么时候检查由虚拟机决定),如果GC发现一个或一组对象为不可到达状态,则将该对象从内存中回收.也就是说,一个对象不被任何引用 ...

  7. Android内存优化7 内存检测工具1 Memory Monitor检测内存泄露

    上篇说了一些性能优化的理论部分,主要是回顾一下,有了理论,小平同志又讲了,实践是检验真理的唯一标准,对于内存泄露的问题,现在通过Android Studio自带工具Memory Monitor 检测出 ...

  8. Android性能优化第(二)篇---Memory Monitor检测内存泄露

    上篇说了一些性能优化的理论部分,主要是回顾一下,有了理论,小平同志又讲了,实践是检验真理的唯一标准,对于内存泄露的问题,现在通过Android Studio自带工具Memory Monitor 检测出 ...

  9. Android Studio 使用Memory Monitor进行内存泄露分析

    在使用Android Studio进行内存泄露分析之前,我们先回顾一下Java相关的内存管理机制,然后再讲述一下内存分析工具如何使用. 一.Java内存管理机制 1. Java内存分配策略 Java ...

随机推荐

  1. BZOJ1046 [HAOI2007]上升序列

    Description 对于一个给定的S={a1,a2,a3,…,an},若有P={ax1,ax2,ax3,…,axm},满足(x1 < x2 < … < xm)且( ax1 < ...

  2. Model1模式的学生信息增删改查

    Student.java package entity; public class Student { private int stuid; private String stuname; priva ...

  3. linux中防CC攻击两种实现方法(转)

    CC攻击就是说攻击者利用服务器或代理服务器指向被攻击的主机,然后模仿DDOS,和伪装方法网站,这种CC主要是用来攻击页面的,导致系统性能用完而主机挂掉了,下面我们来看linux中防CC攻击方法. 什么 ...

  4. 《驾驭Core Data》 第三章 数据建模

    本文由海水的味道编译整理,请勿转载,请勿用于商业用途.    当前版本号:0.1.2 第三章数据建模 Core Data栈配置好之后,接下来的工作就是设计对象图,在Core Data框架中,对象图被表 ...

  5. redis.conf

    redis示例配置文件 分类: redis2013-10-22 16:39 427人阅读 评论(0) 收藏 举报 转载自https://raw.github.com/antirez/redis/2.6 ...

  6. 检测端口状态的python脚本

    #!/usr/bin/env python import os,subprocess,socket,time,sys from urllib import urlencode from socket ...

  7. 两款CSS3样式可视化在线生成工具

    CSS3随着浏览器的升级已经被越来越广泛的运用,合理的运用CSS3可以使你的网站更加美观,并且之前只能用js才能实现的效果也已经可以直接用 CSS3来实现.但是虽然如此,很多浏览器对CSS3的支持还都 ...

  8. Windows 下安装项目管理工具 Redmine 1.1.2

    1.InstantRails-2.0-win 下载地址  https://rubyforge.org/frs/?group_id=904 2.redmine1.1.2 下载地址  http://www ...

  9. Chrome中的Device模块调式响应性设计

    Chrome中的Device模块调式响应性设计 阅读目录 启用Device模块 Device模块设置介绍 自定义预设介绍 查看media queries 触发触摸事件 回到顶部 启用Device模块 ...

  10. Spring+Quartz实现定时任务的配置方法

    1.Scheduler的配置 <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean" ...