Android应用程序消息处理机制及线程消息循环模型分析

本文介绍了Android应用程序的消息处理机制,包括线程消息队列的创建、消息循环过程以及消息发送和处理。通过MessageQueue、Looper和Handler三个类的交互,阐述了主线程和子线程如何进行消息通信,详细分析了消息的发送、循环和处理流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

学习了罗大神的Android应用程序的消息处理机制
简单做个笔记,代码来自大神的讲解

Android应用程序的消息处理机制是围绕消息队列来实现的,
一个线程拥有了消息队列之后,就可以进入到消息循环中,同时其他线程以及线程本身可以往这个消息队列发送消息。
Android系统主要通过MessageQueue、Looper、和Handler三个类来实现Android应用程序的消息处理机制,
其中,MessageQueue用来描述消息队列;Looper类用来创建消息队列,以及进入消息循环;Handler类用来自发送消息和处理消息。

1.创建线程消息队列

调用Looper类的静态成员函数prepareMainLooper或者prepare来创建,
其中前者用来为应用程序的主线程创建消息队列,后者用来为应用程序的其他子线程创建消息队列。
一个Looper对象在创建的过程中,会在内部创建一个MessageQueue对象,并且保存在它的成员变量mQueue中

代码如下:


public class Looper {

private static final ThreadLocal sThreadLocal = new ThreadLocal();  <<保存线程的Looper对象

final MessageQueue mQueue;

......

/** 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 final void prepare() {
	if (sThreadLocal.get() != null) {     <<检查当前线程是否已经有一个Looper对象
		throw new RuntimeException("Only one Looper may be created per thread");
	}
	sThreadLocal.set(new Looper());   <<创建一个Looper对象,并保存在sThreadLocal中
}

/** Initialize the current thread as a looper, marking it as an application's main 
*  looper. The main looper for your application is created by the Android environment,
*  so you should never need to call this function yourself.
* {@link #prepare()}
*/

public static final void prepareMainLooper() {
	prepare();
	setMainLooper(myLooper());
	if (Process.supportsProcesses()) {
		myLooper().mQueue.mQuitAllowed = false;
	}
}

private synchronized static void setMainLooper(Looper looper) {
	mMainLooper = looper;
}

/**
* Return the Looper object associated with the current thread.  Returns
* null if the calling thread is not associated with a Looper.
*/
public static final Looper myLooper() {
	return (Looper)sThreadLocal.get();
}

private Looper() {
	mQueue = new MessageQueue();   <<创建一个MessageQueue对象
	mRun = true;
	mThread = Thread.currentThread();
}

}

一个MessageQueue对象在创建过程中,又会在c++层中创建一个NativeMessageQueue对象,代码如下:

public class MessageQueue {

private int mPtr; // used by native code

private native void nativeInit();

MessageQueue() {
	nativeInit();  <<调用此函数创建NativeMessageQueue
}

......

}

它的初始化工作都交给JNI方法nativeInit来实现了,这个JNI方法定义在frameworks/base/core/jni/android_os_MessageQueue.cpp文件中:

static void android_os_MessageQueue_nativeInit(JNIEnv* env, jobject obj) {
    NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
    if (! nativeMessageQueue) {
        jniThrowRuntimeException(env, "Unable to allocate native queue");
        return;
    }
 
    android_os_MessageQueue_setNativeMessageQueue(env, obj, nativeMessageQueue);
}

    在JNI中,也相应地创建了一个消息队列NativeMessageQueue,NativeMessageQueue类也是定义在frameworks/base/core/jni/android_os_MessageQueue.cpp文件中,它的创建过程如下所示:

NativeMessageQueue::NativeMessageQueue() {
    mLooper = Looper::getForThread();
    if (mLooper == NULL) {
        mLooper = new Looper(false);
        Looper::setForThread(mLooper);
    }
}

一个NativeMessageQueue对象在创建的过程中,又会在内部创建一个c++层的Looper对象,代码如下:

2.线程消息的循环过程

