Android随笔-神经系统Handler

一、Handler 的作用

作用 说明
线程间通信 子线程发送消息到主线程,更新 UI
定时/延时执行 延迟发送消息或定时重复任务
任务调度 将任务投递到指定线程的 Looper 中排队执行

二、核心原理

四组件协作

组件 职责
Handler 发送消息(sendMessage/post)、处理消息(handleMessage/Runnable
Message 承载数据(whatarg1arg2obj)、目标 Handler(target)、回调(callback
MessageQueue 按执行时间排序的链表队列,管理消息的入队/出队
Looper 通过 ThreadLocal 绑定线程,无限循环从队列取消息分发

关键源码

Looper.prepare() --- 创建并绑定线程

java 复制代码
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<>();

public static void prepare() {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

Looper.loop() --- 无限循环

java 复制代码
public static void loop() {
    final Looper me = myLooper();
    final MessageQueue queue = me.mQueue;
    
    for (;;) {  // 死循环
        Message msg = queue.next();  // 阻塞等待(epoll_wait)
        if (msg == null) return;
        
        msg.target.dispatchMessage(msg);  // 分发到 Handler
        msg.recycleUnchecked();            // 回收复用
    }
}

MessageQueue.next() --- 阻塞与唤醒

java 复制代码
Message next() {
    for (;;) {
        nativePollOnce(ptr, nextPollTimeoutMillis);  // epoll 阻塞,线程休眠
        
        synchronized (this) {
            Message msg = mMessages;
            if (msg != null) {
                if (now < msg.when) {
                    nextPollTimeoutMillis = (int)(msg.when - now);  // 计算等待时间
                } else {
                    mMessages = msg.next;  // 取出消息
                    return msg;
                }
            }
        }
    }
}

Handler.sendMessage() --- 入队并唤醒

java 复制代码
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    msg.target = this;  // 标记目标 Handler
    return queue.enqueueMessage(msg, uptimeMillis);
}

// MessageQueue.enqueueMessage
boolean enqueueMessage(Message msg, long when) {
    synchronized (this) {
        // 按时间插入链表
        // ...
        if (needWake) {
            nativeWake(mPtr);  // 写入 eventfd,唤醒 epoll_wait
        }
    }
}

Handler.dispatchMessage() --- 三种回调优先级

java 复制代码
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {       // 优先级1:post(Runnable) 的 callback
        handleCallback(msg);
    } else {
        if (mCallback != null) {     // 优先级2:构造时的 Callback 接口
            if (mCallback.handleMessage(msg)) return;
        }
        handleMessage(msg);            // 优先级3:子类重写的方法
    }
}

三、典型应用

1. 子线程更新 UI

java 复制代码
new Thread(() -> {
    final String result = loadData();
    new Handler(Looper.getMainLooper()).post(() -> {
        textView.setText(result);
    });
}).start();

2. 延时/定时任务

java 复制代码
Handler handler = new Handler(Looper.getMainLooper());

// 延时 3 秒
handler.postDelayed(() -> hideLoading(), 3000);

// 定时轮询
Runnable task = new Runnable() {
    public void run() {
        refresh();
        handler.postDelayed(this, 5000);
    }
};
handler.postDelayed(task, 5000);

3. HandlerThread --- 自带 Looper 的工作线程

java 复制代码
HandlerThread thread = new HandlerThread("Worker");
thread.start();

Handler worker = new Handler(thread.getLooper()) {
    @Override
    public void handleMessage(Message msg) {
        // 在 Worker 线程执行耗时操作
    }
};
worker.sendEmptyMessage(1);

4. 系统框架中的 Handler

场景 作用
ActivityThread 主线程消息循环,处理四大组件生命周期回调
ViewRootImpl 通过 Choreographer 调度 UI 绘制(measure/layout/draw)
Choreographer 接收 VSYNC 信号,同步屏障保障绘制优先
Binder 线程 ApplicationThread 通过 Handler 将 IPC 回调切换到主线程

四、王者地位

Handler 在 Android 中的地位是 "线程通信的基石,消息驱动的灵魂 "。整个 Android 系统框架几乎建立在 Handler + Looper + MessageQueue 这套消息机制之上。

1. 系统框架中的核心地位

1.1 主线程的生命线

java 复制代码
// ActivityThread.java ------ App 启动入口
public static void main(String[] args) {
    Looper.prepareMainLooper();  // 创建主线程 Looper
    
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);
    
    // 获取主线程 Handler
    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();  // H 类 Handler
    }
    
    Looper.loop();  // 进入消息循环,永不退出
}

