今天有兴趣研究了一下Android线程之间的通信,主要是通过Hanlder和looper,和messagequene来完成的。先来看一下子线程之间如何通过它们来实现。 子线程之间的通信我在查看了looper的源码最上面的注释中给出了使用的示例如下:
<pre name="code" class="java">** * Class used to run a message loop for a thread. Threads by default do * not have a message loop associated with them; to create one, call * {@link #prepare} in the thread that is to run the loop, and then * {@link #loop} to have it process messages until the loop is stopped. * * <p>Most interaction with a message loop is through the * {@link Handler} class. * * <p>This is a typical example of the implementation of a Looper thread, * using the separation of {@link #prepare} and {@link #loop} to create an * initial Handler to communicate with the Looper. * <pre> * class LooperThread extends Thread { * public Handler mHandler; * public void run() { * Looper.prepare(); * * mHandler = new Handler() { * public void handleMessage(Message msg) { * // process incoming messages here * } * }; * Looper.loop(); * } * }</pre> */
通过对该示例的分析,可知就是在子线程中定义handler对象通过它来发送消息,因此我写了一个小的demo,代码量不大就直接贴上了:
package com.example.testactivity; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.app.Activity; import android.content.Intent; import android.text.TextUtils; import android.util.Log; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.Toast; public class MainActivity extends Activity implements OnClickListener { public Handler handlerTwo; public Handler handlerOne; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); init(); } private void init() { findViewById(R.id.id_btnOne).setOnClickListener(this); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public void onClick(View arg0) { switch (arg0.getId()) { case R.id.id_btnOne: startThreadOne(); startThreadTwo(); break; default: break; } } /*** * 开启第一个Thread * */ private void startThreadOne() { oneThread mOneThread = new oneThread(); mOneThread.start(); } /** * 开启第二个线程 */ private void startThreadTwo() { twoThread mTwoThread = new twoThread(); mTwoThread.start(); } class oneThread extends Thread { @Override public void run() { int j = 0; Looper.prepare(); handlerOne = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (msg.what == 2) { System.out.println("线程1接受到来自线程2消息" + msg.what); } } }; for (int i = 0; i < 5000; i++) { j++; } if (j > 4000) { if (handlerTwo != null) { handlerTwo.sendEmptyMessage(1); } } Looper.loop(); } } class twoThread extends Thread { int m = 0; @Override public void run() { Looper.prepare(); handlerTwo = new Handler() { @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub super.handleMessage(msg); if (msg.what == 1) { System.out.println("线程2收到线程1发来的数据" + msg.what); } } }; for (int i = 0; i < 6000; i++) { m++; } if (m > 5000) { if (handlerOne != null) { <span > </span>handlerOne.sendEmptyMessage(2); } } Looper.loop(); } } }测试结果如下:
可以看出两个线程这之间完成了消息发送,但是之间具体是如何发送的。下面来查看一下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();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
Handler的构造方法比较多有好几个,但最终都调用了这个方法。在这个方法里面先看一下
mLooper = Looper.myLooper();
mCallback = callback
mQueue = mLooper.mQueue
public interface Callback {
public boolean handleMessage(Message msg);
}
这个就是我们new Handler之后重写的handlemessage方法来处理接收到的消息。Handler是通过这几个方法来发送消息的:
<span > public final boolean sendMessage(Message msg)</span>
<span > public final boolean sendEmptyMessage(int what)</span>
<span > public final boolean sendEmptyMessageDelayed(int what, long delayMillis) </span>
<span > public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis)</span>
<span > public final boolean sendMessageDelayed(Message msg, long delayMillis)</span>
<span > public final boolean sendMessageAtFrontOfQueue(Message msg)</span>
<span > public final boolean sendMessageDelayed(Message msg, long delayMillis)</span>
这几个方法最终都调用的是<span style="font-family: Arial, Helvetica, sans-serif;">sendEmptyMessageAtTime(int what, long uptimeMillis)</span>这个方法。该方法的源码如下:
<span ></span><pre name="code" class="java"> <span > </span> public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
<span > </span> MessageQueue queue = mQueue;
<span > </span>if (queue == null) {
<span > </span> RuntimeException e = new RuntimeException(
<span > </span>this + " sendMessageAtTime() called with no mQueue");
<span > </span>Log.w("Looper", e.getMessage(), e);
<span > </span>return false;
<span > </span> }
return enqueueMessage(queue, msg, uptimeMillis);
}
该方法判断了一下之前获取到的消息队列是否存在不存在跑出异常,存在又调用了enqueueMessage(queue, msg, uptimeMillis)这个方法,跟进看一下具体的代码:
<span style="font-family: Arial, Helvetica, sans-serif;"><span ></span></span><pre name="code" class="html"> private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
通过调用参数MessageQuene对象的<span style="font-family: Arial, Helvetica, sans-serif;">enqueueMessage(msg, uptimeMillis);接着跟进去看一下:</span>
<span style="font-family: Arial, Helvetica, sans-serif;"><span ></span></span><pre name="code" class="java"> boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
<span > </span>前面是是一些异常处理之后,<span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;">是获得自身的同步锁synchronized (this),接着这个msg跟MessageQueue实例的头结点Message进行触发时间先后的比较,<span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;">如果触发时间比现有的头结点Message前,则这个新的Message作为整个MessageQueue的头结点,如果阻塞着,则立即唤醒线程处理<span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;">如果触发时间比头结点晚,则按照触发时间先后,在消息队列中间插入这个结点<span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;">接着如果需要唤醒,则调用nativeWake函数,至此Java层的代码结束。我一直在考虑到这儿我们的消息发送到哪儿了呢?且听下回分解</span></span></span></span>
<span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;"><span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;"><span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;"></span></span></span>
</pre><pre name="code" class="java" style="font-size: 18px;">