消息队列创建完成以后,就调用Looper类的loop函数进入到消息循环中去了,这个过程总共分为7个步骤
step 1:Looper.loop

public class Looper {

public static final void loop() {
	Looper me = myLooper();
	MessageQueue queue = me.mQueue;  //获得当前线程的消息队列

	......

	while (true) {
		Message msg = queue.next(); // might block
		......

		if (msg != null) {
			if (msg.target == null) {
				// No target is a magic identifier for the quit message.
				return;
			}

			......

			msg.target.dispatchMessage(msg);
			
			......

			msg.recycle();
		}
	}
}

......

}

step 2:MessageQueue.next

public class MessageQueue {

final Message next() {
	int pendingIdleHandlerCount = -1; // -1 only during first iteration
	int nextPollTimeoutMillis = 0;

	for (;;) {
		if (nextPollTimeoutMillis != 0) {
			Binder.flushPendingCommands();
		}
		nativePollOnce(mPtr, nextPollTimeoutMillis);

		synchronized (this) {
			// Try to retrieve the next message.  Return if found.
			final long now = SystemClock.uptimeMillis();
			final Message msg = mMessages;
			if (msg != null) {
				final long when = msg.when;
				if (now >= when) {
					mBlocked = false;
					mMessages = msg.next;
					msg.next = null;
					if (Config.LOGV) Log.v("MessageQueue", "Returning message: " + msg);
					return msg;
				} else {
					nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
				}
			} else {
				nextPollTimeoutMillis = -1;
			}

			// If first time, then get the number of idlers to run.
			if (pendingIdleHandlerCount < 0) {
				pendingIdleHandlerCount = mIdleHandlers.size();
			}
			if (pendingIdleHandlerCount == 0) {
				// No idle handlers to run.  Loop and wait some more.
				mBlocked = true;
				continue;
			}

			if (mPendingIdleHandlers == null) {
				mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
			}
			mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
		}

		// Run the idle handlers.
		// We only ever reach this code block during the first iteration.
		for (int i = 0; i < pendingIdleHandlerCount; i++) {
			final IdleHandler idler = mPendingIdleHandlers[i];
			mPendingIdleHandlers[i] = null; // release the reference to the handler

			boolean keep = false;
			try {
				keep = idler.queueIdle();
			} catch (Throwable t) {
				Log.wtf("MessageQueue", "IdleHandler threw exception", t);
			}

			if (!keep) {
				synchronized (this) {
					mIdleHandlers.remove(idler);
				}
			}
		}

		// Reset the idle handler count to 0 so we do not run them again.
		pendingIdleHandlerCount = 0;

		// While calling an idle handler, a new message could have been delivered
		// so go back and look again for a pending message without waiting.
		nextPollTimeoutMillis = 0;
	}
}

......

}

step 3:MessageQueue.nativePollOnce
这里是一个JNI方法,由C++层中的函数android_os_MessageQueue_nativePollOnce来实现
step 4:NativeMessageQueue.pollOnce
step 5:Looper.pollOnce
C++层中的Looper对象
step 6:Looper.pollInner
step 7:Looper.awoken

函数awoken的实现很简单,它只是把管道中的内容都读取出来:

void Looper::awoken() {
	......
 
	char buffer[16];
	ssize_t nRead;
	do {
		nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
	} while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
}

这样,消息的循环过程就分析完了,这部分逻辑还是比较复杂的,它利用Linux系统中的管道(pipe)进程间通信机制来实现消息的等待和处理。

3.线程消息的发送过程
Android系统提供了一个Handler类,用来向一个线程的消息队列发送一个消息。

public class Handler {

public Handler() {
	......

	mLooper = Looper.myLooper();
	......

	mQueue = mLooper.mQueue;
	......
}


final MessageQueue mQueue;
final Looper mLooper;
......

}

