一、内存泄露分析
内部类会有一个指向外部类的引用。
垃圾回收机制中约定,当内存中的一个对象的引用计数为0时,将会被回收。
Handler 作为 Android 上的异步消息处理机制(好吧,我大多用来进行 worker thread 与 UI 线程同步),它的工作是需要 Looper 和 MessageQueue 配合的。简单的说,要维护一个循环体(Looper)处理消息队列(MessageQueue)。每循环一次就从 MessageQueue 中取出一个 Message,然后回调相应的消息处理函数。
如果,我是说如果,循环体中有消息未处理(Message 排队中),那么 Handler 会一直存在,那么 Handler 的外部类(通常是 Activity)的引用计数一直不会是0,所以那个外部类就不能被垃圾回收。很多人会遇到 activity的onDestroy方法一直不执行就是这个原因。
一旦 Handler 被声明为内部类,那么可能导致它的外部类不能够被垃圾回收。如果 Handler 是在其他线程(我们通常成为 worker thread)使用 Looper 或 MessageQueue(消息队列),而不是 main 线程(UI 线程),那么就没有这个问题。如果 Handler 使用 Looper 或 MessageQueue 在主线程(main thread),你需要对 Handler 的声明做如下修改:
声明 Handler 为 static 类;在外部类中实例化一个外部类的 WeakReference(弱引用)并且在 Handler 初始化时传入这个对象给你的 Handler;将所有引用的外部类成员使用 WeakReference 对象。
二、解决方案
解决方法(一)
声明 Handler 为 static 类;在外部类中实例化一个外部类的 WeakReference(弱引用)并且在 Handler 初始化时传入这个对象给你的 Handler;将所有引用的外部类成员使用 WeakReference 对象。
public class MethodSolveOne extends AppCompatActivity {
private static final String TAG = "MethodSolveOne";
private CopyFileHandler mHandler;
private TextView mDisplayTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.method_solve_one);
mDisplayTextView = (TextView)findViewById(R.id.display_text_view);
mHandler = new CopyFileHandler(this);
startCopyFileThread();
}
private void startCopyFileThread() {
Log.d(TAG, "startCopyFileThread");
new Thread(new Runnable() {
@Override
public void run() {
//DO SOMETHING LIKE: copyDBFile();
Message msg = mHandler.obtainMessage();
msg.what = 1;
mHandler.sendMessage(msg);
}
}).start();
}
private void changeDisplayTextView(String str){
mDisplayTextView.setText(str);
}
private static class CopyFileHandler extends Handler {
WeakReference<MethodSolveOne> mActivity;
public CopyFileHandler(MethodSolveOne activity) {
mActivity = new WeakReference<>(activity);
}
public void handleMessage(Message msg) {
final MethodSolveOne activity = mActivity.get();
switch (msg.what){
case 1:
activity.changeDisplayTextView("I have changed!!!");
}
}
}
}
解决方法(二)
在worker thread 中使用 Looper 或 MessageQueue
private Handler testHandler;
private Thread mThread = new Thread() {
public void run() {
Log.d(TAG, "mThread run");
Looper.prepare();
testHandler = new Handler() {
public void handleMessage(Message msg) {
Log.d("TAG", "worker thread:" + Thread.currentThread().getName());
switch (msg.what) {
//handle message here
}
}
};
Looper.loop();
}
};
//start thread here
if(Thread.State.NEW==mThread.getState())
{
Log.d(TAG, "mThread name: " + mThread.getName());
mThread.start();
}
//send message here
testHandler.sendEmptyMessage(1);