This Handler class should be static or leaks might occur,Handler和Context使用的注意事项!

简介:

Android中,在使用到Handler的时候,如果按如下代码编写:

	private Handler handler;
	
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		handler = new Handler() {

			@Override
			public void handleMessage(Message msg) {
				// TODO Auto-generated method stub
				super.handleMessage(msg);
			}

		};

		handler.sendMessageDelayed(Message.obtain(), 60000);

		// just finish this activity
		finish();
	}

然后运行Android Lint工具会有一个内存泄露警告:

This Handler class should be static or leaks might occur (com.example.ta.MainActivity.1)

Issue: Ensures that Handler classes do not hold on to a reference to an outer class
Id: HandlerLeak

In Android, Handler classes should be static or leaks might occur. Messages enqueued on the application thread’s MessageQueue also retain their target Handler. If the Handler is an inner class, its outer class will be retained as well. To avoid leaking the outer class, declare the Handler as a static nested class with a WeakReference to its outer class.

原因是:

  1. 当Android应用启动的时候,会先创建一个应用主线程的Looper对象,Looper实现了一个简单的消息队列,一个一个的处理里面的Message对象。主线程Looper对象在整个应用生命周期中存在。

  2. 当在主线程中初始化Handler时,该Handler和Looper的消息队列关联。发送到消息队列的Message会引用发送该消息的Handler对象,这样系统可以调用 Handler#handleMessage(Message) 来分发处理该消息。

  3. 在Java中,非静态(匿名)内部类会引用外部类对象。而静态内部类不会引用外部类对象。

  4. 如果外部类是Activity,则会引起Activity泄露 。

当Activity finish后,延时消息会继续存在主线程消息队列中1分钟,然后处理消息。而该消息引用了Activity的Handler对象,然后这个Handler又引用了这个Activity。这些引用对象会保持到该消息被处理完,这样就导致该Activity对象无法被回收,从而导致了上面说的 Activity泄露。

要修改该问题,只需要按照Lint提示的那样,把Handler类定义为静态即可,然后通过WeakReference 来保持外部的Activity对象。


	private static class WebViewHandler extends Handler{
		
		public static final int what_pullRefreshCompleted = 1;

		private final WeakReference<WebActivity> mActivity;
		
		public WebViewHandler(WebActivity mActivity) {
			super();
			this.mActivity = new WeakReference<WebActivity>(mActivity);
		}

		@Override
		public void handleMessage(Message msg) {
			if(mActivity.get() == null)
				return;
			switch(msg.what){
				case what_pullRefreshCompleted:
					if (mActivity.get().getmPullRefreshWebView() != null)//因为该Handler为static,所以我们通过调用方法来获取外部类属性
						mActivity.get().getmPullRefreshWebView().refreshComplete();
					break;
			}
			super.handleMessage(msg);
		}
	}

所以,在Activity中使用内部类的时候,需要时刻考虑您是否可以控制该内部类的生命周期,如果不可以,则最好定义为静态内部类。



另外,再附上一个注意事项知识点,希望对大家有用:

不要持有 Context 的静态引用!

public class ExampleActivity extends Activity {

	//...
	private static MyAccountInfo info;
	//...

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		//...
		info = new MyAccountInfo(this);
		//...
	}

}
上面这点代码,对于一个有丰富经验的人来,可能不会犯这样的错误。然后也会有很多人会犯这样的错误,一段看似没有问题的代码,实际上可能会给我们的应用带来麻烦。不要这样做,可能会出问题的。

如果 MyAccountInfo 通过它的构造函数保持一个指向Activity的引用,这个Activity将不会被垃圾回收(GC),除非静态变量被从新分配到不同的Activity。这是因为 info 是静态变量,而静态变量的内存是不会被回收,直到应用程序退出才回收。如果你正在试图做这样的事情,你的代码很有可能有严重的错误。根据实际逻辑需要换个写法吧!

注:从技术上说,你可以对一个Application Context进行静态变量引用而不引起内存泄露,但我不建议你这样做。







目录
相关文章
|
5月前
|
PHP
set_error_handler()
set_error_handler()
20 0
error : Class declarations lacks Q_OBJECT macro
error : Class declarations lacks Q_OBJECT macro
|
9月前
|
消息中间件 Java RocketMQ
【Java】Error creating bean with name ‘functionBindingRegistrar‘ defined in class path resource的一种解决方式
【Java】Error creating bean with name ‘functionBindingRegistrar‘ defined in class path resource的一种解决方式
51 0
|
10月前
|
Dart
Dart报The return type ‘bool‘ isn‘t a ‘void‘, as required by the closure‘s context
Dart报The return type ‘bool‘ isn‘t a ‘void‘, as required by the closure‘s context
|
Java Spring
【新手指南】严重: Exception sending context initialized event to listener instance of class
【新手指南】严重: Exception sending context initialized event to listener instance of class
320 0
【新手指南】严重: Exception sending context initialized event to listener instance of class
|
Java Android开发
The method call() of type XXX must override a superclass
The method call() of type XXX must override a superclass
75 0
|
前端开发
SpringMVC - Failed to instantiate Specified class is an interface
SpringMVC - Failed to instantiate Specified class is an interface
281 0
【Junit 报错】Test class should have exactly one public zero-argument constructor和Test class can only have one constructor
错误1: 1 java.lang.Exception: Test class should have exactly one public zero-argument constructor 2 at org.
6041 0
|
Java Apache Spring
Exception sending context initialized event to listener instance of class
详细错误信息如下: 严重: Exception sending context initialized event to listener instance of class com.auth.spring.
1187 0
|
Kotlin 容器
EventBus3.1.1 解决 Caused by: org.greenrobot.eventbus.EventBusException: Subscriber class ... and its super classes have no public methods with the @Sub
      小菜今天自己写测试 Demo 时,需要用到 EventBus,目前集成 3.1.1 版本,集成的方式很简单,在某个 Fragment 实践应用中,却一直报入下错: Caused by: org.
12517 0