网络中关于Android 异步实现原理,有很多,在这我只写下我对异步线程的理解,如果有幸,给初学Android 的菜鸟们一些帮助,那也很庆幸,如果Android 大神们看了 后,认为有问题,也希望在评论中写出来给一些意见,大家互相学习
Android中的异步线程实现原理如下图所示
在内部有一个或者多个Handler,在外部通过Handler对象,向异步线程发送消息。消息经过Handler传递到MessageQueue对象中,线程内部只包含一个MessageQueue对象
线程主执行函数从这个消息队列中取出消息并且回调给Handler 的handlemessage(),方法
下面是Handler Looper MessageQueue 的调用方法
程序员通过Looper类的静态方法prepare()为线程创建MessageQueue对象 代码如下
public static final void prepare(){
if(sThreadLocal.get() != null ){
throw new RuntimeException( " only one Looper maybe created per thread ");
}
sThreadLocal .set( new Looper() );
}
在上一段代码中 变量sThreadLocal 类型是 ThreadLocal 改变量的作用是提供 线程局部存储 : 什么事线程局部存储呢
变量常见作用域一般遇见的有下面几种
函数类内的局部变量、 其作用域是该函数完成
类内部变量:也就是我们锁说的字段,作用域是改类产生的对象
类内部静态变量:其作用域在整个进程中,无论改对象创建多少次,这个变量只有一个赋值,并且一直保存着,因为对于静态变量而言,无论从哪个进程中的那个线程而言,引用该变量,他的值始终是相同的,因为编译器内部为静态变量分配了专门的存储空间,然而我们有时候希望在同一个线程中引用同一个变量时候其值总是相同的,在不同线程中引用该变量时候其值是不同的,也就是我们要一个变量作用域在线程当中 :而ThreadLocal 就能够提供者样功能的一个类。
在Looper中的ThreadLocal 是在进程第一次调用prepare方法时候 赋值的,之后其线程调用这个变量时候已经被赋值了。而ThreadLocall 内部会更具调用prepare的id保存一个数据对象,这个对象就是我们所说的线程局部对象,同过ThreadLocal的set 方法设置进去的,而Looper 中保存的这个对象就是Looper对象。从上面可以看出。始终只有一个Looper对象 为什么会这样呢
因为每个Looper都会定义一个MessageQueue 对象,一个异步线程只能有一个消息队列,所以只能有一个Looper对象,而程序员可以用ThreadLocal 保存任何数据类型
这也是ThreadLocal 对象是一个模板类的原因
Looper 对象
Looper 类有两个作用
第一是调用prepare方法创建消息队列
第二是提供静态函数loop ,使调用该函数的线程无线循环,并且从消息队列中取出消息
先看一下looper 构造函数
Looper () {
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread() ;
}
对于程序员来说,当要把一个线程变为异步消息线程时候应该在线程中的run函数中先调用Looper。prepare() 方法为改线程创建一个消息队列。,然后调用loop函数,使当前线程进入消息循环
调用myLooper函数返回 当前线程的looper 对象 在该函数内部 仅仅通过sThreadLocal 的get方法返回当前线程id 所对应的Looper 对象
进入循环 通过queue。next 活动下一个消息注意 如果消息为空那么线程会被挂起
调用dispatchMessage 函数对消息进行分发处理,也就是说消息的具体处理是交给程序自己指定的 msg 就是Message msg。target 就是handler 对象
当处理完消息后 调用 msg.recycle(); 对消息中的资源进行释放
MessageQueue
主要执行取出消息 《 next 》和 添加消息 enquenceMessage()
消息队列采用排队的形式对消息就行处理,即先到的先处理,队列中的消息以链表方式进行存储 其中next 变量指向下一个消息
来看一下next 函数
1 调用nativePollOnce(mptr ,time) 这是一个jni 函数其作用是从消息队列中取出消息 MessageQueue 本身没有保存消息队列 ,真正的消息队列保存在c 代码中
如果没有消息,那么将会被挂起 ,如果有消息将消息传送给JAVA 的mMessage 变量
2 接下来的同步方法中判断消息执行的时间是否到了,如果没到将尝试读取下一条消息 ,如果到返回消息并且将mmessage 变量制空
如果没有消息了 那么执行制空回调函数
enquenceMessage 分两步
1 msg 赋值给mMessage
2 调用nativeWake( mptr ) 这是一个jni 韩式,将消息添加到c 的消息队列中,如果消息当前状态是挂起状态泽唤醒
Handler
一般messageQueue 队列提供了直接读写的函数接口,但是对于应用程序而言,一般不直接读写消息队列
程序一般使用Handler类像消息队列发送消息并且重载 Handler 的handlemessage () 函数添加消息处理代码
Handler 只能把消息添加到消息对咧中否则会发生异常,因此在执行handler之前 先调用Looper.prepare () ,不过在Android activity 中主线程已经调用了Looper ,prepare()