frameworks/base/core/java/android/os/Looper.java
final MessageQueue mQueue;
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));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
Androidのメッセージ機構を理解している人は、ハンドラのメッセージ機構を使う場合、まずLooper.prepare()を呼び、最後にLooper.loop()を呼びます。
- prepare() は、MessageQueue オブジェクトを作成します。
- loop() は無限ループに入り、メッセージを取得しようと MessageQueue.next() メソッドを呼び出し続けます。
従来のメッセージング・フレームワークには、メッセージ、メッセージ・キュー、メッセージ・センター、メッセージ・プロデューサー、メッセージ・コンシューマーがあります。一般的にメッセージ・センターはループとして設計され、無限ループでメッセージ・キューからメッセージを読み込みます。メッセージ・キューが空になると、しばらくスリープします。
frameworks/base/core/java/android/os/MessageQueue.java
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
// メッセージをメッセージ・キューに入れ、nativeWake()を呼び出してメッセージ・ハンドラをウェイクアップする。Looper
boolean enqueueMessage(Message msg, long when) {
... ...
synchronized (this) {
... ...
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
// メッセージ・キューからメッセージを読み込もうとし、メッセージ・キューが空であれば、nativePollOnce()を呼び出してメッセージが到着するのを待つ。
Message next() {
... ...
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
nativePollOnce(ptr, nextPollTimeoutMillis);
... ...
}
}
nativeInit/nativeWake/nativePollOnce
ここには3つのネイティブメソッドがあり、JNIでは次のようになります。
frameworks/base/core/jni/android_os_MessageQueue.cpp
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
if (!nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return 0;
}
nativeMessageQueue->incStrong(env);
return reinterpret_cast<jlong>(nativeMessageQueue);
}
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jlong ptr, jint timeoutMillis) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->wake();
}
NativeMessageQueue をインスタンス化し、それぞれ pollOnce/wake メソッドを呼び出します。
NativeMessageQueue::NativeMessageQueue() :
mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
mLooper = Looper::getForThread();
if (mLooper == NULL) {
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
mPollEnv = env;
mPollObj = pollObj;
mLooper->pollOnce(timeoutMillis);
mPollObj = NULL;
mPollEnv = NULL;
if (mExceptionObj) {
env->Throw(mExceptionObj);
env->DeleteLocalRef(mExceptionObj);
mExceptionObj = NULL;
}
}
void NativeMessageQueue::wake() {
mLooper->wake();
}
LooperのpollOnce/wakeメソッドのさらなる呼び出し。
system/core/libutils/Looper.cpp
Looper::Looper(bool allowNonCallbacks) :
mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
// ウェイクアップ用のイベントハンドラmWakeEventFdを作成する。
mWakeEventFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
rebuildEpollLocked();
}
void Looper::rebuildEpollLocked() {
// エポールハンドルの作成
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
struct epoll_event eventItem;
memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
eventItem.events = EPOLLIN;
eventItem.data.fd = mWakeEventFd;
// 書き込みイベントをリッスンするために、epollリスナーにmWakeEventFdを追加する。
int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);
}
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
for (;;) {
... ...
result = pollInner(timeoutMillis);
}
}
int Looper::pollInner(int timeoutMillis) {
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
// イベントを待ち受ける
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
... ...
return result;
}
void Looper::wake() {
uint64_t inc = 1;
// メッセージキューをウェイクアップするためにmWakeEventFdにデータを書き込む。
// このメソッドを呼び出した後epoll_waitこの関数は、コールチェーンのすべての段階でpollInner->pollOnce->pollOnce->android_os_MessageQueue_nativePollOnce->nativePollOnce->next
// このときLooper.loop()next()の呼び出しは戻り、ループ本体は実行を続ける。
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
}
AndroidのHandlerメッセージングメカニズムのメッセージループのコア実装です。Handlerのメッセージループは、Linuxカーネルのepollメカニズムを利用しており、メッセージキューが空になるとハングしてCPUリソースを解放し、メッセージキューに新しいメッセージが到着した時だけ、メッセージ処理のためのCPUリソースを取り戻します。Handlerのメッセージループは、メッセージキューが空になったときにハングしてCPUリソースを解放するためにLinuxカーネルのepollメカニズムを使用します。
従来の実装と比較して、Androidのハンドラーメッセージングメカニズムはより多くのシステムリソースを節約します。
従来の実装と比較して、AndroidのHandlerメッセージングメカニズムのプロデューサーは、メッセージをメッセージキューに入れながら、メッセージハンドラに通知することができます。