Skip to content
Go back

理解 Android 中 Handler、Looper 与 MessageQueue 的协作

在 Android 开发中,经常需要在网络请求或者耗时任务完成后更新页面。但如果直接在子线程修改 UI,就会遇到线程相关的异常。

一开始我只是记住了“用 Handler 切回主线程”这条规则,后来发现如果不理解它背后的工作方式,很容易在延迟任务、页面销毁和内存泄漏这些问题上继续踩坑。

为什么 UI 要在主线程更新

Android 的界面系统并不是线程安全的。页面的测量、布局、绘制以及用户输入事件,通常都在主线程中依次处理。如果多个线程同时修改 View,状态就可能变得不可预测。

因此,后台任务可以在子线程执行,但最终影响页面的操作需要交给主线程处理:

private Handler mHandler = new Handler(Looper.getMainLooper());private void loadData() {    new Thread(new Runnable() {        @Override        public void run() {            final String result = requestData();            mHandler.post(new Runnable() {                @Override                public void run() {                    mTextView.setText(result);                }            });        }    }).start();}

这里并不是 Handler 自己启动了一个线程,而是它把一段待执行的任务投递到了主线程对应的消息队列中。

三个对象如何协作

可以把整个过程简单理解为:

  1. MessageQueue 保存还没有被处理的消息和任务。
  2. Looper 不断从队列中取出下一条消息。
  3. Handler 负责发送消息,也负责在消息被取出后执行对应处理逻辑。

主线程启动时,系统已经为它准备好了 Looper。因此我们可以通过 Looper.getMainLooper() 创建一个明确关联到主线程的 Handler

Handler handler = new Handler(Looper.getMainLooper()) {    @Override    public void handleMessage(Message msg) {        if (msg.what == 1) {            mTextView.setText((String) msg.obj);        }    }};Message message = Message.obtain();message.what = 1;message.obj = "加载完成";handler.sendMessage(message);

如果只是执行一段代码,post(Runnable) 往往更简单;如果需要用类型区分多种任务,Message.what 会比较直观。

延迟任务为什么可能造成泄漏

下面这种写法很常见:

private Handler mHandler = new Handler() {    @Override    public void handleMessage(Message msg) {        mTextView.setText("更新完成");    }};

匿名内部类会持有外部 Activity 的引用。如果消息被延迟很久才处理,而用户在此之前已经退出页面,消息队列仍可能通过 Handler 间接持有这个 Activity,使它暂时无法被回收。

一种比较稳妥的方式是使用静态内部类,并通过 WeakReference 引用页面:

private static class PageHandler extends Handler {    private WeakReference<MainActivity> mActivityRef;    PageHandler(MainActivity activity) {        super(Looper.getMainLooper());        mActivityRef = new WeakReference<>(activity);    }    @Override    public void handleMessage(Message msg) {        MainActivity activity = mActivityRef.get();        if (activity == null) {            return;        }        activity.mTextView.setText("更新完成");    }}private final PageHandler mHandler = new PageHandler(this);

除此之外,在页面退出时移除不再需要的任务也很重要:

@Overrideprotected void onDestroy() {    super.onDestroy();    mHandler.removeCallbacksAndMessages(null);}

Handler 不应该承担所有异步逻辑

Handler 很适合做线程之间的消息切换和短时间延迟任务,但它并不适合替代完整的任务管理机制。

例如,页面已经退出时,网络请求是否要取消;旋转屏幕后任务是否要继续;数据是否应该缓存下来,这些问题不是简单发一条消息就能解决的。Handler 只负责消息投递,任务生命周期仍需要单独考虑。

小结

理解 Handler,重点不在于背诵几个 API,而是建立一个基本认识:主线程通过消息队列顺序处理界面任务,我们通过 Handler 把工作交还给这条线程。

使用时要注意两点:后台任务不要直接触碰 UI;页面退出后不要让无意义的延迟消息继续持有页面。处理好这两个问题,很多常见的线程和内存问题都会更容易定位。


Share this post on: