为何Handler线程中RunOnUiThread直接访问Message.obj会为空?
问题分析:Handler中直接在runOnUiThread访问msg.obj为null的原因
这其实是Android Message对象的复用机制在搞鬼,我来给你拆解清楚:
核心原因:Message会被系统回收复用
Android为了减少对象创建的开销,会维护一个Message消息池。当handleMessage方法执行完毕后,系统会自动把当前的Message回收回池子里,同时清空它的obj、what、arg1等所有字段,等待下一次被复用。
你在不可行代码里的操作逻辑是:
handleMessage拿到msg,立刻把一个Runnable丢给UI线程执行- 但
runOnUiThread的Runnable是异步排队执行的——等UI线程真正执行这个Runnable时,原来的msg已经被回收复用了,它的obj早就被系统清空成null了
而可行代码里,你提前把msg.obj赋值给了一个final局部变量messageString,这个变量会牢牢持有原Data对象的引用,完全不受Message回收复用的影响,所以能正常拿到数据。
代码对比验证
可行代码的关键细节
bt01.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Message msg = new Message(); msg.obj = new Data("Hi im the message"); mHandler.sendMessage(msg); } }); class LooperThread extends Thread { public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(final Message msg) { // 提前把obj的引用保存到局部变量,脱离Message的生命周期 final Object messageString = msg.obj; runOnUiThread(new Runnable() { @Override public void run() { tv01.setText(((Data)messageString).getMessage()); } }); } }; Looper.loop(); } }
这里的messageString是独立于Message的局部变量,只要Runnable还没执行,它就会一直持有原Data对象的引用,不会被回收。
不可行代码的问题所在
public void handleMessage(final Message msg) { runOnUiThread(new Runnable() { @Override public void run() { // 此时msg已经被系统回收复用,obj字段早已被清空 tv01.setText(((Data)msg.obj).getMessage()); } }); }
handleMessage执行完就会释放msg的控制权,系统马上把它回收。等UI线程的Runnable开始执行时,msg已经是被重置过的对象了,obj自然为null。
额外的替代方案
除了提前保存obj的方法,你还可以:
- 改用
Handler.post(Runnable)直接传递带数据的任务,绕开Message的复用问题 - 使用
LiveData、Coroutine这类更现代的Android异步UI更新方案,从根源上避免直接操作Message对象
内容的提问来源于stack exchange,提问作者Logarith
相关产品推荐
相关产品推荐

