内容简介:哥发誓,这是我最后一次分析Handler(这是我第三次说这句话,希望不会有下次了)说起Handler你以为我会这么俗气地从:
哥发誓,这是我最后一次分析Handler(这是我第三次说这句话,希望不会有下次了)
说起Handler 新手的小噩梦,用起来不难,理解起来烦神
Handler总览
一、引入Handler的一般套路
1.说Handler一般套路是从一个异常开始
你以为我会这么俗气地从: 非主线程禁止更新UI
开始说Handler吗?--是的
这个异常定义在 ViewRootImpl
里,更新TextView的UI为什么ViewRootImpl报异常?
子不教,父之过,教不严,师之惰呗。在framework的源码里看一下吧,
---->[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的描述
自己翻译的,仅供参考,有不恰之处敬请指出
其中提到了 MessageQueue
和 message
以及 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.主要成员变量
final Looper mLooper; final MessageQueue mQueue; final Callback mCallback; final boolean mAsynchronous; IMessenger mMessenger; 复制代码
5.构造方法
看这七个葫芦娃,核心就只有两个构造方法(三个hide的,外面不能用),其他四个可以用
|--对具有指定回调接口的[当前线程]使用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()); } } 复制代码
可以看出,在新线程中对sThreadLocal.set(),并不会影响main线程的get()
所以共享成员变量count放在sThreadLocal这个篮子里,可以保证线程间共享变量的独立
2.Looper类( looper:/'luːpə/ 循环者
)
类名:Looper 父类:Object 修饰:public final 实现的接口:[] 包名:android.os 依赖类个数:7 内部类/接口个数:0 源码行数:344 源码行数(除注释):158 属性个数:8 方法个数:20 public方法个数:18 复制代码
---->[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
,搜索一下:
//公共静态方法:准备 ---->[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#成员变量]--------- 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的作用
三、再看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放在子线程进行,不出所料,崩了
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可以看出,两者是同一对象
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) 消息入队 复制代码
---->[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方法 } 复制代码
现在我们面前出现了两个类: Message
和 MessageQueue
3.先看Message(消息)类
Message的成员变量有点多
what----int 型,用来表识该信息 arg1----int型,储存简单的int数据 arg2----int型,储存简单的int数据 obj --- 任意类型,储存任意数据 replyTo ---- Messenger类型 可选的信使,在那里回复这条消息可以发送。具体如何使用它的语义取决于发送方和接收方。 复制代码
先从一个例子开始引入Message吧
3.1: new Message()
与 Message.obtain()
与 Handler.obtain()
可见三种方式都能运作,那么有什么区别呢?
/** * 作者:张风捷特烈<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; } >看文字估计挺晕的,以上面三个消息为例,分析一下他们加入的流程 复制代码
当第二条消息入队时: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,这里再看一次
---->[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方法分析图,应该足以说明 复制代码
3.Handler的几个postXXX方法
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()是,下一句无法打印 复制代码
为了让你对Looper有更深的认识,换一种写法
下面的写法如果你看懂了,handler机制就不在话下了
一句话:在main线程中使用线程2的looper创建Handler,在线程1中通过Handler发送消息
结果消息仍是在线程2中执行的,看日志并未改变:(只有looper在哪个线程,消息就会发到哪个线程)
/** * 作者:张风捷特烈<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.更多关于我
笔名 | 微信 | |
---|---|---|
张风捷特烈 | 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点将台:济世儒侠[-ContentProvider-]
- Android点将台:颜值担当[-Activity-]
- Android点将台:绝命暗杀官[-Service-]
- Android点将台:金科玉律[-AIDL-]
- Android点将台:传令官[-BroadcastReciver-](使用级)
- Android 点将台:撒豆成兵[- Fragment -]
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Ajax修炼之道
(美)哥特兰、高伯瑞斯、艾米亚 / 徐锋,胡冰 / 电子工业出版社 / 2006-4 / 29.8
Ajax将静态Web页面转变为充满交互的应用。现在您不需要牺牲Web应用程序部署的简单性,就可以将“胖”客户端应用程序部署到客户端。不过对于很多人业说,Ajax看起来很难。这就是我们撰写本书的原因。作为实践的指导,本书揭开了Ajax神秘的面纱,教您如何以简单的方式使用Ajax。本书内容覆盖了DHTML、Javascript和闻名已久的XmlHttp Request回调技术的基础知识。您将了解如何将......一起来看看 《Ajax修炼之道》 这本书的介绍吧!