Android中使用Thread造成内存泄露的分析和解决

Thread 内存泄露

线程也是造成内存泄露的一个重要的源头。线程产生内存泄露的主要原因在于线程生命周期的不可控。

看一下下面是否存在问题

    public class ThreadActivity extends Activity {
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            new MyThread().start();
        }

        private class MyThread extends Thread {
            @Override
            public void run() {
                super.run();
                dosomthing();
            }
        }
        private void dosomthing(){

        }
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

这段代码很平常也很简单,是我们经常使用的形式。


真的没有问题吗

我们思考一个问题:假设MyThread的run函数是一个很费时的操作,当我们开启该线程后,将设备的横屏变为了竖屏, 
一般情况下当屏幕转换时会重新创建Activity,按照我们的想法,老的Activity应该会被销毁才对,然而事实上并非如此。 
由于我们的线程是Activity的内部类,所以MyThread中保存了Activity的一个引用,当MyThread的run函数没有结束时, 
MyThread是不会被销毁的,因此它所引用的老的Activity也不会被销毁,因此就出现了内存泄露的问题。

这种线程导致的内存泄露问题应该如何解决呢?

  1. 将线程的内部类,改为静态内部类。
  2. 在线程内部采用弱引用保存Context引用。

代码如下:

public class ThreadAvoidActivity extends Activity {
            public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_main);
                new MyThread(this).start();
            }

            private void dosomthing() {

            }

            private static class MyThread extends Thread {
                WeakReference<ThreadAvoidActivity> mThreadActivityRef;

                public MyThread(ThreadAvoidActivity activity) {
                    mThreadActivityRef = new WeakReference<ThreadAvoidActivity>(
                            activity);
                }

                @Override
                public void run() {
                    super.run();
                    if (mThreadActivityRef == null)
                        return;
                    if (mThreadActivityRef.get() != null)
                        mThreadActivityRef.get().dosomthing();
                    // dosomthing
                }
            }
        }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

上面的两个步骤其实是切换两个对象的双向强引用链接 
静态内部类:切断Activity 对于 MyThread的强引用。 
弱引用: 切断MyThread对于Activity 的强引用。

AsynTask 内部类会如何呢?

有些人喜欢用Android提供的AsyncTask,但事实上AsyncTask的问题更加严重, 
Thread只有在run函数不结束时才出现这种内存泄露问题,然而AsyncTask内部的实现机制是运用了ThreadPoolExcutor, 
该类产生的Thread对象的生命周期是不确定的,是应用程序无法控制的, 
因此如果AsyncTask作为Activity的内部类,就更容易出现内存泄露的问题。

在看android联系人2.3源码的时候看到一个类WeakAsyncTask, 内部用到了WeakReference软引用, 这样可以解决内存泄露的问题; 其实给AysncTask加上static, 静态的内部类不会持有对外部类的引用, 就能够解决问题, 只不过这样如果AysncTask的方法用到的成员变量都需要加上static;

WeakReference和AsyncTask的美妙结合

为了避免开发者在UI线程上做耗时操作,Android提供了不少异步API,其中之一就是AsyncTask。而对于某些频繁操作数据库的应用(例如,Phonebook)而言,需要一种异步的并且低耗资源的(低耗是两个方面的事情,要么是你占有的多点,但是能快速释放;要么是你本身就占有的少。这两种都可以保证其它应用有资源可用)组件。所以,那就来个WeakAsyncTask吧,唔,美妙的产物,既保证占有资源的快速释放,又保证操作是异步进行。 
那为什么不是soft reference呢? 
这个,来看看weak和soft两者的区别:

* A SoftReference should be cleared and enqueued as late as possible, that is, in case the VM is in danger of running out of memory.
* A WeakReference may be cleared and enqueued as soon as is known to be weakly-referenced.

下面是WeakAsyncTask的源码:

public abstract class WeakAsyncTask<Params, Progress, Result, WeakTarget>
    extends AsyncTask<Params, Progress, Result> {
protected WeakReference<WeakTarget> mTarget;

public WeakAsyncTask(WeakTarget target) {
    mTarget = new WeakReference<WeakTarget>(target);
}

@Override
protected final void onPreExecute() {
    final WeakTarget target = mTarget.get();
    if (target != null) {
        this.onPreExecute(target);
    }
}

@Override
protected final Result doInBackground(Params... params) {
    final WeakTarget target = mTarget.get();
    if (target != null) {
        return this.doInBackground(target, params);
    } else {
        return null;
    }
}

@Override
protected final void onPostExecute(Result result) {
    final WeakTarget target = mTarget.get();
    if (target != null) {
        this.onPostExecute(target, result);
    }
}

protected void onPreExecute(WeakTarget target) {
    // Nodefaultaction
}

protected abstract Result doInBackground(WeakTarget target,
        Params... params);

protected void onPostExecute(WeakTarget target, Result result) {
    // Nodefaultaction
}
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

使用也很简单, 用法也几乎和AsyncTask一样:

private static class MyTask extends WeakAsyncTask<Void, Void, String, MainActivity> {
        public MyTask(MainActivity target) {
            super(target);
        }

        @Override
        protected String doInBackground(MainActivity target, Void... params) { // 获取context,
            // 执行一些操作
            Context context = target;
            return "Hello Android    !!!!!";
        }

        @Override
        protected void onPostExecute(MainActivity target, String s) {
            // 执行操作
        }
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

直接调用执行:

new MyTask(this).execute(); 
 
 
  • 1
  • 1

其实还可以根据这个思想写一个WeakHandler! 
Android Weak Handler:可以避免内存泄漏的Handler库 
http://blog.csdn.net/jdsjlzx/article/details/51388862

本文部分来自:http://blog.csdn.net/zhuanglonghai/article/details/37909553

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值