Android App 内存泄露之Thread

Thread 内存泄露

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

1.看一下以下是否存在问题

 <span style="white-space:pre">	</span>/**
	 * 
	 * @version 1.0.0 
	 * @author Abay Zhuang <br/>
	 *		   Create at 2014-7-17
	 */
	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(){
		
		}
	}




这段代码非常寻常也非常easy,是我们常常使用的形式。


真的没有问题吗

我们思考一个问题:如果MyThread的run函数是一个非常费时的操作,当我们开启该线程后,将设备的横屏变为了竖屏。
普通情况下当屏幕转换时会又一次创建Activity,依照我们的想法。老的Activity应该会被销毁才对,然而其实并不是如此。

因为我们的线程是Activity的内部类。所以MyThread中保存了Activity的一个引用,当MyThread的run函数没有结束时, MyThread是不会被销毁的。因此它所引用的老的Activity也不会被销毁。因此就出现了内存泄露的问题。

2.这样的线程导致的内存泄露问题应该怎样解决呢?

  • 第一、将线程的内部类,改为静态内部类。
  • 第二、在线程内部採用弱引用保存Context引用。 代码例如以下:

    <span style="white-space:pre">		</span>/**
    		 * 
    		 * @version 1.0.0
    		 * @author Abay Zhuang <br/>
    		 *         Create at 2014-7-17
    		 */
    		
    		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. 静态内部类:切断Activity 对于 MyThread的强引用。
  2. 弱引用: 切断MyThread对于Activity 的强引用。

3.AsynTask 内部类会怎样呢?

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

代码例如以下:

/**
 * 
 * 弱引用
 * @version 1.0.0 
 * @author Abay Zhuang <br/>
 *         Create at 2014-7-17
 */
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
}
}


posted on 2017-06-27 08:10  slgkaifa  阅读(202)  评论(0编辑  收藏  举报

导航