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

栏目: 后端 · 发布时间: 5年前

内容简介:哥发誓,这是我最后一次分析Handler(这是我第三次说这句话,希望不会有下次了)说起Handler你以为我会这么俗气地从:

哥发誓,这是我最后一次分析Handler(这是我第三次说这句话,希望不会有下次了)

说起Handler 新手的小噩梦,用起来不难,理解起来烦神

Handler总览

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

一、引入Handler的一般套路

1.说Handler一般套路是从一个异常开始

你以为我会这么俗气地从: 非主线程禁止更新UI 开始说Handler吗?--是的

这个异常定义在 ViewRootImpl 里,更新TextView的UI为什么ViewRootImpl报异常?

子不教,父之过,教不严,师之惰呗。在framework的源码里看一下吧,

Android点将台:烽火狼烟[-Handler-]
---->[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的面具之下隐藏着一个有点小复杂的消息机制,这篇就来理一下

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

|--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.主要成员变量

Android点将台:烽火狼烟[-Handler-]
final Looper mLooper;
final MessageQueue mQueue;
final Callback mCallback;
final boolean mAsynchronous;
IMessenger mMessenger;
复制代码

5.构造方法

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

Android点将台:烽火狼烟[-Handler-]
|--对具有指定回调接口的[当前线程]使用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,给个小例子,看它的功能

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());

    }
}
复制代码
Android点将台:烽火狼烟[-Handler-]

可以看出,在新线程中对sThreadLocal.set(),并不会影响main线程的get()

所以共享成员变量count放在sThreadLocal这个篮子里,可以保证线程间共享变量的独立

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

2.Looper类( looper:/'luːpə/ 循环者 )

类名:Looper      父类:Object      修饰:public final
实现的接口:[]
包名:android.os   依赖类个数:7
内部类/接口个数:0
源码行数:344       源码行数(除注释):158
属性个数:8       方法个数:20       public方法个数:18
复制代码
Android点将台:烽火狼烟[-Handler-]
---->[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 ,搜索一下:

Android点将台:烽火狼烟[-Handler-]
//公共静态方法:准备
---->[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中

Android点将台:烽火狼烟[-Handler-]
---->[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的作用

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

三、再看Handler

1.使用含Callback的构造函数

以前是直接在Handler中覆写 handleMessage 方法,AndroidStudio飘橙色,看起来很碍眼

我们完全可以传回调来构建Handler,加上 Java 8的简化书写看着爽多了,运行无误

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接口

/**
 * 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

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放在子线程进行,不出所料,崩了

Android点将台:烽火狼烟[-Handler-]
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可以看出,两者是同一对象

Android点将台:烽火狼烟[-Handler-]
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.回到这个最简单的消息发送

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)

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) 消息入队
复制代码
Android点将台:烽火狼烟[-Handler-]
---->[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的成员变量有点多

what----int 型,用来表识该信息
arg1----int型,储存简单的int数据
arg2----int型,储存简单的int数据
obj --- 任意类型,储存任意数据
replyTo ---- Messenger类型 可选的信使,在那里回复这条消息可以发送。具体如何使用它的语义取决于发送方和接收方。
复制代码
Android点将台:烽火狼烟[-Handler-]

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

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

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

Android点将台:烽火狼烟[-Handler-]
/**
 * 作者:张风捷特烈<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:三者的区别

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 为切入点,看一下这个消息是如何加入消息队列中的

---->[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;
}

>看文字估计挺晕的,以上面三个消息为例,分析一下他们加入的流程
复制代码
Android点将台:烽火狼烟[-Handler-]
当第二条消息入队时: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。

---->[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,这里再看一次

Android点将台:烽火狼烟[-Handler-]
---->[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

---->[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方法分析图,应该足以说明
复制代码
Android点将台:烽火狼烟[-Handler-]

3.Handler的几个postXXX方法

Android点将台:烽火狼烟[-Handler-]
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背后的消息机制:

消息承载单体: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进行通信

/**
 * 作者:张风捷特烈<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 -> {
            Thread thread = new Thread(() -> {//第一子宇宙
                Message newMsg = new Message();
                newMsg.what = 0x01;
                newMsg.obj = "玉面奕星龙";
                mHandler.sendMessage(newMsg);
            });
            thread.start();
        });

        new Thread() {//第二子宇宙
            @Override
            public void run() {
                Looper.prepare();//让当前子宇宙生成--looper能源
                //Handler通过第二子宇宙的looper能源能源构造
                mHandler = new Handler(msg -> {
                    Log.e("HandlerOtherActivity", "handleMessage"+msg.obj);
                    return false;
                });
                Log.e("HandlerOtherActivity", "run: ");
                Looper.loop();//looper能源启动--此时该线程会阻塞------下面的方法无法执行
                Log.e("HandlerOtherActivity", "run:-------- ");
            }
        }.start();
    }
}

|-- 注意点:当第一线程要向第二线程传递数据,mHandler要拥有第二线程的looper
|-- 也就是让Handler在第二线程中创建,下图已经很明确让的表达出:
|-- 第一线程向第二线程传递了一条信息,而且第二线程会阻塞在Looper.loop()是,下一句无法打印
复制代码
Android点将台:烽火狼烟[-Handler-]

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

下面的写法如果你看懂了,handler机制就不在话下了

一句话:在main线程中使用线程2的looper创建Handler,在线程1中通过Handler发送消息

结果消息仍是在线程2中执行的,看日志并未改变:(只有looper在哪个线程,消息就会发到哪个线程)

Android点将台:烽火狼烟[-Handler-]
/**
 * 作者:张风捷特烈<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();
        }

    }
}
复制代码

后记:捷文规范

1.本文成长记录及勘误表

项目源码 日期 附录
V0.1--无 2018-1-29

发布名: Android点将台:烽火狼烟[-Handler-]
捷文链接: juejin.im/post/5c4584…

2.更多关于我

笔名 QQ 微信
张风捷特烈 1981462002 zdl1994328

我的github: github.com/toly1994328

我的简书: www.jianshu.com/u/e4e52c116…

我的简书: www.jianshu.com/u/e4e52c116…

个人网站:www.toly1994.com

3.声明

1----本文由张风捷特烈原创,转载请注明

2----欢迎广大编程爱好者共同交流

3----个人能力有限,如有不正之处欢迎大家批评指证,必定虚心改正

4----看到这里,我在此感谢你的喜欢与支持

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Ajax修炼之道

Ajax修炼之道

(美)哥特兰、高伯瑞斯、艾米亚 / 徐锋,胡冰 / 电子工业出版社 / 2006-4 / 29.8

Ajax将静态Web页面转变为充满交互的应用。现在您不需要牺牲Web应用程序部署的简单性,就可以将“胖”客户端应用程序部署到客户端。不过对于很多人业说,Ajax看起来很难。这就是我们撰写本书的原因。作为实践的指导,本书揭开了Ajax神秘的面纱,教您如何以简单的方式使用Ajax。本书内容覆盖了DHTML、Javascript和闻名已久的XmlHttp Request回调技术的基础知识。您将了解如何将......一起来看看 《Ajax修炼之道》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

随机密码生成器
随机密码生成器

多种字符组合密码

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具