Android多线程源码学习笔记一:handler、looper、message、messageQueue
最近在学习Android多线程相关知识的源码,现在把自己的笔记整理一下,写出来加深印象。
Android多线程通讯的核心是handler、looper、message、messageQueue,这篇文章就先记录下这套系统的源码要点,具体的实现方法下一篇文章再写。
内容为自己看源码的理解,如有问题,欢迎留言探讨,共同进步。
Thread
用法一:
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1:
mThread.setText(msg.obj.toString());
}
}
};
...
new Thread(new Runnable() {
@Override
public void run() {
Log.d("coco", "thread:" + Thread.currentThread().getName());
Message message = handler.obtainMessage();
message.obj = "thread_msg";
message.what = 1;
handler.sendMessage(message);
}
}).start();
主线程中初始化handler,实现handleMessage,子线程中sendMessage,实现通讯。(ps:handler内存泄漏后面写)
方法二:
handler.post(new Runnable() {
@Override
public void run() {
Message message = Message.obtain(handler);
message.obj = "thread_msg1";
message.what = 1;
handler.sendMessage(message);
}
});
这种方法跟第一种实现原理是一样的,直接返回sendMessageDelayed(getPostMessage(r), 0),通过getPostMessage从Runnable中获取message,然后放到messageQueue中。
handler
handler是多线程通讯的控制器,负责消息的发送与处理,handler的初始化代码如下:
//FIND_POTENTIAL_LEAKS为常量,值为false,即第一个if语句不会执行。内部的代码逻辑是判断handler的创建方式,决定是否需要打
//印内存泄漏的log,如果是该handler对象是通过匿名类、成员类、内部类、非静态类的话,有可能造成内存泄漏,需要打印log
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());
}
}
//首先对looper进行判空,如果为空就抛出异常,所以如果在子线程中初始化handler,一定要先初始化looper,主线程在系统创建时就初
//始化了looper,所以可以直接创建handler。
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
mQueue是获取的mLooper的mQueue,所以mQueue也是当前线程相关的,具体原因在looper的源码分析中会讲。mAsynchronous是判断是否有异步消息,Android会优先处理异步消息,具体的实现在messageQueue中会讲到。
public final Message obtainMessage()
{
return Message.obtain(this);
}
obtainMessage方法是从message的公共池中取出一个message,相对于直接new出来,效率更高。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
dispatchMessage方法是handler在接收到message后进行分发时调用的,msg.callback是一个Runnable对象,在message创建时传入,或者通过setCallback方法设置,默认为空;mCallback是Callback对象,在handler初始化的时候传入,默认也为空。所以没有特定设置的情况下,会直接走到handlerMessage中,即我们创建handler时复写的回调方法。
looper
looper的主要成员变量如下:
MessageQueue mQueue跟looper绑定的消息队列。
Thread mThreadlooper所在线程对象。
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));
}
sThreadLocal的数据结构为ThreadLocal,在prepare中首先判断sThreadLocal是否为空,表明一个线程只能有一个looper对象,符合单例模式的设计思想。
sThreadLocal.set(new Looper(quitAllowed))该方法是new一个Looper,并将该Looper与当前线程的threadLocalMap关联起来,所以该looper属于调用prepare方法的线程。
接下来是最重要的loop方法,loop与prepare方法都是静态方法,通过Looper.prepare跟Looper.loop调用即可,所以在loop开始的时候要先获取当前thread的looper与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.");
}
final MessageQueue queue = me.mQueue;
Binder.clearCallingIdentity();
...
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
return;
}
...
try {
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...
msg.recycleUnchecked();
}
}
Binder.clearCallingIdentity() 方法是为了清除当前线程传入的IPC标识,如果在其他线程传入IPC请求,当前线程又要调用当前线程的本地接口,先清除传入的IPC标识,那么调用本地接口时就不需要进行权限验证。 常见的使用Handler线程间通讯: 主线程: Handler handler = new Handler() { @Override public void handleMessage(Messag ... JUC源码学习笔记5--线程池,FutureTask,Executor框架源码解析 源码基于JDK8 参考了美团技术博客 https://tech.meituan.com/2020/04/02/jav ... Hadoop源码学习笔记(4) ——Socket到RPC调用 Hadoop是一个分布式程序,分布在多台机器上运行,事必会涉及到网络编程.那这里如何让网络编程变得简单.透明的呢? 网络编程中,首先我们要 ... 本文主要讲述AQS的共享模式,共享和独占具有类似的套路,所以如果你不清楚AQS的独占的话,可以看我的<JUC源码学习笔记1> 主要参考内容有<Java并发编程的艺术>,< ... JUC源码学习笔记4--原子类,CAS,Volatile内存屏障,缓存伪共享与UnSafe相关方法 volatile的原理和内存屏障参考<Java并发编程的艺术> 原子类源码基于JDK8 ... 上接 Underscore.js 源码学习笔记(上) === 756 行开始 函数部分. var executeBound = function(sourceFunc, boundFunc, cont ... 版本 Underscore.js 1.9.1 一共 1693 行.注释我就删了,太长了… 整体是一个 (function() {...}()); 这样的东西,我们应该知道这是一个 IIFE(立即执行 ... AXI_LITE源码学习笔记 1. axi_awready信号的产生 准备接收写地址信号 // Implement axi_awready generation // axi_awready is a ... Hadoop源码学习笔记(6) ——从ls命令一路解剖 Hadoop几个模块的程序我们大致有了点了解,现在我们得细看一下这个程序是如何处理命令的. 我们就从原头开始,然后一步步追查. 我们先选中ls命 ... 国内物联网平台(3)——QQ物联·智能硬件开放平台 马智 平台定位 将QQ帐号体系.好友关系链.QQ消息通道及音视频服务等核心能力提供给可穿戴设备.智能家居.智能车载.传统硬件等领域的合作伙伴,实现用 ... 一,制作winform 窗体 窗体要做小一点,你见过500*500的悬浮框吗? 二,去掉边框和标题栏 this.FormBorderStyle = FormBorderStyle.None; 运行出来 ... 在程序窗口上使用鼠标画图 private Point pStart, pEnd; private bool isAllowDraw = false; private bool isOpenPen = ... 1.写一个简单小程序 #include<stdio.h> #include<stdlib.h> int main(int argc,char **argv) { while(1 ... 题面 传送门 题解 为啥全世界除了我都会\(exLucas\)啊--然而我连中国剩余定理都不会orz 不知道\(exLucas\)是什么的可以去看看yx巨巨的这篇博客->这里 好了现在我们就解决 ... web安全问题 cookie 1.cookies只能设置过期 不能删除 <script> now.toGMTString() => 事件可以用来设置cookie document.c ... \(\color{#0066ff}{ 题目描述 }\) 对Samuel星球的探险已经取得了非常巨大的成就,于是科学家们将目光投向了Samuel星球所在的星系--一个巨大的由千百万星球构成的Samuel ... [中山市选]杀人游戏 Tarjan+概率 题目描述 一位冷血的杀手潜入\(Na\)-\(wiat\),并假装成平民.警察希望能在\(N\)个人里面,查出谁是杀手.警察能够对每一个人进行查证,假如查 ... public static void main(String[] args) { String ss= "[{\"type\":\"0\",\&quo ... <!doctype html> <html> <head> <style id="mceDefaultStyles" type=" ...
然后通过 for(;
Android多线程源码学习笔记一:handler、looper、message、messageQueue的更多相关文章
随机推荐