前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android点将台:烽火狼烟[-Handler-]

Android点将台:烽火狼烟[-Handler-]

作者头像
张风捷特烈
发布2024-02-11 08:29:42
980
发布2024-02-11 08:29:42
举报

前言

哥发誓,这是我最后一次分析Handler(这是我第三次说这句话,希望不会有下次了) 说起Handler新手的小噩梦,用起来不难,理解起来烦神

Handler总览
Handler.png
Handler.png

一、引入Handler的一般套路
1.说Handler一般套路是从一个异常开始

你以为我会这么俗气地从:非主线程禁止更新UI开始说Handler吗?--是的 这个异常定义在ViewRootImpl里,更新TextView的UI为什么ViewRootImpl报异常? 子不教,父之过,教不严,师之惰呗。在framework的源码里看一下吧,

非主线程禁止更新UI.png
非主线程禁止更新UI.png
代码语言:javascript
复制
---->[ViewRootImpl#checkThread]-------
void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
                "Only the original thread that created a view hierarchy can touch its views.");
    }
}
|--可见在checkThread时会判断mThread是不是等于当前线程,如果不等于,就异常
|---那mThread又是什么线程呢?

---->[ViewRootImpl#ViewRootImpl]-------
public ViewRootImpl(Context context, Display display) {
    mThread = Thread.currentThread();
|--在构造方法中被赋值的,也就是说是创建ViewRootImpl时所在的线程
|---ViewRootImpl又是在哪里被创建的呢?这里不深入讲了,是在main线程
|---从异常来看是在进行requestLayout方法时崩的

---->[ViewRootImpl#requestLayout]-------
@Override
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals();
    }
}

2.然后再俗套的说一下怎么用Handler解决

然后发现Handler好神奇啊,至于那里神奇也说不出个道道,也就是神秘 Handler的面具之下隐藏着一个有点小复杂的消息机制,这篇就来理一下

代码语言:javascript
复制
public class HandlerActivity extends AppCompatActivity {
    private TextView msgTv;
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            msgTv.setText("hello");
        }
    };
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.ac_handler);
        msgTv = findViewById(R.id.id_tv_handler);
        msgTv.setOnClickListener(v->{
            new Thread(new Runnable() {
                @Override
                public void run() {
                    mHandler.sendEmptyMessage(0x01);
                }
            }).start();
        });
    }
}

3.源码中对Handler的描述

自己翻译的,仅供参考,有不恰之处敬请指出 其中提到了MessageQueuemessage以及runnables

代码语言:javascript
复制
|--Handler允许您发送和处理与线程的MessageQueue关联的消息和可运行对象。
|--每个Handler实例都与单个线程和该线程的消息队列相关联。
|--当您创建一个新的Handler时,它会被绑定到正在创建它的线程的线程/消息队列上,
|--从那时起,它将向该消息队列传递消息和可运行项,并在它们从消息队列发出时执行它们。

|--Handler有两大主要的用处:
|--(1) 安排将消息和可运行项在将来的某个点执行
|--(2) 将在不同线程上执行的操作加入队列。

|--调度消息是通过方法:
|--post(Runnable), postAtTime(Runnable, long), postDelayed(Runnable, long),
|--sendEmptyMessage(int), sendMessage(Message),sendMessageAtTime(Message, long),
|--sendMessageDelayed(Message, long). 

4.主要成员变量
Handler主要成员变量.png
Handler主要成员变量.png
代码语言:javascript
复制
final Looper mLooper;
final MessageQueue mQueue;
final Callback mCallback;
final boolean mAsynchronous;
IMessenger mMessenger;

5.构造方法

看这七个葫芦娃,核心就只有两个构造方法(三个hide的,外面不能用),其他四个可以用

Handler的七个构造函数.png
Handler的七个构造函数.png
代码语言:javascript
复制
|--对具有指定回调接口的[当前线程]使用Looper,并设置handler是否应该是异步的。
|--默认情况下,handler是同步的,除非使用此构造函数创建一个严格异步的handler。
|--(插句话,此方法是隐藏的,说明外部调用者是无法创建异步的handler)

|--异步消息表示:不需要对同步消息进行全局排序的中断或事件。
|--异步消息不受MessageQueue#enqueueSyncBarrier(long)引入的同步屏障的限制。

