最近在学习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标识,那么调用本地接口时就不需要进行权限验证。

然后通过 for(;

Android多线程源码学习笔记一:handler、looper、message、messageQueue的更多相关文章

  1. Adnroid 源码学习笔记:Handler 线程间通讯

    常见的使用Handler线程间通讯: 主线程: Handler handler = new Handler() { @Override public void handleMessage(Messag ...

  2. JUC源码学习笔记5——线程池,FutureTask,Executor框架源码解析

    JUC源码学习笔记5--线程池,FutureTask,Executor框架源码解析 源码基于JDK8 参考了美团技术博客 https://tech.meituan.com/2020/04/02/jav ...

  3. Hadoop源码学习笔记(4) ——Socket到RPC调用

    Hadoop源码学习笔记(4) ——Socket到RPC调用 Hadoop是一个分布式程序,分布在多台机器上运行,事必会涉及到网络编程.那这里如何让网络编程变得简单.透明的呢? 网络编程中,首先我们要 ...

  4. JUC源码学习笔记2——AQS共享和Semaphore,CountDownLatch

    本文主要讲述AQS的共享模式,共享和独占具有类似的套路,所以如果你不清楚AQS的独占的话,可以看我的<JUC源码学习笔记1> 主要参考内容有<Java并发编程的艺术>,< ...

  5. JUC源码学习笔记4——原子类,CAS,Volatile内存屏障,缓存伪共享与UnSafe相关方法

    JUC源码学习笔记4--原子类,CAS,Volatile内存屏障,缓存伪共享与UnSafe相关方法 volatile的原理和内存屏障参考<Java并发编程的艺术> 原子类源码基于JDK8 ...

  6. Underscore.js 源码学习笔记(下)

    上接 Underscore.js 源码学习笔记(上) === 756 行开始 函数部分. var executeBound = function(sourceFunc, boundFunc, cont ...

  7. Underscore.js 源码学习笔记(上)

    版本 Underscore.js 1.9.1 一共 1693 行.注释我就删了,太长了… 整体是一个 (function() {...}());  这样的东西,我们应该知道这是一个 IIFE(立即执行 ...

  8. AXI_LITE源码学习笔记

    AXI_LITE源码学习笔记 1. axi_awready信号的产生 准备接收写地址信号 // Implement axi_awready generation // axi_awready is a ...

  9. Hadoop源码学习笔记(6)——从ls命令一路解剖

    Hadoop源码学习笔记(6) ——从ls命令一路解剖 Hadoop几个模块的程序我们大致有了点了解,现在我们得细看一下这个程序是如何处理命令的. 我们就从原头开始,然后一步步追查. 我们先选中ls命 ...

随机推荐

  1. 符合条件中用where 1=1影响效率以及having和where的区别

    想当初我自己想出来用where 1=1的时候还高兴了一小会,毕竟把代码简化了许多.今天看到的书里面说会影响性能.摘要如下: 低效的“WHERE 1=1” 网上有不少人提出过类似的问题:“看到有人写了W ...

  2. ConnectionState详解

    ConnectionState有六个属性值ConnectionState.Broken;与数据源连接断开.只有在连接打开后才有可能发生这种情况.可以关闭处于这种状态下的连接,然后重新打开.Connec ...

  3. unit vs单元测试

    vs单元测试(unit) 一.什么是单元测试及它的作用? 在小量代码编写时,往往可以通过新建控制台项目(Console Application),新建网站项目(Web Form)等,在其中敲入测试代码 ...

  4. java7大设计原则

    1.单一职责原则(srp) 每一个对象或者类只有一个职责.高内聚.低耦合. 2.开闭原则(ocp) 对扩展开放,对修改关闭.使用性,灵活性,扩展性,稳定性延续性,重复.维护. 3.里氏替换原则(lsp ...

  5. CloudStack网络概念

    转载:http://www.300wl.com/news/2016/0203/206663.html CloudStack网络模式分为简单网络(Basic Networking)和高级网络(Advan ...

  6. Pandas——读取csv,txt文件

    """ 读取csv文件 该文本中的分割符既有空格又有制表符(‘/t’),sep参数用‘/s+’,可以匹配任何空格. """ import p ...

  7. Div的移动

    //JQuery 拖拽本体DIV,把以下代码全部复制即可 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN&q ...

  8. SEVERE: One or more listeners failed to start.

    Full details will be found in the appropriate container log file 错误日志在/home/dela/.IntelliJIdea2017.1 ...

  9. BFS【bzoj1667】: [Usaco2006 Oct]Cows on Skates滑旱冰的奶牛

    1667: [Usaco2006 Oct]Cows on Skates滑旱冰的奶牛 Description 经过跟Farmer John长达数年的谈判,奶牛们终于如愿以偿地得到了想要的旱冰鞋.农场上大 ...

  10. 【NOIP 2009】最优贸易

    描述 C 国有 n 个大城市和 m 条道路,每条道路连接这 n 个城市中的某两个城市.任意两个城市之间最多只有一条道路直接相连.这 m 条道路中有一部分为单向通行的道路,一部分为双向通行的道路,双向通 ...