Android的Handler

1. Handler是用于线程间通信,本质上是:

Handler调用发送方法,向与Looper绑定的消息队列写入消息,然后Looper.loop()会循环的从消息队列里拿出消息。并调用dispatchMessage处理消息。而需要此消息的线程会实现回调的handleMessage接口来处理消息。

2.举个例子:主线程调用子线程的Handler发送消息。

cpp 复制代码
package com.android.car.myapplication;


import static java.lang.Thread.currentThread;
import static java.lang.Thread.sleep;

import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.widget.TextView;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    private TextView textView;
    private Handler mainHandler;
    private Handler backgroundHandler;
    private Thread backgroundThread;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_carsetting);

        textView = findViewById(R.id.textView);

        // 主线程的 Handler,用于更新 UI
        mainHandler = new Handler(Looper.getMainLooper());

        // 创建子线程并启动它
        backgroundThread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("childThread prepare");
                Looper.prepare();

                // 在子线程中创建一个 Handler
                // 充当其他线程调用 backgroundHandler.sendMessage/backgroundHandler.post的接收端
                backgroundHandler = new Handler(Looper.myLooper()) {
                    @Override
                    public void handleMessage(Message msg) {
                        if (msg.what == 1) {
                            System.out.println("childThread receive Msg 1");
                        }
                    }
                };

                System.out.println("childThread Looping");
                Looper.loop();
            }
        });

        backgroundThread.start();

        // 等待线程跑起来
        try {
            sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        Message message = backgroundHandler.obtainMessage(1);
        backgroundHandler.sendMessage(message);
        System.out.println("MainThread: sendMsg 1");
    }
    
}
  • 日志
bash 复制代码
2024-11-07 16:25:54.908 12880-12902 System.out       com...myapplication  I  childThread prepare
2024-11-07 16:25:54.908 12880-12902 System.out       com...myapplication  I  childThread Looping
2024-11-07 16:25:56.910 12880-12880 System.out       com...myapplication  I  MainThread: sendMsg 1
2024-11-07 16:25:56.912 12880-12902 System.out       com...myapplication  I  childThread receive Msg 1

3. Handler使用的源码解析

  • 3.0 调用端使用sendMessage来发送消息
cpp 复制代码
Message message = backgroundHandler.obtainMessage(1);
backgroundHandler.sendMessage(message);

追sendMessage的调用栈,最终会调用到Handler.java的enqueueMessage函数

cpp 复制代码
    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;   // 
。。。。。
        return queue.enqueueMessage(msg, uptimeMillis);
    }

1)msg.target指定了this,即当前的Handler, 对应3.3中取出消息的地方msg.target为handler, 且这个Handler是backgroundHandler。

2)enqueueMessage将msg放入了消息队列。

  • 3.1 Looper.prepare();
cpp 复制代码
    public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }
  • 3.2 Handler backgroundHandler = new Handler(Looper.myLooper())
cpp 复制代码
    public Handler(@NonNull Looper looper) {
        this(looper, null, false);
    }



    @UnsupportedAppUsage
    public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
        this(looper, callback, async, /* shared= */ false);
    }


    public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async,
            boolean shared) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
        mIsShared = shared;
    }
  • 3.3 Looper.loop(); 关键是loopOnce()从消息队列中循环拿消息,

假设现在调用了3.0中的sendMessage方法,此时消息队列中已经有了消息。

那么调用me.mQueue.next();即可拿到这条message.

再调用dispatchMessage处理消息。

cpp 复制代码
private static boolean loopOnce(final Looper me,
            final long ident, final int thresholdOverride) {
        Message msg = me.mQueue.next(); // might block
。。。。
        msg.target.dispatchMessage(msg);

。。。。。
        msg.recycleUnchecked();
。。。。。
        return true;
    }

3.0 讲过了msg.target是backgroundHandler,再往下查看dispatchMessage的实现:

cpp 复制代码
    public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);  // 调用者overide 实现的 handleMessage函数
        }
    }

会发现其中的handleMessage是在 2.当中实现的handleMessage方法。

4. nativePollOnce(ptr, nextPollTimeoutMillis);

nativePollOnce, 就是linux中的poll函数,可以让出线程对cpu的占用。

没有消息则线程进入空闲状态,不用一直调用来浪费cpu, 这种等待是非阻塞的。

5.总结:

在子线程中:

  • Looper.prepare()初始化了MessageQueue,与当前线程绑定。
  • Handler通过传入当前的Looper实现与Loop绑定,
  • Handler通过override实现了handleMessage。
  • 再通过Looper.loop()开启死循环来处理消息。

其他线程:

  • 调用子线程的Handler的post/sendMessage方法,来向目标线程的MessageQueue写入消息。

子线程:

  • 子线程调用Loop.loop()拿到消息,并调用dispatchMessage处理消息。
  • 在调用子线程override的handleMessage方法来处理来自其他线程的消息。
相关推荐
Vincent(朱志强)12 分钟前
设计模式详解(十二):单例模式——Singleton
android·单例模式·设计模式
mmsx44 分钟前
android 登录界面编写
android·登录界面
姜毛毛-JYM44 分钟前
【JetPack】Navigation知识点总结
android
花生糖@2 小时前
Android XR 应用程序开发 | 从 Unity 6 开发准备到应用程序构建的步骤
android·unity·xr·android xr
是程序喵呀2 小时前
MySQL备份
android·mysql·adb
casual_clover2 小时前
Android 之 List 简述
android·list
锋风Fengfeng3 小时前
安卓15预置第三方apk时签名报错问题解决
android
User_undefined4 小时前
uniapp Native.js原生arr插件服务发送广播到uniapp页面中
android·javascript·uni-app
程序员厉飞雨5 小时前
Android R8 耗时优化
android·java·前端
丘狸尾6 小时前
[cisco 模拟器] ftp服务器配置
android·运维·服务器