Windows编程的朋友可能知道Windows程序是消息驱动的,并且有全局的消息循环系统。而Android应用程序也是消息驱动的,按道理来说也应该提供消息循环机制。Android通过Looper、Handler来实现消息循环机制,Android消息循环是针对线程的(每个线程都可以有自己的消息队列和消息循环)。
在 Android 系统 ,这些工作由由由Looper 及 Handler 来完成。
先分析Looper类:
主要提供负责线程的消息循环和消息队列
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(); // 进入消息循环
}
}
下面Looper类的准备函数:
private static final ThreadLocal sThreadLocal = new ThreadLocal();
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
这里利用 ThreadLocal 线程局部存储变量将将Looper与调用线程关联起来。
而消息处理流程如何呢?
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static final void loop() {
Looper me = myLooper(); // 线程中存储的Looper对象
MessageQueue queue = me.mQueue; // Looper类中的消息队列
while (true) {
Message msg = queue.next(); // might block 获取消息
//if (!me.mRun) {
// break;
//}
if (msg != null) {
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
}
if (me.mLogging!= null) me.mLogging.println(
">>>>> Dispatching to " + msg.target + " "
+ msg.callback + ": " + msg.what
);
msg.target.dispatchMessage(msg); // 利用 Target 注册的方法处理消息
if (me.mLogging!= null) me.mLogging.println(
"<<<<< Finished to " + msg.target + " "
+ msg.callback);
msg.recycle();
}
}
}
可以通过Loop.myLooper()得到当前线程的Looper对象,通过Loop.getMainLooper()可以获得当前进程的主线程的Looper对象。
在android系统,UI线程就是消息的主线程,在 ActivityThread.java 中创建:
public static final void main(String[] args) {
Looper.prepareMainLooper();
Looper.loop(); //消息循环处理
}
-->
public static final void prepareMainLooper() {
prepare();
setMainLooper(myLooper());
if (Process.supportsProcesses()) {
myLooper().mQueue.mQuitAllowed = false;
}
}
private synchronized static void setMainLooper(Looper looper) {
mMainLooper = looper;
}
再来分析 Handler 类:
主要提供负责将消息加入到特定的Looper消息队列中,并分发和处理访消息队列中的消息,构造Handler的时候可以指定一个Looper对象,如果不指定则利用当前线程的Looper创建。
请看如下代码即可明白:
public Handler(Looper looper, Callback callback) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
}
这里也同时告诉我们将消息放入到主线程的消息队列中,只需要创建Hanlde对象时以主线程的Looper创建即可。则sendMessage及handleMessage都会在主线程中进行处理。
再看如下变量:
final MessageQueue mQueue;
final Looper mLooper;
final Callback mCallback;
这里也有 队列,Looper对象及回调函数类,通过 Handler 不同构造函数完成相应的操作。
简化使用队列及消息传递的复杂性,提供方便的调用方法。其中最重要的函数是:
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg); // 1、利用 Calback 函数处理消息
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) { // 2、利用mCallback处理消息
return;
}
}
handleMessage(msg); // 3、利用子类处理消息,这里最常用的方法,直接重载handleMessage函数
}
}
那么这几者的关系是怎么样的呢?
一个Activity中可以创建多个工作线程或者其他的组件,如果这些线程或者组件把他们的消息放入Activity的主线程消息队列,那么该消息就会在主线程中处理了。
还有一个问题就是Looper和Handler的同步关系如何处理,在android由HandlerThread类进行解决了。
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(); // 如果新线程还未创建Looper对象,则等待
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
新线程创建运行run函数创建Looper对象:
public void run() {
mTid = Process.myTid();
Looper.prepare();// 这里会创建程的Looper对象
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll(); // ok,创建好了则通知,最后调用Looper.loop()进入消息循环
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
这里利用 notifyAll / wait 轻检解决此问题了,所以多多使用用HanlderThread类完成多线程同步问题吧。
培训时学员提出的疑问进行补充说明:
每个线程都可以有自己的消息队列和消息循环,一个线程可以存在(当然也可以不存在)一个消息队列和一个消息循环(Looper),特定线程的消息只能分发给本线程,不能进行跨线程,跨进程通讯。
那么除了UI thread(ActivityThread main Looper线程)工作如何运行的呢?
可以参考下 AsyncQueryHandler.java 实现:
private Handler mWorkerThreadHandler;
// 工作线程,处理消息,减轻UI thread负担
protected class WorkerHandler extends Handler {
public WorkerHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
...
}
}
//1、构造Looper消息处理线程HandlerThread
public AsyncQueryHandler(ContentResolver cr) {
super();
mResolver = new WeakReference<ContentResolver>(cr);
synchronized (AsyncQueryHandler.class) {
if (sLooper == null) {
HandlerThread thread = new HandlerThread("AsyncQueryWorker");
// 这里将会调用到HandlerThread.run()进入到Looper.loop();循环消息处理中
thread.start();
//2、利用thread.getLooper()获取自已的Looper
sLooper = thread.getLooper();
}
}
//3、并设定给WorkerHandler进行构造绑定其Looper对象
mWorkerThreadHandler = createHandler(sLooper);
}
protected Handler createHandler(Looper looper) {
return new WorkerHandler(looper);
}
//4 利用mWorkerThreadHandler(Handler对象)发送消息
public void startQuery(int token,...){
Message msg = mWorkerThreadHandler.obtainMessage(token);
msg.arg1 = EVENT_ARG_QUERY;
...
msg.obj = args;
mWorkerThreadHandler.sendMessage(msg);
}
内容补充:
Android的消息循环是针对线程的,每个线程都可以有自己的消息队列和消息循环。
Android系统中的Looper负责管理线程的消息队列和消息循环。通过Looper.myLooper()得到当前线程的Looper对象,通过Looper.getMainLooper()得到当前进程的主线程的Looper对象。
消息队列(MessageQueue)和消息循环(Looper),但是我们看到每个消息处理的地方都有Handler的存在,它是做什么的呢?
Handler的作用是把消息加入特定的Looper所管理的消息队列中,并分发和处理该消息队列中的消息。
构造Handler的时候可以指定一个Looper对象,如果不指定则利用当前线程的Looper对象创建。
多个子线程访问主线程的Handler对象,Handler对象管理的Looper对象是线程安全的,不管是添加消息到消息队列还是从消息队列中读取消息都是同步保护的,所以不会出现数据不一致现象。