您的位置:首页 > 其它

源码分析 — Handler机制(线程间通信)

2018-01-02 15:11 260 查看
最近整理以前的文章,上传到CSDN。

一、前言

线程间通讯机制的内部实现原理,即
Handler
Message
MessageQueue
Looper
HandlerThread
AsyncTask
类的实现以及之间的关系。

二、了解相关的几个类:

Handler:负责发送Message和Runnable到MessageQueue中,然后依次处理MessageQueue队列里的消息。

MessageQueue:消息队列。负责存放一个线程的Message和Runnable的集合。

Message:消息实体类。

Looper:消息轮询器。负责将MessageQueue中的Message或Runnable循环取出来,然后分发到Handler中。

小结:

这四者的关系:

一个线程可以有多个Handler实例,

一个线程只能有一个Looper和MessageQueue,

一个MessageQueue对应多个Message和Runnable。

一对多的关系:

一方:Looper和MessageQueue

多方:Handler、Message和Runnable

三、主线程和子线程之间的通信

3.1 Handler类

当实例化一个Handler对象时,就完成了Handler实例与一个线程和一个消息队列的绑定

/**
* Default constructor associates this handler with the {@link Looper} for the
* current thread.
*
* If this thread does not have a looper, this handler won't be able to receive messages
* so an exception is thrown.
*/
public Handler() {
this(null, false);
}

public Handler(Callback callback, boolean async) {

······
//获取Looper对象
mLooper = Looper.myLooper();
······
//获取消息队列实体
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}


问题: 为什么说一个线程对应一个Looper实例?

分析: 查看Looper.myLooper()源码

//-------------------ThreadLacal类----------------------

// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
/**
* Return the Looper object associated with the current thread.  Returns
* null if the calling thread is not associated with a Looper.
*/
public static Looper myLooper() {
return sThreadLocal.get();//获得当前线程的Looper实例。
}


3.2 ThreadLocal类

这个类实现了一个线程的本地存储,即每个线程都会有自己的内存空间来存放线程自己的值。

同时,所有的线程都共享一个ThreadLocal对象,但是不同的线程会有对应不同的value,且单独修改某一个线程值时不影响其它线程值,并支持null值。

小结: 每个线程都会存放一个独立的Looper实例,通过ThreadLocal.get()方法,就会获得当前线程的Looper实例。

问题: Handler是如何发生Runnable的呢?

分析:

//-------------------Handler类-------------------
public final boolean post(Runnable r) {
return  sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;  //将Runnable对象当作msg中的callback属性传入;
return m;
}


小结:其实传入的Runnable对象都是封装到Message类中。

问题: 那么Message中存放哪些信息呢?

分析: Message.obtain()是获取断掉的Message链关系的第一个Message。

1. Message.obtain()是通过从全局Message sPool中读取一个Message,回收的时候也是将Message放入到mPool中。

2. Message中实现了Parcelable接口。

public final class Message implements Parcelable {
public int what;
public int arg1;
public int arg2;
public Object obj;
public Messenger replyTo;
static final int FLAG_IN_USE = 1 << 0;
static final int FLAG_ASYNCHRONOUS = 1 << 1;
static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;
int flags;
long when;  //向Handler发送Message生成的时间
Bundle data;   //在Bundler对象上绑定要在线程中传递的数据
Handler target;   //处理当前Message的Handler对象
Runnable callback;
Message next;   //当前Message对下一个Message的引用
private static final Object sPoolSync = new Object();
private static Message sPool;   //有下一个Message引用的Message链(Message池)
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
sPoolSize--;
return m;
}
}
return new Message();
}
}


问题: Handler是如何发生Message的呢?

分析:

//-------------------------Handler类----------------------------

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
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) {
// this代表Handler
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}


小结: 当按时间发送时,无论是发送Message还是Runnable,最终调用的都是sendMessageAtTime()方法,里面的核心方法是enqueueMessage()方法,该方法就是调用MessageQueue中的enqueueMessage()方法,其实就是将Message加入到消息队列中。

问题: 如果发送消息只是将消息加入到消息队列中,那谁来把消息分发到Handler中呢?

分析:

//------------------------Looper类---------------------------
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
//获取一个Looper对象
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//获取Looper里的消息队列
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);
}

//消息分发 handler.dispatchMessage(msg),将消息从轮询器中取出发送到对应的handler中
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.recycle();//释放消息占据的资源
}
}

//----------------------Handler类---------------------------
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
//msg.callback其实就是传入的Runnable对象;
if (msg.callback != null) {
//如果handler传入的值是Runnable,则直接执行callback回调
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//Callback接口,具体业务处理在这里执行
handleMessage(msg);
}
}


小结:

在Looper类的loop()方法中调用Handler的dispatchMessage(msg)方法,在dispatchMessage(msg)内部最终调用了handlerMessage(msg)。

