Android基于Handler、Looper、MessageQueue、ThreadLocal的跨线程通信
来源:互联网 发布:人民网舆情监测室 知乎 编辑:程序博客网 时间:2024/06/05 16:09
一般用法:该demo没有使用Handler
class SoundPoolListenerThread extends Thread {
public SoundPoolListenerThread() {
super("SoundPoolListenerThread");
}
@Override
public void run() {
Looper.prepare();
mSoundPoolLooper = Looper.myLooper();
synchronized (mSoundEffectsLock) {
if (mSoundPool != null) {
mSoundPoolCallBack = new SoundPoolCallback();
mSoundPool.setOnLoadCompleteListener(mSoundPoolCallBack);
}
mSoundEffectsLock.notify();
}
Looper.loop();
}
}
这种Looper用法:目的跑起来个线程,在线程中执行work,对应在另一个地方应该调用Looper对象的quit来结束线程
2:上面代码竟然没有使用mSoundPoolLooper对象创建的Handler对象,没用的废物;
来看另一个demo
mLooperThread = new Thread() {
@Override
public void run() {
Log.v(TAG, "starting looper");
Looper.prepare();
mHandler = new Handler();
sem.release();
Looper.loop();
Log.v(TAG, "quit looper");
}
};
mLooperThread.start();
在某个地方我们会看到如下的操作:都是套路
mHandler.post(new Runnable() {
@Override
public void run() {
try {
command.run();
} finally {
sem.release();
}
}
});
分析一下第二个demo:
1-我们在调用mLooperThread.start()时,会去调用Thread的run(),run()中会(注意:套路来了)Looper.prepare() -> new Handler -> Looper.loop(),这三个方法都是在新的线程中执行的,即都运行在mLooperThread的空间中;
2- Looper.prepare():在sThreadLocal中创建一个与(调用prepare()对应的)线程对应的Looper对象;
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
我们在new Looper的时候还要new 一个MessageQueue,想让工人挑水,总得给个水桶吧,这个MessageQueue就是这个水桶
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
3- new Handler():
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
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());
}
}
//new Handler()运行的线程所对应的Looper对象,也是通过ThreadLocal来管理不同线程的Looper对象
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//取出MessageQueue:此时mLooper都是在线程空间创建的了,更别说Looper的成员变量的 创建空间了,肯定也是在线程空间中
//获取mQueue的目的:在sendMessage时好知道往哪个MessageQueue中丢消息
//在把Message入队到MessageQueue中时,还会把自己作为Message的target,一起放到MessageQueue中
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
和prepare对应的一个操作
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
先得出来一个小结论:Looper 需要在目的线程运行时创建,以便能够与目的线程关联,关联手段就是通过ThreadLocal实现;若需要向目的线程发消息,则Handler对象也应该在目标线程运行时创建,以便能够与目标进程对应的Looper绑定,本质就是持有了目标线程的Looper对象;
4- Looper.loop(): 记得恒小金服一个面试官问我 子线程转换到主线程的切入点是什么?应该就是在这里吧,主线程通过handler发送消息,入队到Handler的mQueue中,该mQueue由Looper创建,通过子线程Looper.loop不断循环,获取由主线程持有的子线程的Handler引用发出来的Message,进而切换到子线程;
可是那个半瓶醋面试官非要说是MessageQueue切换~~~,估计是跟我一样中午没睡醒吧~~
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//取出Looper对应的MessageQueue
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
//第一次执行时,肯定没有消息,所以会阻塞
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
//调用Message对应的Handler,该Handler是和Looper绑定的,也不能乱发Message对不,谁发的谁处理
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
注意:MessageQueue.next()可能会阻塞、也可能会返回null: 阻塞表示没有消息在MessageQueue中了,当有新消息到来时再运行;而返回null表明有一个null的消息,调用MessageQueue的quit方法才会有return null这种操作,即停止loop循环,退出线程。
再瞅一眼Handler发送消息的操作:实质就是:把Message对象放入到目标线程的Looper对象所持有的MessageQueue中去
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
//mQueue从对应的Looper中得到,发消息不能没有目的的发
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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//放到MessageQueue中等待 Looper.loop调用 queue.next来取出Message了
return queue.enqueueMessage(msg, uptimeMillis);
}
总结下如何实现线程切换:
先类比一下:有A和B两个工人,这两个工人之间有一条通信线路,当A工人想要B工人做某件任务时,就会通过通信线路告诉B工人;A和B两个工人通过监听通信线路来检查是不是有新的任务要去做,有的话就记在一个小册子上,然后执行,如果任务太多,小册子就起到一个记录的做作用;
类比到Android的线程通信:工人:Looper+Thread(反正Looper.loop是运行在Thread中)、通信线路:Handler、小册子:MessageQueue、任务:Message;A线程持有通过B线程的Looper创建的Handler(A工人持有通过B工人创建的线路),A线程通过Handler给B线程发消息,即将Message入队到B线程的Looper对应的MessageQueue中,然后运行在B线程的Looper.loop通过循环取出Message进行处理,实现了A线程到B线程的切换。
关键点:B线程暴露了一个Looper给A线程
还有一种操作
就是在Thread的run中创建了Handler,但是该Handler的构造方法中传入的是主线程的Looper,此时我们就可以往主线程中发消息了
public Handler(Looper looper, Callback callback, boolean async) {
//无非就是跟不同的线程的Looper对象关联
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
阅读全文
0 0
- Android基于Handler、Looper、MessageQueue、ThreadLocal的跨线程通信
- Android线程间通信 Handler、Looper、MessageQueue
- Android--Handler+Looper+MessageQueue+Thread线程之间的通信
- Android:Handler+Looper+MessageQueue+Thread(线程间的通信)随记
- Android线程通信机制,handler,looper,messageQueue的关系
- Android笔记--ThreadLocal,Looper,Handler,Message,MessageQueue的关系
- Android 消息机制---Handler,Looper,MessageQueue,ThreadLocal
- Android中线程间通信原理分析:Looper,MessageQueue,Handler
- Android 中线程间通信原理分析:Looper, MessageQueue, Handler
- Android线程通信:Handler,MessageQueue和Looper原理分析
- 关于Android中由 Thread 、Handler、MessageQueue、Looper组成的“线程间通信 套件” 的分析
- Handler、Looper、ThreadLocal、MessageQueue、Message
- Android线程 异步 asynctask Looper MessageQueue handler
- Android线程—Handler、Looper、Message、MessageQueue
- Android的线程间通信Hanlder、Looper、Message、MessageQueue
- android Handler Message MessageQueue Looper ThreadLocal源码解读
- Android Handler Looper MessageQueue
- Android Handler+Looper+MessageQueue
- dubbo配置文件xml校验报错
- ML001.梯度下降(Gradient Descent)
- CSS隐藏一个元素的几种方式及其不同
- linux 下修改 忘记MySQL如何修改
- unix和linux的区别
- Android基于Handler、Looper、MessageQueue、ThreadLocal的跨线程通信
- 深度学习(六十三)空间变换网络
- 单机多broker
- 【LeetCode】287.Find the Duplicate Number解题报告
- CentOS 7.3上图数据库Neo4j的安装和测试
- #4 计算属性
- java 栈的压入、弹出序列
- Vue开源项目库汇总
- == 和 equals,equals 与 hashcode,HashSet 和 HashMap,HashMap 和 Hashtable