发送是从Handler类的成员函数sendMessage开始,总共有5个步骤:
Step1:Handler.sendMessage
public class Handler {

public final boolean sendMessage(Message msg)
{
	return sendMessageDelayed(msg, 0);
}

public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
	if (delayMillis < 0) {
		delayMillis = 0;
	}
	return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
	boolean sent = false;
	MessageQueue queue = mQueue;
	if (queue != null) {
		msg.target = this;
		sent = queue.enqueueMessage(msg, uptimeMillis);
	}
	else {
		......
	}
	return sent;
}

......

}

Step2:MessageQueue.enqueueMessage
Step3:MessageQueue.nativeWake
Step4:NativeMessageQueue.Wake
Step5:Looper.Wake

void Looper::wake() {
	......
 
	ssize_t nWrite;
	do {
		nWrite = write(mWakeWritePipeFd, "W", 1);
	} while (nWrite == -1 && errno == EINTR);
 
	.......
}
C++层的Looper类的成员变量mWakeWritePipeFd是用来描述一个管道的写段文件描述符的。
这个wake函数很简单,只是通过打开文件描述符mWakeWritePipeFd往管道的写入一个"W"字符串。其实,往管道写入什么内容并不重要,往管道写入内容的目的是为了唤醒应用程序的主线程。
前面我们在分析应用程序的消息循环时说到,当应用程序的消息队列中没有消息处理时,应用程序的主线程就会进入空闲等待状态,
而这个空闲等待状态就是通过调用这个Looper类的pollInner函数来进入的,具体就是在pollInner函数中调用epoll_wait函数来等待管道中有内容可读的。

这时候既然管道中有内容可读了,应用程序的主线程就会从这里的Looper类的pollInner函数返回到JNI层的nativePollOnce函数,最后返回到Java层中的MessageQueue.next函数中去,

这里它就会发现消息队列中有新的消息需要处理了,于就会处理这个消息。

4.线程消息的处理过程
线程中有消息需要处理时,首先会在C++层的Looper类的成员函数pollInner中唤醒,然后沿着调用路径一直返回到Java层的Looper类的
静态函数loop中,最后就可以对新的消息进行处理了。
这个过程分为3步:
Step1:Looper.loop
public class Looper {

public static final void loop() {
	Looper me = myLooper();
	MessageQueue queue = me.mQueue;

	......

	while (true) {
		Message msg = queue.next(); // might block  << 将要处理的消息保存在Message对象msg中
		......

		if (msg != null) {
			if (msg.target == null) {  << Message对象msg的成员变量target等于null,说明要处理的是一个退出消息
				// No target is a magic identifier for the quit message.
				return;
			}

			......

			msg.target.dispatchMessage(msg);  <<Message对象msg的成员变量target指向的是一个Handler对象
			
			......

			msg.recycle();
		}
	}
}

......

}

Step2:Handler.dispatchMessage

public class Handler {

public void dispatchMessage(Message msg) {
	if (msg.callback != null) {
		handleCallback(msg);
	} else {
		if (mCallback != null) {
			if (mCallback.handleMessage(msg)) {    // 这里的消息对象msg的callback成员变量和Handler类的mCallBack成员变量一般都为null,
				return;                            //于是,就会调用Handler类的handleMessage函数来处理这个消息,
			}
		}
		handleMessage(msg);
	}
}

......

}

Step3:Handler.handleMessage
至此,一个线程消息的处理过程就完成了。

我们就从消息循环、消息发送和消息处理三个部分分析完Android应用程序的消息处理机制了,为了更深理解,这里我们对其中的一些要点作一个总结:

     A. Android应用程序的消息处理机制由消息循环、消息发送和消息处理三个部分组成的。

     B. Android应用程序的主线程在进入消息循环过程前,会在内部创建一个Linux管道(Pipe),这个管道的作用是使得Android应用程序主线程在消息队列为空时可以进入空闲等待状态,并且使得当应用程序的消息队列有消息需要处理时唤醒应用程序的主线程。

     C. Android应用程序的主线程进入空闲等待状态的方式实际上就是在管道的读端等待管道中有新的内容可读,具体来说就是是通过Linux系统的Epoll机制中的epoll_wait函数进行的。

     D. 当往Android应用程序的消息队列中加入新的消息时,会同时往管道中的写端写入内容,通过这种方式就可以唤醒正在等待消息到来的应用程序主线程。

     E. 当应用程序主线程在进入空闲等待前,会认为当前线程处理空闲状态,于是就会调用那些已经注册了的IdleHandler接口,使得应用程序有机会在空闲的时候处理一些事情。