没有 Handler/Looper,主线程无法运行,App 直接崩溃。

1.2 四大组件的生命周期调度

所有 Activity、Service、BroadcastReceiver 的生命周期回调,都是通过 ActivityThread.H 这个 Handler 分发到主线程的:

java 复制代码
// ActivityThread.java 内部类 H
class H extends Handler {
    public static final int LAUNCH_ACTIVITY         = 100;
    public static final int PAUSE_ACTIVITY          = 101;
    public static final int STOP_ACTIVITY_SHOW      = 103;
    public static final int RESUME_ACTIVITY         = 107;
    public static final int DESTROY_ACTIVITY        = 109;
    public static final int BIND_APPLICATION        = 110;
    public static final int EXIT_APPLICATION        = 111;
    public static final int RECEIVER                = 113;
    public static final int CREATE_SERVICE          = 114;
    public static final int SERVICE_ARGS            = 115;
    public static final int STOP_SERVICE            = 116;
    // ... 更多

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case LAUNCH_ACTIVITY:
                handleLaunchActivity((ActivityClientRecord) msg.obj);
                break;
            case PAUSE_ACTIVITY:
                handlePauseActivity((IBinder) msg.obj);
                break;
            case RESUME_ACTIVITY:
                handleResumeActivity((IBinder) msg.obj);
                break;
            // ... 所有生命周期方法都在这里
        }
    }
}

Binder 线程收到 AMS 的 IPC 调用 → 封装成 Message → 通过 H Handler 切换到主线程执行。

1.3. View 的绘制与事件分发

java 复制代码
// ViewRootImpl.java
final ViewRootHandler mHandler = new ViewRootHandler();

// 申请 UI 绘制
void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        // 同步屏障:阻塞普通消息,保障绘制优先
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        
        // 通过 Choreographer 注册 VSYNC 回调
        mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
    }
}

// 执行 measure / layout / draw
final class TraversalRunnable implements Runnable {
    public void run() {
        doTraversal();
    }
}

没有 Handler,View 无法绘制,屏幕永远是黑的。

1.4. 输入事件的流转

java 复制代码
// Input 事件流程
InputDispatcher (Native, system_server)
    ↓ 通过 InputChannel (SocketPair)
InputEventReceiver (App, 子线程)
    ↓ 封装成 Message
NativeInputEventReceiver → ViewRootImpl.mHandler
    ↓ 切换到主线程
DecorView.dispatchTouchEvent() → Activity.dispatchTouchEvent()
    ↓
ViewGroup.dispatchTouchEvent() → 子 View

Input 事件从底层到应用层,最终通过 Handler 切换到主线程处理。

2. 与其他核心机制的关联

机制 Handler 的作用
Binder IPC ApplicationThread 通过 Handler 将 AMS 的 IPC 回调切换到主线程
Choreographer 基于 Handler + 同步屏障 + VSYNC 实现帧调度
AsyncTask 底层使用 InternalHandler 切换线程
LiveData 通过 Dispatchers.Main 或 Handler 确保主线程更新
RxJava AndroidSchedulers.mainThread() 内部使用 Handler
协程 (Kotlin) Dispatchers.Main 基于 Handler 实现
Jetpack 组件 ViewModelRoom 等异步回调通过 Handler 切回主线程

3. 地位

五、常见问题

1. 内存泄漏

原因: 非静态内部类持有外部 Activity 引用,延迟消息未执行时 Activity 无法回收。

java 复制代码
// 错误写法
private Handler mHandler = new Handler() {  // 匿名内部类持有 Activity
    public void handleMessage(Message msg) {
        textView.setText("...");  // 持有 Activity 引用链
    }
};
mHandler.sendEmptyMessageDelayed(1, 10 * 60 * 1000);  // 10分钟后执行

解决方案:

java 复制代码
// 静态内部类 + WeakReference
private static class SafeHandler extends Handler {
    private final WeakReference<Activity> mRef;
    
    SafeHandler(Activity activity) {
        mRef = new WeakReference<>(activity);
    }
    
    @Override
    public void handleMessage(Message msg) {
        Activity activity = mRef.get();
        if (activity == null || activity.isFinishing()) return;
        // 处理消息
    }
}

// 同时 onDestroy 中移除消息
@Override
protected void onDestroy() {
    super.onDestroy();
    mHandler.removeCallbacksAndMessages(null);
}

2. 子线程创建 Handler 崩溃

java 复制代码
// 错误:子线程默认没有 Looper
new Thread(() -> {
    new Handler();  // RuntimeException: No Looper
}).start();

// 正确:手动创建 Looper
new Thread(() -> {
    Looper.prepare();
    Handler handler = new Handler();
    Looper.loop();
}).start();

3. 主线程 loop() 是死循环,为什么没 ANR?

Looper.loop() 的 for(;😉 本身不会导致 ANR。ANR是因为消息处理耗时过长,阻塞了后续消息(如输入事件、绘制消息)。

java 复制代码
// 不会 ANR:loop 只是循环取消息
for (;;) {
    Message msg = queue.next();  // 无消息时 epoll 阻塞,线程休眠,不耗 CPU
    msg.target.dispatchMessage(msg);  // ANR 发生在这里:处理耗时
}

4. Message.obtain() vs new Message()

java 复制代码
// 推荐:从消息池复用,避免 GC
Message msg = Message.obtain();
msg.what = 1;
handler.sendMessage(msg);

// 不推荐:频繁创建对象
Message msg = new Message();  // 每次 new,增加 GC 压力

5. postDelayed 不准确

延迟时间是入队时间,不是精确的执行时间。如果消息队列前面有耗时消息,实际执行会延后。

6. 同步屏障(Sync Barrier)是什么?

系统框架内部机制,阻塞所有同步消息,只允许异步消息通过。用于保障 Choreographer 的 UI 绘制(CALLBACK_TRAVERSAL)优先执行,避免掉帧。应用层无法直接调用。

六、总结

  • Handler = 线程间邮差。把任务打包成 Message,投递到目标线程的 MessageQueue,由 Looper 按顺序取出,回调 Handler 执行。底层通过 ThreadLocal 实现线程隔离,通过 epoll + eventfd 实现阻塞与唤醒。

  • Handler 是 Android 的"神经系统"------没有它,主线程无法呼吸,组件无法存活,UI 无法绘制,App 就是一具空壳。

相关推荐
布朗克1681 小时前
Go 入门到精通-09-复合类型之Map
开发语言·后端·golang·map
zh_xuan1 小时前
c++ Expected用法
开发语言·c++·expected
ch.ju1 小时前
Java程序设计(第3版)第四章——final修饰的方法和类
java·开发语言
摇滚侠2 小时前
云原生 Java 架构师的第一课 K8s+Docker+KubeSphere+DevOps 26-40
java·云原生·kubernetes
极***技2 小时前
2026云原生核心!Go高并发限流实战,从原理到落地
开发语言·云原生·golang
牛大了20233 小时前
场景题:数据迁移解决方案
开发语言
认真的酒窝10 小时前
自己动手开发编译器(十一)语义分析
java·开发语言
wbs_scy12 小时前
Linux C++ 高并发编程:线程池全链路深度解析,从原理到手撕实现
java·开发语言
JAVA面经实录91712 小时前
Linux 常用命令完整知识体系
java·linux·开发语言·汇编