换句话说,Looper.loop()方法就是取得当前线程中的MessageQueue实例,然后不断循环获取消息,并将消息分发到对应的Handler实例上。

实质上只要调用Looper.loop()方法,就可以执行消息分发。

四、Handler、Message、MessageQueue、Looper的关系原理图:



整个机制实现原理流程:

当应用程序运行时,会创建一个Linux进程和一个UI线程(ActivityThread),这个类里有一个main方法,是Java程序运行最开始的入口。

//-----------------------ActivityThread类---------------------------
public static void main(String[] args) {
SamplingProfilerIntegration.start();

// CloseGuard defaults to true and can be quite spammy.  We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);

Process.setArgV0("<pre‐initialized>");

//由framework设置的UI程序的主消息循环,注意,这个主消息循环是不会主动退出的
Looper.prepareMainLooper();
if (sMainThreadHandler == null) {
sMainThreadHandler = new Handler();
}
//创建主线程
ActivityThread thread = new ActivityThread();
thread.attach(false);

if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
//获取MessageQueue实例,执行消息分发
Looper.loop()

throw new RuntimeException("Main thread loop unexpectedly exited");
}

ActivityThread() {
...省略代码...
}


小结: UI线程一执行就已经调用了loop消息分发,所以当在UI线程中通过Handler对象发送消息或者任务时,会把Message加入到MessageQueue消息队列中,然后分发到Handler的handlerMessage方法里。

五、子线程之间的线程通信

问题: 如何实现子线程之间的线程通信?

分析: 其实就是在子线程中实现handler.handleMessage()方法的调用。

//----------------------Thread类----------------------------
public Thread(String threadName) {
if (threadName == null) {
throw new NullPointerException("threadName == null");
}
//创建一个子线程
create(null, null, threadName, 0);
}

//--------------------------HandlerThread类-------------------------------
/**
* Handy class for starting a new thread that has a looper. The looper can then be
* used to create handler classes. Note that start() must still be called.
*/
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
public HandlerThread(String name) {
super(name); //调用Thread(String threadName)
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}

/**
* Constructs a HandlerThread.
* @param name
* @param priority The priority to run the thread at. The value supplied must be from
* {@link android.os.Process} and not from java.lang.Thread.
*/
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}

/**
* 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();

//Initialize the current thread as a looper.
Looper.prepare();
synchronized (this) {
//实例化Looper对象
mLooper = Looper.myLooper();
notifyAll();//Object类中的本地方法(native)
}
Process.setThreadPriority(mPriority);
onLooperPrepared();

//内部实现消息分发功能
Looper.loop();
mTid = -1;
}

/**
* 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;
}
/**
* Quits the handler thread's looper.
* @return True if the looper looper has been asked to quit or false if the
* thread had not yet started running.
* @see #quitSafely
*/
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
/**
* Quits the handler thread's looper safely.
* @return True if the looper looper has been asked to quit or false if the
* thread had not yet started running.
*/
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
/**
* Returns the identifier of this thread. See Process.myTid().
*/
public int getThreadId() {
return mTid;
}
}


小结: 通过查看HandlerThread源码,发现HandlerThread继承自Thread类,从run()方法中可以发现,HandlerThread要调用start()方法,才能实例化HandlerThread的Looper对象和实现消息分发功能。

所以,要使用HandlerThread,必须先运行HandlerThread,这样才能取出对应的Looper对象,然后使用Handler(Looper)构造方法实例化Handler(即实现Handler和Looper的关联),这时handler就可以再子线程中执行handlerMessage()方法了。

HandlerThread类

自身继承Thread,是一个子线程;

与Handler+Message+Thread的区别:HandlerThread类里面处理Message消息可以是耗时操作,但是不能对UI操作,即handleMessage()方法只能在子线程中执行;

例子:

public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

HandlerThread mThread = new HandlerThread("myThread");
//先调用start(启动一个线程
mThread.start();
//调用Handler(Looper)方法实例化一个Handler对象
myHandler myHandler = new myHandler(mThread.getLooper());
Message msg = myHandler.obtainMessage();
//把Message发送到目标对象,目标对象就是生成msg的目标对象。
msg.sendToTarget();  //实际上方法内部调用handler.sendMessage(msg);
}

class myHandler extends Handler {
//必须调用Handler(Looper looper)方法
public myHandler(Looper looper) {
super(looper);
}
public void handleMessage(Message msg) {
Log.e("这是新线程", ">>>>>>>新线程的测试");
}
}


六、自己实现一个可以处理消息的子线程:(模仿HandlerThread)

自定义一个线程类继承Thread类;

重写run()方法,并在该方法内部实现以下三个方法

Looper.prepare(); //将Looper设置到这个线程中

Looper mLooper = Looper.myLooper();

Looper.loop(); //开启消息循环

在另一个类中创建出该线程类对象,并启动线程;

创建一个Handler类进行线程和Looper绑定;

发送消息;

处理消息;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: