Android下的多线程通信机制
来源:互联网 发布:开源淘宝客 导入 编辑:程序博客网 时间:2024/04/27 20:37
Android下的多线程通信机制
这几天一直在做和android 多线程之间通信相关的工作,UI线程和子线程之间进行通信以及使用子线程进行UI界面更新,也阅读了Looper、Handler、Message这几个类的源码,同时也参考网上一些博客,有一些使用心得,特此记录下来。
其实,在我的android程序中进行通信的只有三个线程,暂且叫UI线程,work线程和Log捕获线程吧。Log捕获线程用于抓取Activity Manager的Log信息并发送信息到UI线程,work线程接收UI线程发送的消息并执行具体的工作。
android多线程之间的通信是通过线程之间的消息机制驱动的,android的消息处理机制有四大核心类:Looper, Handler, Message, Message Queue。当然我们在使用时几乎不会和Message Queue直接打交道,但它也是组成android消息处理机制不可或缺的一部分。
下面通过源码来看android消息处理机制的实现过程。
Looper的奇妙
从面上看Looper是“循环者“的意思,Looper被设计把一个普通的线程变成一个Looper线程即循环工作的线程。通常我们需要循环工作的线程,不断的等待新任务的出现并执行。创建一个Looper线程很简单,代码如下:
public class looperThread extends Thread{
@Override
public void run() {
/*将当前线程初始化为Looper线程*/
Looper.prepare();
//... 其他work
/*开始循环处理消息队列*/
Looper.loop();
}
}
通过上面两行核心代码,我们的线程就变成了Looper线程,如此神奇,下面我们看看Looper源码是如何实现的。
1) Looper.prepare();
public final class Looper {
// 每个线程中的Looper对象都是一个ThreadLocal,线程的本地存储变量.
//这是创建Looper对象的核心,下面获取的是每个线程的Looper本地变量
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class
final MessageQueue mQueue; //消息队列
final Thread mThread; //当前线程
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
public static void prepare() {
prepare(true);
}
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));
}
/**
* Return the {@link MessageQueue} object associated with the current
* thread. This must be called from a thread running a Looper, or a
* NullPointerException will be thrown.
*/
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed); //Looper线程的消息队列
mThread = Thread.currentThread(); //Looper所属的线程
}
通过上面的源码,Looper.prepara();在线程的本地存储变量中创建Looper对象,并且只创建一个Looper对象。
2) Looper.loop();
当线程调用该方法之后,Looper线程开始真正的工作,不断的从自己的Message Queue中取出消息执行,源码如下:
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
final Looper me = myLooper(); //获取当前线程的Looper对象
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue; //获取当前Looper的Message Queue
//进入循环,取出消息 执行消息
for (;;) {
Message msg = queue.next(); // 取出message
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
........
//关键点所在
msg.target.dispatchMessage(msg); //分派给Message实例关联的target去执行
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();
}
}
通过以上我们可以得出一下结论:
1. 每个线程只能有一个Looper实例, 因为它是一个ThreadLocal变量。
2. 通过调用Looper.prepare()方法,使一个普通线程摇身一变成为Looper线程
3. Looper内部有一个MessageQueue实例, 通过调用Looper.loop()方法,线程开始不断的从消息队列取出消息并执行。
那么, Looper和MessageQueue和当前的线程都已经关联起来了,是怎么发送消息到到MessageQueue的,消息的发送和处理是怎样的,这就是Handler的工作。
Handler、Message、Handler之间的关系为:
Looper中有一个消息队列Message,里面存储的一个个待处理的消息
Message中有一个Handler,用来处理Message
Handler中的Message指向Looper中的Message
Handler消息的异步处理者
在整个的android消息处理机制中,handler是很重要的,handler负责发送消息和处理自己发送的消息,即是handler将消息送到消息队列(MessageQueue), 当前线程的Handler实例发送消息到自己的关联的Looper线程,然后Handler在自己关联的线程中去处理其他线程发送的消息,整个过程是异步实现的。
首先,我们得知道,即使没有Handler,我们仍然可以实现将消息发送到Message队列
因为,Looper中包含Message实例,可以实例化一个Message去发送消息,但是会比较麻烦,Handler出现,封装消息,将消息插入到消息队列都变的简单多了。
下面看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());
}
}
mLooper = Looper.myLooper(); //关联当前线程的Looper
//Looper不能为空
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue; //关联Looper的MessageQueue.
mCallback = callback;
mAsynchronous = async;
}
一个线程可以有多个Handler,但是一个线程只能有一个Looper。
HandlerThread Looper线程
HandlerThread 解决了Handler和Looper之间的同步问题
HandlerThread继承Thread,当我们创建一个HandlerThread实例时,就创建了一个Looper线程
源码如下:
/**
* Call back method that can be explicitly overridden if needed to execute some
* setup before Looper loops.
*/
protected void onLooperPrepared() {
}
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
在run方法中通过Looper.prepare() 和Looper.loop() 两个方法将此线程转化为Looper线程,这样该线程就拥有自己的消息队列,通过Handler就可以和其他线程进行异步通信。
HandlerThread也提供了获取关联当前线程的方法:
/**
* This method returns the Looper associated with this thread. If this thread not been started
* or for any reason is isAlive() returns false, this method will return null. If this thread
* has been started, this method will block until the looper has been initialized.
* @return The looper.
*/
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
为什么会存在Handler和Looper之间的线程同步问题呢?
因为Android Handler机制本来就是用于解决线程之间通信的问题。例如:
public LooperThread extends Thread {
public Looper myLooper = null;
//假设LooperThread为线程2
public void run() {
Looper.prepare();
myLooper = Looper.myLooper();
Looper.loop();
}
}
在线程1 中获取线程2 的myLooper;
{
LooperThread lpThread = new LooperThread();
lpThread.start(); //启动线程2
Looper looper = lpThread.myLooper(); //get the Looper of thread 2
Handler handler = new Handler(looper);
}
很明显,红色标志的代码是有问题的,因为looper可能为空。为什么?
原因在于在线程1 在获取线程2的myLooper,首先的得保证线程2的myLooper不为空。
所以,HandlerThread的上诉代码就好理解了,如果新线程还没有创建Looepr对象,则等待
然后notifyAll,表明已经获取到新线程的Looper对象。
使用HandlerThread的方法:
HandlerThread handlerThread = new HanlerThread(“Handler”);
handlerThread.start();
以上两行就创建并启动了一个HandlerTHread。
但是,想让此线程循环的工作,还应该绑定Handler实例。
Handler handler = new Handler(handlerThread.getLooper(), handlerThread );
Handler的构造函数较多,根据自己的需要选择构造Handler实例
- Android下的多线程通信机制
- Android多线程通信机制
- linux下的多线程/多进程同步/通信机制
- Android下网络通信机制
- linux 下基于特定通信协议利用多线程同步通信机制实现的串口通信
- linux 下基于特定通信协议利用多线程同步通信机制实现的串口通信
- 关于android 下activity间通信的问题-广播机制
- Android下的两种http通信机制介绍
- Chromium多线程通信的Closure机制分析
- Java的多线程机制下
- Java多线程通信机制
- Java多线程通信机制
- Java多线程通信机制
- Java多线程通信机制
- Android通信的Handler机制
- Android的Binder通信机制
- 多线程下的Socket网络通信
- Android开发知识(四)Android进程间Binder通信机制的源码分析(下)
- PHP【求两个经纬度之间的距离】
- Android多渠道打包
- 基于OpenCV的双目测距系统实现
- 虚拟机中的锁优化简介(适应性自旋/锁粗化/锁削除/轻量级锁/偏向锁)
- Java中构造方法、类方法、final方法的重载与覆盖问题
- Android下的多线程通信机制
- SignalR+AForge实现视频会话[WPF]
- Handler+viewPager实现图片轮播
- 判断浏览器类型
- 欢迎使用CSDN-markdown编辑器
- lvs、haproxy、nginx 负载均衡的比较分析
- jdbc连接数据库使用sid和service_name的区别
- 技巧: 使用truss、strace或ltrace诊断软件的"疑难杂症"
- ReactiveCocoa框架菜鸟入门(四)——信号(Signal)详解