* @param callback 处理消息的回调接口,或null。
* @param async 如果为true,handler将为[发送到它那的每个Message或Runnable]调用
* Message#setAsynchronous(boolean)
---->[Handler#Handler(Callback,boolean)]-------------------------------
public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {//final常量---false,所以不管他  
        final Class<? extends Handler> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }
    mLooper = Looper.myLooper();//通过Looper的myLooper方法返回值,为mLooper赋值
    if (mLooper == null) {//如果mLooper拿不到,报异常
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;//mQueue通过mLooper获取
    mCallback = callback;//入参
    mAsynchronous = async;//入参
}
|--貌似也没有做什么东西,只是将mLooper、mQueue、mCallback、mAsynchronous赋值
|--焦点在Looper.myLooper()上

---->[Handler#Handler(Callback,boolean)]-------------------------------
public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}
|--三参的大佬挺任性,直接赋值,所以Handler的构造函数没有什么太特别的
|--下面看一下Looper类

二、赤胆忠心:Looper
1:ThreadLocal

花了点时间看了ThreadLocal,已单独成文,详情见:java点将台:多重影分身[-ThreadLocal-] 这里不过多探讨ThreadLocal,给个小例子,看它的功能

代码语言:javascript
复制
public class ThreadLocalTest {
    //实例化一个装载Integer类型的ThreadLocal静态变量
    static final ThreadLocal<Integer> sThreadLocal = new ThreadLocal<>();
    //用来测试的共享成员变量
    static int count = 10;
    public static void main(String[] args) throws InterruptedException {
        sThreadLocal.set(count);
        Thread thread = new Thread() {
            @Override
            public void run() {//新建线程
                count++;
                sThreadLocal.set(count);
                System.out.println("new :" + sThreadLocal.get());
            }
        };
        thread.start();
        thread.join();//为避免歧义,这里新线程join,让main线程的打印在新线程执行之后
        System.out.println("main:"+sThreadLocal.get());

    }
}
结果2.png
结果2.png

可以看出,在新线程中对sThreadLocal.set(),并不会影响main线程的get() 所以共享成员变量count放在sThreadLocal这个篮子里,可以保证线程间共享变量的独立

ThreadLocal基本使用2.png
ThreadLocal基本使用2.png

2.Looper类(looper:/'luːpə/ 循环者)
代码语言:javascript
复制
类名:Looper      父类:Object      修饰:public final
实现的接口:[]
包名:android.os   依赖类个数:7
内部类/接口个数:0
源码行数:344       源码行数(除注释):158
属性个数:8       方法个数:20       public方法个数:18
Looper构造函数+主要成员变量.png
Looper构造函数+主要成员变量.png
代码语言:javascript
复制
---->[Looper#成员变量]------------
//实例化一个装载Looper类型的ThreadLocal静态变量
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
//似有静态内部成员变量sMainLooper
private static Looper sMainLooper; 
//MessageQueue对象
final MessageQueue mQueue;
//线程对象
final Thread mThread;

---->[Looper#Looper]------------
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}
|--在Looper的构造函数中,分别对mQueue,mThread进行初始化
|--注意构造函数是私有的,所以无法直接构造Looper对象

所以本类中一定存在new Looper,搜索一下:

搜索.png
搜索.png
代码语言:javascript
复制
//公共静态方法:准备
---->[Looper#prepare]------------
public static void prepare() {
    prepare(true);
}

//私有静态方法:准备
---->[Looper#prepare(boolean)]------------
private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
    //如果sThreadLocal可与获取到Looper,抛异常
    //也就是一个线程只能够创建一个Looper
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    //为空时创建Looper对象,并以sThreadLocal为键,new Looper(quitAllowed)为值
    //设置到当前线程的threadLocals(ThreadLocalMap对象)上
    sThreadLocal.set(new Looper(quitAllowed));
}

---->[Looper#prepare(boolean)]------------
 public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
}
|--Looper会在因ThreadLocal在每个线程中独立,非Looper生成的线程
|--sThreadLocal.get()会得到null,那prepare()是framework层的那里初始化(即prepare)的呢?

3.ActivityThread中对Looper的初始化

到framework层的源码去看一下,程序的入口main方法在ActivityThread中

ActivityThread中关于Looper.png
ActivityThread中关于Looper.png
代码语言:javascript
复制
---->[ActivityThread#成员变量]---------
final Looper mLooper = Looper.myLooper();

---->[ActivityThread#main]---------
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();//开启loop
    throw new RuntimeException("Main thread loop unexpectedly exited");
    
---->[Looper#prepareMainLooper]---------
public static void prepareMainLooper() {
    //这里调用了prepare方法,为sThreadLocal设置了Looper值
    而且在main函数中调用,所在线程为main线程,即主线程
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {//这里看出只能prepared一次
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();//在这里初始化成员变量sMainLooper
    }
}

在这里为了方便说明,debug来测试一下: 在main线程和子线程分别进行断点调试,看一下两个线程中的Looper.myLooper() 由于Looper.prepare在main线程中执行,即:sThreadLocal.set在main线程 所以Looper.myLooper(),即sThreadLocal.get()在子线程无法获取到Looper,这就是ThreadLocal的作用

Looper.myLooper()测试.png
Looper.myLooper()测试.png

三、再看Handler
1.使用含Callback的构造函数

以前是直接在Handler中覆写handleMessage方法,AndroidStudio飘橙色,看起来很碍眼 我们完全可以传回调来构建Handler,加上Java8的简化书写看着爽多了,运行无误

代码语言:javascript
复制
public class HandlerActivity extends AppCompatActivity {
    private TextView msgTv;
    private Handler mHandler ;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mHandler = new Handler(msg -> {
            msgTv.setText("hello");
            return true;
        });
        setContentView(R.layout.ac_handler);
        msgTv = findViewById(R.id.id_tv_handler);
        msgTv.setOnClickListener(v->{
            Thread thread = new Thread(() ->
                    mHandler.sendEmptyMessage(0x01));
            thread.start();
        });
    }
}

2.看一下CallBack接口
代码语言:javascript
复制
/**
 * Callback interface you can use when instantiating a Handler to avoid
 * having to implement your own subclass of Handler.
  在实例化处理程序时可以使用回调接口,以避免必须实现自己的处理Handler子类。
 */
  ---->[Handler$Callback]----------------------
public interface Callback {
    /**
     * @param msg A {@link android.os.Message Message} object
     * @return True 如果不需要进一步处理
     */
    public boolean handleMessage(Message msg);
}


/**
 * Handle system messages here.(在这里处理系统消息)
 */
 ---->[Handler#dispatchMessage]----------------------
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {//如果mCallback不为空
        //先回调用 mCallback.handleMessage(msg),返回true的话,之后就return了  
        //这有什么用? 如果你重写Handler的handleMessage又有Callback都有的话
        // true就不会再去执行Handler的handleMessage 的方法了,例子如下
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

3.即使用Callback又覆写handleMessage

感觉很不是很常用,了解一下即可, 下面代码return true;结果是hello,return false;结果是hello2

代码语言:javascript
复制
public class HandlerActivity extends AppCompatActivity {
    private TextView msgTv;
    private Handler mHandler;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mHandler = new Handler(msg -> {
            msgTv.setText("hello");
            return true;
        }) {
            @Override
            public void handleMessage(Message msg) {
                msgTv.setText("hello2");
            }
        };
        setContentView(R.layout.ac_handler);
        msgTv = findViewById(R.id.id_tv_handler);
        msgTv.setOnClickListener(v -> {
            Thread thread = new Thread(() -> {
                mHandler.sendEmptyMessage(0x01);
            });
            thread.start();
        });
    }
}

4.在子线程中创建Handler对象是否有用?

现在将创建Handler放在子线程进行,不出所料,崩了

子线程中初始化Handler.png
子线程中初始化Handler.png
代码语言:javascript
复制
public class HandlerActivity extends AppCompatActivity {
    private TextView msgTv;
    private Handler mHandler;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Handler.Callback callback = msg -> {
            msgTv.setText("hello");
            return true;
        };
        setContentView(R.layout.ac_handler);
        msgTv = findViewById(R.id.id_tv_handler);
        msgTv.setOnClickListener(v -> {
            Thread thread = new Thread(() -> {
                mHandler = new Handler(callback);//<--创建Handler
                mHandler.sendEmptyMessage(0x01);
            });
            thread.start();
        });
    }
}

---->[看一下崩的原因]----------------
---->[Handler#Handler(Callback, boolean)]----------------
public Handler(Callback callback, boolean async) {
    //略...
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}
|---前面我们已经debug过,在子线程中Looper.myLooper()为空,所以会崩掉

5.如何在子线程中创建Handler

Handler的构造函数中有Looper参数,我们可以在外面获取主线程的Looper对象,给Handler构造 除此之外Context也给我们提供了获取main线程Looper的方法,debug可以看出,两者是同一对象

如何在子线程中创建Handler.png
如何在子线程中创建Handler.png
代码语言:javascript
复制
public class HandlerActivity extends AppCompatActivity {
    private TextView msgTv;
    private Handler mHandler;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Handler.Callback callback = msg -> {
            msgTv.setText("hello");
            return true;
        };
        Looper looper = Looper.myLooper();//获取main线程的looper
        setContentView(R.layout.ac_handler);
        msgTv = findViewById(R.id.id_tv_handler);
        msgTv.setOnClickListener(v -> {
            Thread thread = new Thread(() -> {
                mHandler = new Handler(looper,callback);
                mHandler.sendEmptyMessage(0x01);
            });
            thread.start();
        });
    }
}

四、消息队列(难点,核心)
1.回到这个最简单的消息发送
代码语言:javascript
复制
public class HandlerActivity extends AppCompatActivity {
    private TextView msgTv;
    private Handler mHandler;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Handler.Callback callback = msg -> {
            msgTv.setText("hello");
            return true;
        };
        mHandler = new Handler(callback);

        setContentView(R.layout.ac_handler);
        msgTv = findViewById(R.id.id_tv_handler);

        msgTv.setOnClickListener(v -> {
            Thread thread = new Thread(() -> {
                mHandler.sendEmptyMessage(0x01);
            });
            thread.start();
        });
    }
}

2.走一下Handler源码

一连串的sendMessageXXX基本上把发送消息的方法走了一遍, 但万剑归一,最终调用的是enqueueMessage(MessageQueue queue,Message msg, long uptimeMillis)

代码语言:javascript
复制
sendEmptyMessage(int what) : 发送空消息,
sendEmptyMessageDelayed(int what, long delayMillis),发送延迟空消息
sendMessageDelayed(Message msg, long delayMillis) 发送延迟消息
sendMessageAtTime(Message msg, long uptimeMillis) 定时发送消息
enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) 消息入队
Handler发送消息.png
Handler发送消息.png
代码语言:javascript
复制
---->[Handler#sendEmptyMessage]-----------------
|--只有消息的what(用来表识消息),调用:sendEmptyMessageDelayed
public final boolean sendEmptyMessage(int what){
    return sendEmptyMessageDelayed(what, 0);
}

---->[Handler#sendEmptyMessageDelayed]-----------------
|--只有消息的what(用来表识消息),延迟毫秒数,调用:sendMessageDelayed
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageDelayed(msg, delayMillis);
}

---->[Handler#sendMessageDelayed]-----------------
|--接收一个消息对象,延迟毫秒数,调用 sendMessageAtTime
public final boolean sendMessageDelayed(Message msg, long delayMillis){
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

---->[Handler#sendMessageAtTime]-----------------
|--接收一个消息对象,延迟毫秒数,调用 enqueueMessage
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;//获取消息队列
    if (queue == null) {//消息队列为空,抛异常
        RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}

---->[Handler#enqueueMessage]-----------------
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;//注意这里讲当前Handler作为消息的target
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);//调用了MessageQueue的enqueueMessage方法
}

现在我们面前出现了两个类:MessageMessageQueue


3.先看Message(消息)类

Message的成员变量有点多

代码语言:javascript
复制
what----int 型,用来表识该信息
arg1----int型,储存简单的int数据
arg2----int型,储存简单的int数据
obj --- 任意类型,储存任意数据
replyTo ---- Messenger类型 可选的信使,在那里回复这条消息可以发送。具体如何使用它的语义取决于发送方和接收方。
Message部分成员变量.png
Message部分成员变量.png

先从一个例子开始引入Message吧


3.1:new Message()Message.obtain()Handler.obtain()

可见三种方式都能运作,那么有什么区别呢?

三种创建消息对象的方式.png
三种创建消息对象的方式.png
代码语言:javascript
复制
/**
 * 作者:张风捷特烈<br/>
 * 时间:2019/1/25/025:14:24<br/>
 * 邮箱:1981462002@qq.com<br/>
 * 说明:三种创建消息对象的方式
 */
public class HandlerActivity extends AppCompatActivity {
    private TextView msgTv;
    private Handler mHandler;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Handler.Callback callback = msg -> {
            String txt = (String) msg.obj;
            switch (msg.what) {
                case 0x01:
                    txt += "----第一条";
                    break;
                case 0x02:
                    txt += "----第二条";
                    break;
                case 0x03:
                    txt += "----第三条";
                    break;
            }
            msgTv.setText(txt);
            return true;
        };
        mHandler = new Handler(callback);
        setContentView(R.layout.ac_handler);
        msgTv = findViewById(R.id.id_tv_handler);
        msgTv.setOnClickListener(v -> {
            Thread thread = new Thread(() -> {
                //new Message()创建消息
                Message newMsg = new Message();
                newMsg.what = 0x01;
                newMsg.obj = "玉面奕星龙";
                mHandler.sendMessage(newMsg);
                //Message.obtain()创建消息
                Message msgObtain = Message.obtain();
                msgObtain.what = 0x02;
                msgObtain.obj = "张风捷特烈";
                mHandler.sendMessageDelayed(msgObtain, 3000);
                //mHandler.obtainMessage()创建消息
                Message handlerObtain = mHandler.obtainMessage();
                handlerObtain.what = 0x03;
                handlerObtain.obj = "千里巫缨";
                mHandler.sendMessageDelayed(handlerObtain, 6000);
            });
            thread.start();
        });
    }
}

3.2:三者的区别
代码语言:javascript
复制
1. new Message() 直接创建对象,没什么好说的


2.---->[Message]------------------
private static final Object sPoolSync = new Object();//锁对象
private static Message sPool;//消息池的链表首
private static int sPoolSize = 0;//当前消息池的大小
private static final int MAX_POOL_SIZE = 50;//消息池的最大尺寸

---->[Message#obtain]------------------
public static Message obtain() {
    synchronized (sPoolSync) {//同步锁
        if (sPool != null) {//如果消息池不为空
            Message m = sPool;//将sPool对象赋值给m对象
            sPool = m.next;//m的next赋值给sPool对象
            m.next = null;//将m的next置空
            m.flags = 0; // clear in-use flag
            sPoolSize--;//消息池的大小-1
            return m;//将消息返回
        }
    }
    return new Message();//如果消息池为空时,返回新的Message对象
}
|-- 这里很明显使用了单链表,将一个消息从消息池中出列。
|-- 维护消息池的好处不用多说,避免频繁创建和销毁Message,
|-- 比如频繁地发送消息(轮播图),每次切换一下发一个消息,使用消息池维护会更好

3.---->[Handler#obtainMessage]------------------
public final Message obtainMessage(){
    return Message.obtain(this);
}

public static Message obtain(Handler h) {
    Message m = obtain();
    m.target = h;
    return m;
}

|-- 获取Message后将target设置成当前handler,这一步enqueueMessage中已经有了,所以两者一样
|-- 除此之外Message还有很多重载的 `Message obtain`,都是基于Message.obtain,
|-- 同时再为其设置一些属性,本质上也没有什么太大的区别,自己看一下就行了

4.MessageQueue消息队列

Message中并没有为消息池中添加消息的方法,那么消息池是怎么实现的? 从Handler#enqueueMessage为切入点,看一下这个消息是如何加入消息队列中的

代码语言:javascript
复制
---->[Handler#enqueueMessage]------------------------
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;//设置当前消息的target为本Handler
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

---->[MessageQueue#enqueueMessage]------------------------
boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {//target为null,即没有对应的Handler,抛异常
        throw new IllegalArgumentException("Message must have a target.");
    }
    if (msg.isInUse()) {//消息已经在使用,抛异常
        throw new IllegalStateException(msg + " This message is already in use.");
    }
    synchronized (this) {//this同步锁
        if (mQuitting) {
            IllegalStateException e = new IllegalStateException(
                    msg.target + " sending message to a Handler on a dead thread");
            Log.w(TAG, e.getMessage(), e);
            msg.recycle();
            return false;
        }
        msg.markInUse();
        msg.when = when;//设置入队时间
        Message p = mMessages;//将mMessages,即队首赋给p
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {//p为空
            //新建队列头,如果阻塞,唤醒事件队列
            msg.next = p;//当前消息作为队列首
            mMessages = msg;//维护队首
            needWake = mBlocked;
        } else {//表示当前消息队列中有消息了
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;//声明前一个消息
            for (;;) {//相当于while(true)的作用
                prev = p;//将原来的队首元素赋给prev变量
                p = p.next;//将p的下一个消息赋值给p
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p; //将当前消息的下一指向到p
            prev.next = msg;//prev指向当前消息
        }
        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

>看文字估计挺晕的,以上面三个消息为例,分析一下他们加入的流程
第一条消息入队.png
第一条消息入队.png
代码语言:javascript
复制
当第二条消息入队时:msg:张风捷特烈
Message p = mMessages  p:玉面奕星龙 不为空,走下面
Message prev;//声明前一个消息
for (;;) {//相当于while(true)的作用
    prev = p;//prev:玉面奕星龙
    p = p.next;//p.next =null,执行后 p=null
    if (p == null || when < p.when) {
        break;
    }
    if (needWake && p.isAsynchronous()) {
        needWake = false;
    }
}
msg.next = p; //张风捷特烈-->null
prev.next = msg;//玉面奕星龙-->张风捷特烈-->null


当第三条消息入队时:msg:百里巫缨
Message p = mMessages  p:玉面奕星龙 不为空,走下面
Message prev;//声明前一个消息
for (;;) {//相当于while(true)的作用
    //第一次循环--prev:玉面奕星龙
    //第二次循环--prev:张风捷特烈
    prev = p;
    //第一次循环--p.next = 张风捷特烈,执行后 p=张风捷特烈
    //第二次循环--p.next = null,执行后 p=null
    p = p.next;
    if (p == null || when < p.when) {
        break;
    }
    if (needWake && p.isAsynchronous()) {
        needWake = false;
    }
}
msg.next = p; //百里巫缨-->null,此时prev:张风捷特烈
prev.next = msg;//玉面奕星龙-->张风捷特烈-->百里巫缨-->null

|-- 由此可见,每添加一条消息都是添加到队尾

5.Looper的loop方法

将消息加入队列之后是如何取出的?又如何触发Handler的dispatchMessage回调方法? 这里略去了打日志的一些语句,可见loop方法一直将调用queue.next()直到msg == null 在拿到消息后出发msg.target.dispatchMessage(msg);然后就豁然开朗了 但当没有消息时MessageQueue#next()会被阻塞,而导致loop阻塞。所以next无法让轮循停止 要关闭循环使用MessageQueue#quit。

代码语言:javascript
复制
---->[Looper#loop]---------
public static void loop() {
    //获取当前线程的Looper对象
    final Looper me = myLooper();
    if (me == null) {//如果为空,报异常。说明当前线程没有Looper对象
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.")
    }
    //queue使用的是Looper中的mQueue(在构造函数中被初始化)
    final MessageQueue queue = me.mQueue;
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();
    for (;;) {//相当于while(true)的作用
        Message msg = queue.next(); // 消息
        if (msg == null) {
            return;
        }
        //略...
        final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
        //略...
        final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        final long end;
        try {
            //在这里调用了msg.target即该handler的dispatchMessage方法
            msg.target.dispatchMessage(msg);
            end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
         //略...
        msg.recycleUnchecked();
    }
}

---->[MessageQueue#next]---------------
Message next() {
    final long ptr = mPtr;
    if (ptr == 0) {
        return null;
    }
    int pendingIdleHandlerCount = -1; 
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        nativePollOnce(ptr, nextPollTimeoutMillis);//这里用来一个native方法
        synchronized (this) {
            //尝试检索下一条消息。如果发现返回。
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;//前一个msg
            Message msg = mMessages;//当前msg为队首
            if (msg != null && msg.target == null) {//一般都有target
                // 被障碍物挡住了。在队列中查找下一个异步消息
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {
                    // 下一条消息没有准备好。设置超时以在何时唤醒
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integ
                } else {
                    //获取一个msg-------
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    msg.markInUse();
                    return msg;
                }
            } else {
                // 没有消息了
                nextPollTimeoutMillis = -1;
            }
            // 现在所有挂起的消息都已完成,请处理退出消息
            if (mQuitting) {
                dispose();
                return null;
            }
   
            if (pendingIdleHandlerCount < 0
                    && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            if (pendingIdleHandlerCount <= 0) {
                // No idle handlers to run.  Loop and wait some more.
                mBlocked = true;
                continue;
            }
            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandl
            }
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            final IdleHandler idler = mPendingIdleHandlers[i];
            mPendingIdleHandlers[i] = null; // release the reference to the hand
            boolean keep = false;
            try {
                keep = idler.queueIdle();
            } catch (Throwable t) {
                Log.wtf(TAG, "IdleHandler threw exception", t);
            }
            if (!keep) {
                synchronized (this) {
                    mIdleHandlers.remove(idler);
                }
            }
        }
        // Reset the idle handler count to 0 so we do not run them again.
        pendingIdleHandlerCount = 0;
        // While calling an idle handler, a new message could have been delivere
        // so go back and look again for a pending message without waiting.
        nextPollTimeoutMillis = 0;
    }
}

---->[Handler#dispatchMessage]---------------
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {//如果msg有回调
        handleCallback(msg);//处理msg的callback 
    } else {
        if (mCallback != null) {//这个上面举例说明过,不说了
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);//回调覆写的handleMessage方法
    }
}

---->[Handler#handleCallback]---------------
private static void handleCallback(Message message) {
    message.callback.run();
}

关于MessageQueue#next方法,无非就是让消息出队,没有消息时next会一直阻塞 这里涉及了native的方法,以及控制next的阻塞来完成延迟触发,这里native不展开, 想深入的,这里推荐一篇文章Android MessageQueue消息循环处理机制


五、Handler#postXXX与Message的callback
1.dispatchMessage方法分析

不知道你有没有注意到,msg里有的callback,为了强调dispatchMessage,这里再看一次

dispatchMessage的流程.png
dispatchMessage的流程.png
代码语言:javascript
复制
---->[Handler#dispatchMessage]---------------
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {//如果msg有回调
        handleCallback(msg);//处理msg的callback 
    } else {
        if (mCallback != null) {//这个上面举例说明过,不说了
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);//回调覆写的handleMessage方法
    }
}

---->[Handler#handleCallback]---------------
private static void handleCallback(Message message) {
    message.callback.run();
}

2.既然msg可以添加一个Runnable的callback那试一下呗

不幸的事callback是包访问级别的,没有提供set方法,所以用不了 但是提供了一个obtain的重载,可以放置callback

代码语言:javascript
复制
---->[Message#obtain(Handler, Runnable)]
public static Message obtain(Handler h, Runnable callback) {
    Message m = obtain();
    m.target = h;
    m.callback = callback;
    return m;
}
------------------------------------------------------
/**
 * 作者:张风捷特烈<br/>
 * 时间:2019/1/25/025:14:24<br/>
 * 邮箱:1981462002@qq.com<br/>
 * 说明:当msg自身有Runnable回调时
 */
public class HandlerMsgWithCbkActivity extends AppCompatActivity {
    private TextView msgTv;
    private Handler mHandler;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Handler.Callback callback = msg -> {
            msgTv.setText("回调的handleMessage");
            return true;
        };

        mHandler = new Handler(callback){
            @Override
            public void handleMessage(Message msg) {
                msgTv.setText("覆写的handleMessage");

            }
        };

        setContentView(R.layout.ac_handler);
        msgTv = findViewById(R.id.id_tv_handler);

        msgTv.setOnClickListener(v -> {
            Thread thread = new Thread(() -> {

                Message msgObtain = Message.obtain(mHandler, new Runnable() {
                    @Override
                    public void run() {
                        msgTv.setText("Message + Runnable");
                    }
                });
                msgObtain.what = 0x02;
                msgObtain.obj = "张风捷特烈";
                mHandler.sendMessageDelayed(msgObtain, 3000);
            });
            thread.start();
        });
    }
}

|--运行结果如下,结合dispatchMessage方法分析图,应该足以说明
jieguo.png
jieguo.png

3.Handler的几个postXXX方法
post(Runnable r).png
post(Runnable r).png
代码语言:javascript
复制
boolean post(Runnable r) post一个Runnable
boolean postAtTime(Runnable r, long uptimeMillis) 定时
boolean postAtTime(Runnable r, Object token, long uptimeMillis) 加token
boolean postDelayed(Runnable r, long delayMillis) 演示
boolean postAtFrontOfQueue(Runnable r) post到队首

---->[Handler#post]-----------------
public final boolean post(Runnable r){
   return  sendMessageDelayed(getPostMessage(r), 0);
}

---->[Handler#getPostMessage]-----------------
private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

|-- 可见post也没有多么高大上,只是调用了sendMessageDelayed而已
|-- 其中的Message通过getPostMessage获取,在getPostMessage中
|-- 通过Message.obtain()从消息池中取出一个消息,并添加callback而已
|-- 后面几个方法差不多,只是给Message多添一点信息而已,Message理解了,就不成问题
|-- 这几个方法相当于Handler给我们封装了一下,要完成上面的测试,可以:

---->[HandlerMsgWithCbkActivity]-----------------
msgTv.setOnClickListener(v -> {
    Thread thread = new Thread(() -> {
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                msgTv.setText("Message + Runnable");
            }
        }, 3000);
    });
    thread.start();
});

|-- 注意,使用postXXX,会让Handler.callback和覆写的handleMessage失效  
|-- 因为其本质上是通过Message的callback,前面已经讲了Message.callback一旦存在  
|-- 是不会再走上两者的。

终曲、回看Handler:

这时候回头看一下Handler背后的消息机制:

代码语言:javascript
复制
消息承载单体:Message  
消息管理器:MessageQueue
消息机制驱动力:Looper  
消息处理器:Handler

---->[为了结尾点题,编个故事吧]---注:本故事纯属虚构-------------
传说在100亿年前,宇宙不止一个,人们生活的宇宙被称为main宇宙
除了main宇宙之外,称为子宇宙,由于宇宙众多,被人们称为三千宇宙
有些人试图在子宇宙改变自己的样子(包括服饰),开始新的生活,
但无一例外,全部灰飞烟灭,非main宇宙无法改变容饰成了平行宇宙法则
但main宇宙中人员众多,资源相对匮乏,很多人只能去子宇宙去获取资源

一个叫TextView的女生想要一件漂亮的衣服,作为男友的Handler义无反顾独闯子宇宙,
他爆出了3件非常漂亮的衣服,知道让TextView直接来到子宇宙穿上的话,她便会立刻灰飞烟灭  
于是他用3个空间立方(Message)将3件衣服分别装入其中,并标识message的target是自己  
然后3个空间立方被依次放入了[平行宇宙传送台(MessageQueue)],
main宇宙中的强大Looper能源驱动着[平行宇宙传送台],自从三千宇宙诞生的那刻就开启了(Looper.loop)  
当空间立方传递到主宇宙时,空间立方传根据target找到自己的主人曾经的嘱托(handleMessage) 
然后将三件美丽的服饰依次给TextView穿上,这样就实现了子宇宙资源对宇宙的传送  

有些常年在子宇宙工作的人,也经常使用这种机制,寄一封回家,传达思念与祝福
而这些,就是当时他们的日常生活...
最后一个问题:Handler只是能在主宇宙和子宇宙间传递资源吗?

每个子宇宙都可以拥有仅属于自己的一个Looper,子宇宙间也可以通过Handler进行通信

代码语言:javascript
复制
/**
 * 作者:张风捷特烈<br/>
 * 时间:2019/1/25/025:14:24<br/>
 * 邮箱:1981462002@qq.com<br/>
 * 说明:子线程间通信
 */
public class HandlerOtherActivity extends AppCompatActivity {
    private TextView msgTv;
    private Handler mHandler;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.ac_handler);
        msgTv = findViewById(R.id.id_tv_handler);
        msgTv.setOnClickListener(v -> {
            new Thread("第一子宇宙") {//第一子宇宙
                @Override
                public void run() {
                    Message newMsg = new Message();
                    newMsg.what = 0x01;
                    newMsg.obj = "玉面奕星龙";
                    mHandler.sendMessage(newMsg);
                    Log.e("HandlerOtherActivity", "当前线程名称: " + Thread.currentThread().getName() + " 发送:" + ne
                }
            }.start();
        });
        new Thread("第二子宇宙") {//第二子宇宙
            @Override
            public void run() {
                Looper.prepare();//让当前子宇宙生成--looper能源
                //Handler通过第二子宇宙的looper能源能源构造
                mHandler = new Handler(msg -> {
                    Log.e("HandlerOtherActivity", "当前线程名称: " + Thread.currentThread().getName() + " 接收:" + ms
                    return false;
                });
                Log.e("HandlerOtherActivity", "run: ");
                Looper.loop();//looper能源启动--此时该线程会阻塞------下面的方法无法执行
                Log.e("HandlerOtherActivity", "run:-------- ");
            }
        }.start();
    }
}

|-- 注意点:当第一线程要向第二线程传递数据,mHandler要拥有第二线程的looper
|-- 也就是让Handler在第二线程中创建,下图已经很明确让的表达出:
|-- 第一线程向第二线程传递了一条信息,而且第二线程会阻塞在Looper.loop()是,下一句无法打印
子线程通过handler通信.png
子线程通过handler通信.png

为了让你对Looper有更深的认识,换一种写法

下面的写法如果你看懂了,handler机制就不在话下了 一句话:在main线程中使用线程2的looper创建Handler,在线程1中通过Handler发送消息 结果消息仍是在线程2中执行的,看日志并未改变:(只有looper在哪个线程,消息就会发到哪个线程)

子线程通过handler通信.png
子线程通过handler通信.png
代码语言:javascript
复制
/**
 * 作者:张风捷特烈<br/>
 * 时间:2019/1/25/025:14:24<br/>
 * 邮箱:1981462002@qq.com<br/>
 * 说明:子线程间通信
 */
public class HandlerOtherActivity extends AppCompatActivity {
    private TextView msgTv;
    private Handler mHandler;

    private Looper mOtherLooper;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.ac_handler);
        msgTv = findViewById(R.id.id_tv_handler);

        msgTv.setOnClickListener(v -> {

            new Thread("第一子宇宙") {//第一子宇宙
                @Override
                public void run() {
                    Message newMsg = new Message();
                    newMsg.what = 0x01;
                    newMsg.obj = "玉面奕星龙";
                    mHandler.sendMessage(newMsg);
                    Log.e("HandlerOtherActivity", "当前线程名称: " + Thread.currentThread().getName() + " 发送:" + newMsg.obj);
                }
            }.start();
        });

        new Thread("第二子宇宙") {//第二子宇宙
            @Override
            public void run() {
                Looper.prepare();//让当前子宇宙生成--looper能源
                mOtherLooper = Looper.myLooper();

                Log.e("HandlerOtherActivity", "run: ");
                Looper.loop();//looper能源启动--此时该线程会阻塞------下面的方法无法执行
                Log.e("HandlerOtherActivity", "run:-------- ");
            }
        }.start();

        try {
            Thread.sleep(10);//睡10ms让mOtherLooper可以初始化
//            Handler通过第二子宇宙的looper能源能源构造
            mHandler = new Handler(mOtherLooper, msg -> {
                Log.e("HandlerOtherActivity", "当前线程名称: " + Thread.currentThread().getName() + " 接收:" + msg.obj);
                return false;
            });

        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2024-02-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
    • Handler总览
      • 一、引入Handler的一般套路
        • 1.说Handler一般套路是从一个异常开始
        • 2.然后再俗套的说一下怎么用Handler解决
        • 3.源码中对Handler的描述
        • 4.主要成员变量
        • 5.构造方法
      • 二、赤胆忠心:Looper
        • 1:ThreadLocal
        • 2.Looper类(looper:/'luːpə/ 循环者)
        • 3.ActivityThread中对Looper的初始化
      • 三、再看Handler
        • 1.使用含Callback的构造函数
        • 2.看一下CallBack接口
        • 3.即使用Callback又覆写handleMessage
        • 4.在子线程中创建Handler对象是否有用?
        • 5.如何在子线程中创建Handler
      • 四、消息队列(难点,核心)
        • 1.回到这个最简单的消息发送
        • 2.走一下Handler源码
        • 3.先看Message(消息)类
        • 4.MessageQueue消息队列
        • 5.Looper的loop方法
      • 五、Handler#postXXX与Message的callback
        • 1.dispatchMessage方法分析
        • 2.既然msg可以添加一个Runnable的callback那试一下呗
        • 3.Handler的几个postXXX方法
      • 终曲、回看Handler:
        • 最后一个问题:Handler只是能在主宇宙和子宇宙间传递资源吗?
        • 为了让你对Looper有更深的认识,换一种写法
    相关产品与服务
    消息队列 CMQ
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档