赞
踩
1、问题出现场景:
class MyHandler extends Handler { //msg的值为0 private static final int MSG_UPDATE = 0; @Override public void handleMessage(@NonNull Message msg) { super.handleMessage(msg); switch (msg.what) { case MSG_UPDATE: mActivityMainBinding.tvMessage.setText("update text!"); break; } } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mActivityMainBinding = ActivityMainBinding.inflate(getLayoutInflater()); View rootView = mActivityMainBinding.getRoot(); setContentView(rootView); mMyHandler = new MyHandler(); mMyHandler.postDelayed(new Runnable() { @Override public void run() { mActivityMainBinding.tvMessage.setText("Runnable text!"); } }, 2 * 1000); //msg移除 mMyHandler.removeMessages(MyHandler.MSG_UPDATE); }
上面代码中,mMyHandler
先post
一个Runnable
,2s后执行。后面又执行了removeMessages
操作,把what
为0的Message
移除掉了。2s后Runnable
的内容没有执行。
2、问题原因:
看下源码,postDelayed
方法执行:
public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
private final Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
再看Message.obtain()
:
public int what;
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
sPoolSize--;
return m;
}
}
return new Message();
}
可以看到,postDelayed(Runnable)
实际上也是创建一个Message
,从代码中看到,该Message
要不就是从sPool
池中获取,要不就是new
出来的,如果new
出来的,其成员变量自然被初始化为0。
如果从sPool
中获取的话,sPool
中的Message
都是经过recycle()
之后才能使用的,我们看源码:
public void recycle() { ... clearForRecycle(); } void clearForRecycle() { flags = 0; what = 0; arg1 = 0; arg2 = 0; obj = null; replyTo = null; when = 0; target = null; callback = null; data = null; synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { next = sPool; sPool = this; sPoolSize++; } } }
设置的也是0,所以移除的时候就把Runnable
所在的Message
给移除掉了,所以不会执行。不管是post
,还是postDelayed
都有这个问题。
所以在定义Handler中的what
值的时候,不要从0开始。
参考文章:
[原创]Android Handler使用Message的一个注意事项
1、问题出现场景:
private Handler mThreadHandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ... new Thread(new Runnable() { @Override public void run() { if (mThreadHandler == null){ mThreadHandler= new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); Log.i(TAG,"handle Message"); } }; } } }).start(); } public void sendMsg() { if (mThreadHandler != null){ mThreadHandler.sendMessage(mThreadHandler.obtainMessage()); } }
2、问题原因:
原因很简单,在子线程中新建Handler
,要想让消息机制跑起来,需要先调用Looper.prepare()
,new
一个Handler
后,还需要调用Looper.loop()
方法。修改上面代码如下:
private Handler mThreadHandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new Thread(new Runnable() { @Override public void run() { if (mThreadHandler == null){ Looper.prepare(); mThreadHandler= new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); Log.i(TAG,"handle Message"); } }; Looper.loop(); } } }).start(); } public void sendMsg() { if (mThreadHandler != null){ mThreadHandler.sendMessage(mThreadHandler.obtainMessage()); } }
这个是之前老代码中的一个bug,因为在主线程中我们可以直接new
一个Handler
,这样写习惯了,可能会忽视这个点。
3、为什么主线程可以new Handler?
来看下源码,在ActivityThread.java
里有一个main()
函数,它是Android
每一个应用最早执行的函数。
public static void main(String[] args) { ...... Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } // End of event ActivityThreadMain. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); ...... }
来看下prepareMainLooper()
函数,初始化了一个Looper
。
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
后面执行的loop()
函数,for
循环不断的调用next()
函数,去轮询的MessageQueue
,拿取消息并执行。
public static void loop() {
final Looper me = myLooper();
...
for (;;) {
Message msg = queue.next(); // might block
}
}
从上面的源码能看到,主线程一启动,在main()
函数中,系统已经帮我们完成了Looper
机制的创建,我们主线程中的所有代码,全都运行在这两个函数(prepare()
和 loop()
)之间。
下面看下在主线程中new
一个Handler
的源码如下:
private Handler mMainHandler = new Handler(); //无参构造 public Handler() { this(null, false); } //构造函数中获取mLooper public Handler(@Nullable Callback callback, boolean async) { ... mLooper = Looper.myLooper(); ... } //获取当前线程的Looper public static @Nullable Looper myLooper() { return sThreadLocal.get(); }
在主线程new
一个Handler
的时候,默认使用的是当前线程的Looper,就是系统为我们创建好的MainLooper
。所以不需要自己在手动写prepare()
和 loop()
函数。
4、子线程中new Handler的完整用法
private Handler mThreadHandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new Thread(new Runnable() { @Override public void run() { if (mThreadHandler == null){ Looper.prepare(); mThreadHandler= new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); Log.i(TAG,"handle Message"); } }; Log.i(TAG,"*** loop() - UP"); Looper.loop(); Log.i(TAG,"*** loop() - DOWN"); } } }).start(); } public void sendMsg() { if (mThreadHandler != null){ mThreadHandler.sendMessage(mThreadHandler.obtainMessage()); } } public void stopLooper() { if (mThreadHandler != null){ mThreadHandler.getLooper().quit(); } }
如果只执行sendMsg()
后,log打印如下:
*** loop() - UP
handle Message
从打印可以看到,loop()后的log没有打印,因为现在Looper处于一直循环处理消息的状态,这就意味着这个Looper一直处于一个阻塞状态。
所以在使用完子线程退出的时候,还需要将Looper循环停掉,进行资源释放。Looper退出的时候,有两个函数:quit()
和quitsafely()
可调用,具体区别:(细节解析可以看这篇文章:Android Handler机制Looper的quit和quitSafely区别(3))
Looper.quit():调用后直接终止Looper,不在处理任何Message,所有尝试把Message放进消息队列的操作都会失败,比如Handler.sendMessage()会返回 false,但是存在不安全性,因为有可能有Message还在消息队列中没来的及处理就终止Looper了。
Looper.quitsafely():调用后会在所有消息都处理后再终止Looper,所有尝试把Message放进消息队列的操作也都会失败。
在回到上面的代码,执行完sendMsg()
后,再执行stopLooper()
函数,log打印如下:
*** loop() - UP
handle Message
handle Message
*** loop() - DOWN
总结下:如果在子线程中创建了一个Handler,那么就必须做三个操作: prepare()、 loop()、quit()。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。