Android应用程序线程消息循环模型分析
Android应用程序线程的三种消息循环模型
第一种是应用程序主线程消息循环模型,第二种是与界面无关的应用程序子线程消息循环模型,第三种是与界面相关的应用程序子线程消息循环模型;
通过ActivityThread、HandlerThread和AsyncTask这三个类来分别实现上述三种消息循环模型
1、应用程序主线程消息循环模型
当运行在Android应用程序框架层中的ActivityManagerService决定要为当前启动的应用程序创建一个主线程的时候,
它会在ActivityManagerService中的startProcessLocked成员函数调用Process类的静态成员函数start为当前应用程序创建一个主线程:
public final class ActivityManagerService extends ActivityManagerNative
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {

......    

private final void startProcessLocked(ProcessRecord app,    
            String hostingType, String hostingNameStr) {    

    ......       
            
        int pid = Process.start("android.app.ActivityThread",    
            mSimpleProcessManagement ? app.processName : null, uid, uid,    
            gids, debugFlags, null);    
            
        ......    

    } catch (RuntimeException e) {    
            
        ......    

    }    
}    

......    

}
这里Process.start函数的第一个参数“android.app.ActivityThread”,它表示要在当前新建的线程中加载
android.app.ActivityThread类,并且调用这个类的静态成员函数main作为应用程序的入口点。
因此,我们可以说Android应用程序的主线程是以ActivityThread类的静态成员函数main来作为入口函数的。
public final class ActivityThread {

public static final void main(String[] args) {  
    ......

    Looper.prepareMainLooper();     //在当前应用程序的主线程中创建一个消息循环
     
    ......  

    ActivityThread thread = new ActivityThread();  
    thread.attach(false);  

    ...... 
    Looper.loop();            //使当前应用程序主线程进入到前面所创建的一个消息循环中

    ...... 

    thread.detach();  
    ......  
}  

......  

}
//至此,应用程序主线程的消息循环模型就完成。

2、应用程序主线程消息循环模型
与界面无关的应用程序子线程消息循环模型
为了创建一个具有消息循环的应用程序子线程,我们可以通过Android应用程序框架层提供的HandlerThread类来实现。
public class HandlerThread extends Thread {

private Looper mLooper;

public HandlerThread(String name) {
	super(name);
	......
}

......

public void run() {
	......
	Looper.prepare();
	synchronized (this) {
		mLooper = Looper.myLooper();
		......
	}
	......
	Looper.loop();
	......
}

public Looper getLooper() {
	......
	return mLooper;
}

......

}
我们看到HandlerThread类继承了Thread类,因此,通过它可以在应用程序中创建一个子线程,
其次在它的run函数中,会进入一个消息循环中,因此,这个子线程可以常驻在应用程序中,直到它接收收到一个退出消息为止。
在run函数中,首先是调用Looper类的静态成员函数prepare来准备一个消息循环对象:Looper.prepare();
然后通过Looper类的myLooper成员函数将这个子线程中的消息循环对象保存在HandlerThread类中的成员变量mLooper中:mLooper = Looper.myLooper();
这样,其它地方就可以方便地通过它的getLooper函数来获得这个消息循环对象了,有了这个消息循环对象后,就可以往这个子线程的消息队列中发送消息,通知这个子线程执行特定的任务了。
最后在这个run函数通过Looper类的loop函数进入消息循环中:Looper.loop();
这样,一个具有消息循环的应用程序子线程就准备就绪了

3、与界面相关的应用程序子线程消息循环模型;
Android系统为开发人员提供了一个异步任务类(AsyncTask)来实现上面所说的功能,即它会在一个子线程中执行计算任务,
同时通过主线程的消息循环来获得更新应用程序界面的机会。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值