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()。
参考文章子线程中:new Handler需要做哪些准备?消息队列中无消息的时候,Looper的处理方案是什么?