消息处理机制(AOSP4.4.2)

news2024/11/14 15:11:09

消息处理机制(AOSP4.4.2)

  • Android 应用程序是通过消息来驱动的,系统为每一个应用程序维护一个消息队列,应用程序的主线程,不断地从这个消息队列中获取消息(Looper),然后对消息进行处理(Handler)。

  • 消息存放在一个消息队列中(MessageQueue),应用程序的主线程围绕消息队列进入无限循环,直到应用程序退出。

  • 如果队列中有消息,应用程序的主线程会把它取出,并分发给相应的 Handler 进行处理;如果队列中没有消息,应用程序的主线程会进入空闲等待状态,等待消息的到来。

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

  • 网络文章

    • HandlerThread详解 - 简书 (jianshu.com)
      请添加图片描述

一、创建主线程消息队列

  • Java 层的 Looper 类和 MessageQueue 类是通过 C++ 层实现的。C++ 层有一个相应的 Looper 类和 NativeMessageQueue 类。

  • Java 层 Looper 对象内部的成员变量 mQueue ,它指向了一个 MessageQueue 对象;而在 C++ 层中,每一个 NativeMessageQueue 对象内部都有一个成员变量 mLooper,它指向了一个 C++ 层的 Looper 对象。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dXd31dIw-1687829592199)(image/消息.png)]

1——3 Java层

ActivityThread Looper MessageQueue android_os_MessageQueue.cpp main prepareMainLooper prepare Looper MessageQueue nativeInit ActivityThread Looper MessageQueue android_os_MessageQueue.cpp
1. ActivityThread.main
//frameworks/base/core/java/android/app/ActivityThread.java
public final class ActivityThread {
    ......
    public static void main(String[] args) {
        ......
        /* 1.为当前线程初始化Looper */
        Looper.prepareMainLooper();
        ......
        /* 2.使主线程进入消息循环中 */
        Looper.loop();
        ......
    }
}
2. Looper.prepareMainLooper
  • 只能在主线程调用,即 UI 线程,其他线程通过 Looper.getMainLooper() 方法访问主线程的 Looper。
  • 成员变量 sThreadLocal 的类型为 ThreadLocal ,这是一个线程局部变量,保证每一个调用 prepare 函数的线程,都有一个独立的 Looper 对象。Java中的ThreadLocal详解 - 夏末秋涼 - 博客园 (cnblogs.com)
  • 因为 Looper 类的构造方法是 private 的,假若通过类似单例模式的写法,多个线程修改同一个 Looper 实例显然不符合设计需求。
//frameworks/base/core/java/android/os/Looper.java
public final class Looper {
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    final MessageQueue mQueue;
    
    private Looper(boolean quitAllowed) {
        /* 3.创建Looper对象时,会同时创建一个消息队列MessageQueue,
        保存在Looper的成员变量mQueue中,消息就存放在这个队列。*/
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
    
    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            ......
        }
        /* 2.函数prepare在线程中创建一个Looper对象,
    	这个对象存放在sThreadLocal成员变量里。*/
        sThreadLocal.set(new Looper(quitAllowed));
    }
    
    // 获得与当前线程相关联的一个Looper对象
    public static Looper myLooper() {
        return sThreadLocal.get();
    }
    
    public static void prepareMainLooper() {
        /* 1.调用prepare */
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException(
                    "The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
    ......
}
3.MessageQueue.MessageQueue
//frameworks/base/core/java/android/os/MessageQueue.java
public final class MessageQueue {
    ......
    // NativeMessageQueue的地址
    private int mPtr; // used by native code
    ......
    private native static int nativeInit();
	......
    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        /* 1.初始化工作交给Native方法实现 */
        mPtr = nativeInit();
    }
    ......
}

4——6 C++层

android_os_MessageQueue.cpp NativeMessageQueue Looper android_os_MessageQueue_nativeInit NativeMessageQueue Looper android_os_MessageQueue.cpp NativeMessageQueue Looper
4. android_os_MessageQueue.cpp : android_os_MessageQueue_nativeInit
  • 智能指针:(150条消息) Android 智能指针详解_android 智能指针使用方法_私房菜的博客-CSDN博客
  • 指针强制类型转换:C++强制类型转换运算符(static_cast、reinterpret_cast、const_cast和dynamic_cast) (biancheng.net)
//frameworks/base/core/jni/android_os_MessageQueue.cpp
......
static jint android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
    /* 1.先创建一个NativeMessageQueue对象 */
    NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
    ......
    /* 2.增加nativeMessageQueue的强指针引用计数 */
    nativeMessageQueue->incStrong(env);
    /* 3.将nativeMessageQueue地址强制转换,返回 */
    return reinterpret_cast<jint>(nativeMessageQueue);
}
......
5. android_os_MessageQueue.cpp :: NativeMessageQueue
//frameworks/base/core/jni/android_os_MessageQueue.cpp
......
NativeMessageQueue::NativeMessageQueue() : mInCallback(false), mExceptionObj(NULL) {
    /* 1.调用静态成员函数getForThread来检查是否已经为
    当前线程创建过一个C++层的Looper对象。 */
    mLooper = Looper::getForThread();
    if (mLooper == NULL) {
        /* 2.没有即创建,保存在成员变量mLooper */
        mLooper = new Looper(false);
        /* 3.与当前线程关联 */
        Looper::setForThread(mLooper);
    }
}
......
6. Looper.cpp :: Looper
  • 管道在一个线程的消息循环过程中的作用:
    • 当一个线程没有新消息要处理时,它会在这个管道的读端文件描述符上睡眠,直到有新的消息要处理;
    • 当其他线程向本线程发送了一个消息后,其他线程会通过管道的写端文件描述符,往管道写入数据,从而将这个线程唤醒。
  • fcntl:file control ,提供了对文件描述符的各种操作。(150条消息) Unix/Linux编程:fcntl函数总结_fcntl全称_OceanStar的学习笔记的博客-CSDN博客
  • Linux 系统的 epoll :为了同时监听多个文件描述符的 IO 读写事件而设计,它是一个多路复用的 IO 接口。Linux epoll模型 - venow - 博客园 (cnblogs.com)
  • 多路复用:单线程或单进程同时监测若干个文件描述符,可以执行IO操作。
  • 文件描述符:Linux 的所有对设备和文件的操作都是使用文件描述符来进行的。文件描述符是一个非负的整数,它是一个索引值, 并指向在内核中每个进程打开文件的记录表。
//system/core/libutils/Looper.cpp
......
Looper::Looper(bool allowNonCallbacks) :
        mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
        mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
    int wakeFds[2];
    /* 1.创建一个管道 */
    int result = pipe(wakeFds);
    ......
    mWakeReadPipeFd = wakeFds[0];
    mWakeWritePipeFd = wakeFds[1];

    /* 2.O_NONBLOCK使I/O变成非阻塞模式(non-blocking),在读取
    不到数据或是写入缓冲区已满会马上return,而不会阻塞等待。*/
    result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
    ......

    result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
    ......

    /* 3.创建一个epoll实例,并将它的文件描述符保存在成员变量
    mEpollFd */
    mEpollFd = epoll_create(EPOLL_SIZE_HINT);
    ......

    /* 4.将创建的管道读端文件描述符添加到epoll实例中,
    以便epoll对管道的写操作进行监听 */
    struct epoll_event eventItem;
    memset(& eventItem, 0, sizeof(epoll_event)); 
    eventItem.events = EPOLLIN;
    eventItem.data.fd = mWakeReadPipeFd;
    // EPOLL_CTL_ADD宏:注册新的fd到epoll
    result = epoll_ctl(
        mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);
    ......
}
......

二、消息循环

1——2 Java层

ActivityThread Looper MessageQueue android_os_MessageQueue.cpp loop next nativePollOnce ActivityThread Looper MessageQueue android_os_MessageQueue.cpp
1. Looper.loop
//frameworks/base/core/java/android/os/Looper.java
public final class Looper {
    ......
    public static void loop() {
        final Looper me = myLooper();
        ......
        /* 1.获得当前线程的消息队列 */
        final MessageQueue queue = me.mQueue;
        ......
        /* 2.不断检查这个消息队列中是否有消息要处理 */
        for (;;) {
            // 如果没有消息要处理,会进入睡眠等待状态
            Message msg = queue.next(); // might block
            // 如果消息为null,退出消息循环
            if (msg == null) {
                return;
            }
            ......
            // 否则的话就调用target对象的dispatchMessage
            // 来处理这个消息,这个target对象的类型为Handler
            msg.target.dispatchMessage(msg);
            ......
            msg.recycle();
        }
    }
    ......
}
2. MessageQueue.next
  • 调用这个函数的时候,有可能会让线程进入等待状态。
  • 线程会进入等待状态的情况:
    • 消息队列中没有消息时,它会使线程进入等待状态;
    • 消息队列中有消息,但是消息指定了执行的时间,而现在还没有到这个时间,线程也会进入等待状态(消息队列中的消息是按时间先后来排序的)。
  • 同步拦截器:它可以拦截它之后的所有同步消息,直到该拦截器被移除。消息拦截器和普通消息的差异在于,拦截器的 target 是空的,正常通过 enqueueMessage 方法入队的消息限制 target 不能为空。
  • Android 学习笔记之Handler 总结 - 掘金 (juejin.cn)
//frameworks/base/core/java/android/os/MessageQueue.java
public final class MessageQueue {
    ......
    // 用来描述当前线程需要处理的消息
    Message mMessages;
    ......
    private native static void nativePollOnce(
        int ptr, int timeoutMillis);
    ......
    Message next() {
        // 用来保存:注册到消息队列中,IdleHandler的个数
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        
        // 当消息队列中没有新消息要处理时,当前线程需要进入睡眠
        // 等待状态的时间:-1一直阻塞;0不阻塞;>0阻塞指定时间
        /* 1.初始设置为0 */
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                // 为阻塞做准备,把正在等待处理的
                // Binder IPC请求都释放了,避免
                // 长时间得不到处理
                Binder.flushPendingCommands();
            }
            
            /* 2.检查当前线程的消息队列中是否有新消息要处理。
            参数mPtr是在C++层创建的NativeMessageQueue */
            nativePollOnce(mPtr, nextPollTimeoutMillis);

            synchronized (this) {
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                
                /* 3.判断队头是不是同步拦截器 */
                if (msg != null && msg.target == null) {
                // 同步屏障,寻找第一条异步消息。
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                
                /* 4.处理消息 */
                if (msg != null) {
                    if (now < msg.when) {
                        // 下一条消息未准备好,设置超时唤醒时间
                        nextPollTimeoutMillis = 
                            (int) Math.min(
                            msg.when - now,Integer.MAX_VALUE);
                    } else {// 未设置延迟的普通消息
                        mBlocked = false;
                        // 将消息从队列中删除
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        // 让消息出队
                        msg.next = null;
                        ......
                        // 设置为in-use状态
                        msg.markInUse();
                        // 返回取出的消息
                        return msg;
                    }
                } else {
                    // 如果消息队列中没有可以取出的消息,nextPollTimeoutMillis
                    // 等于-1,让线程一直阻塞。
                    nextPollTimeoutMillis = -1;
                }
                
                // 如果队列已经退出了直接注销、结束方法
                if (mQuitting) {
                    dispose();
                    return null;
                }
                
                // --- IdleHandler的处理 --------------------------------

                ......
            }

            ......

            /* 5.执行了IdleHandler代码块,有可能有新的消息入队了,
            不阻塞线程,去查看有没有新消息 */
            nextPollTimeoutMillis = 0;
        }
    }
    ......
}

3——7 C++层

android_os_MessageQueue.cpp NativeMessageQueue Looper android_os_MessageQueue_nativePollOnce pollOnce pollOnce pollInner awoken android_os_MessageQueue.cpp NativeMessageQueue Looper
3. android_os_MessageQueue.cpp : android_os_MessageQueue_nativePollOnce
//frameworks/base/core/jni/android_os_MessageQueue.cpp
......
// 参数ptr是Java层MessageQueue对象的成员变量mPtr,它保存的是一个C++层
// 创建的NativeMessageQueue对象的地址值
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, 
                                                   jclass clazz,
                                                   jint ptr, 
                                                   jint timeoutMillis) {
    /* 1.将ptr转化为NativeMessageQueue对象 */
    NativeMessageQueue* nativeMessageQueue = 
        reinterpret_cast<NativeMessageQueue*>(ptr);
    /* 2.调用函数来检查是否有新消息进行处理 */
    nativeMessageQueue->pollOnce(env, timeoutMillis);
}
......
4. android_os_MessageQueue.cpp :: pollOnce
//frameworks/base/core/jni/android_os_MessageQueue.cpp
......
void NativeMessageQueue::pollOnce(JNIEnv* env, int timeoutMillis) {
    ......
    /* 1.调用Looper类的pollOnce */
    mLooper->pollOnce(timeoutMillis);
    ......
}
......
5. Looper.cpp :: pollOnce
//system/core/libutils/Looper.cpp
......
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
    int result = 0;
    for (;;) {
        ......
        if (result != 0) {
            ......
            return result;
        }
		/* 1.循环不断地调用,检查当前线程是否有新的消息要处理,
		如果有,那么返回值不为0,就会跳出循环,以便当前线程对
		新的信息进行处理 */
        result = pollInner(timeoutMillis);
    }
}
......
6. Looper.cpp :: pollInner
//system/core/libutils/Looper.cpp
......
int Looper::pollInner(int timeoutMillis) {
    ......
    // Poll.
    int result = ALOOPER_POLL_WAKE;
    ......
    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    /* 1.监听注册在epoll实例中的文件描述符的IO读写事件。
    如果没有发生IO读写事件,那么当前线程就会在这里进入睡
    眠等待状态,timeoutMillis指定其时间。 */
    int eventCount = epoll_wait(
        mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
    ......
    /* 2.检查哪一个文件描述符发生了IO读写事件 */
    for (int i = 0; i < eventCount; i++) {
        int fd = eventItems[i].data.fd;
        uint32_t epollEvents = eventItems[i].events;
        // 检查IO读写事件的文件描述符是不是与当前线程
        // 关联的一个管道的读端文件描述符
        if (fd == mWakeReadPipeFd) {
            // 检查IO读写事件的类型是否为EPOLLIN
            if (epollEvents & EPOLLIN) {
                // 这时说明其他线程向与当前线程相
                // 关联的管道,写入了新的数据
                awoken();
            } ......
        } ......
    }
    ......
    return result;
}
......
7. Looper.cpp :: awoken
//system/core/libutils/Looper.cpp
......
void Looper::awoken() {
    ......
    char buffer[16];
    ssize_t nRead;
    do {
        /* 1.调用函数将与当前线程所关联的管道的数据读出。
        就是简单读出数据,不关心数据本身 */
        nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
    } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
}
......

三、消息发送

  • 应用程序的主线程准备好消息队列,并且进入到消息循环后,其它地方就可以往这个消息队列中发送消息了。
  • 以应用程序的启动为例,说明应用程序是如何把消息加入到应用程序的消息队列中去的。

1——4 Java层

ApplicationThread Handler MessageQueue scheduleLaunchActivity sendMessage sendMessageDelayed sendMessageAtTime enqueueMessage nativeWake ApplicationThread Handler MessageQueue
1. ActivityThread.ApplicationThread.scheduleLaunchActivity
//frameworks/base/core/java/android/app/ActivityThread.java
public final class ActivityThread {
    ......
    private class ApplicationThread extends ApplicationThreadNative {
		......
		public final void scheduleLaunchActivity(Intent intent, 
                                    IBinder token,int ident,
                                    ActivityInfo info,
                                    Configuration curConfig,
                                    CompatibilityInfo compatInfo,
                                    int procState, Bundle state,
                                    List<ResultInfo> pendingResults,
									List<Intent> pendingNewIntents, 
                                    boolean notResumed, 
                                    boolean isForward,
									String profileName, 
                                    ParcelFileDescriptor profileFd, 
                                    boolean autoStopProfiler) {
			updateProcessState(procState, false);
			/* 1.把相关的参数封装成一个ActivityClientRecord对象 */
			ActivityClientRecord r = new ActivityClientRecord();
			r.token = token;
			r.ident = ident;
			r.intent = intent;
			r.activityInfo = info;
			r.compatInfo = compatInfo;
			r.state = state;
			r.pendingResults = pendingResults;
			r.pendingIntents = pendingNewIntents;
			r.startsNotResumed = notResumed;
			r.isForward = isForward;
			r.profileFile = profileName;
			r.profileFd = profileFd;
			r.autoStopProfiler = autoStopProfiler;

			updatePendingConfiguration(curConfig);
			
            /* 2.调用sendMessage函数发送消息H.LAUNCH_ACTIVITY */
			sendMessage(H.LAUNCH_ACTIVITY, r);
		}
        ......
    }
    ......
}
2. ActivityThread.ApplicationThread.sendMessage
  • 这里的 mH 是 ActivityThread 类的成员变量,它的类型为 H ,继承于 Handler 类。
//frameworks/base/core/java/android/app/ActivityThread.java
private class ApplicationThread extends ApplicationThreadNative {
	......
    private void sendMessage(int what, Object obj) {
        sendMessage(what, obj, 0, 0, false);
    }
	......
    private void sendMessage(int what, Object obj, int arg1, int arg2, 
                             boolean async) {
        ......
        /* 1.把传进来的参数封装成一个Message对象msg */
        Message msg = Message.obtain();
        msg.what = what;
        msg.obj = obj;
        msg.arg1 = arg1;
        msg.arg2 = arg2;
        if (async) {
            msg.setAsynchronous(true);
        }
        /* 2.通过mH.sendMessage函数把消息对象msg
        加入消息队列中 */
        mH.sendMessage(msg);
    }
    ......
}
3. Handler.sendMessage
//frameworks/base/core/java/android/os/Handler.java
public class Handler {
	......
	public final boolean sendMessage(Message msg){
        /* 1.在发送消息时,可以指定消息的处理时间,sendMessage
        发送消息的默认处理时间为当前时间,表示马上处理。因此,
        这里传入的时间参数为0 */
        return sendMessageDelayed(msg, 0);
    }

	public final boolean sendMessageDelayed(
        Message msg, long delayMillis){
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        /* 2.先获得当前时间,然后加上消息要延时处理的时间,得到处理
        这个消息的绝对时间。然后把消息加入到消息队列中 */
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() +
                                 delayMillis);
    }

	public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        /* 3.首先得到应用程序的消息队列mQueue,它是在Handler对象
        构造时初始化的 */
        MessageQueue queue = mQueue;
        ......
        return enqueueMessage(queue, msg, uptimeMillis);
    }
    
    private boolean enqueueMessage(MessageQueue queue, Message msg, 
                                   long uptimeMillis) {
        /* 4.设置这个消息的目标对象target,即这个消息最终是由谁
        来处理 */
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
	......
}
4. MessageQueue.enqueueMessage
  • 把消息加入到消息队列时,分两种情况:
    • 第一种,当前消息队列为空:这时应用程序的主线程一般处于空闲等待状态,这时要唤醒它。
    • 第二种,消息队列不为空:这时不需要唤醒应用程序的主线程,因为这时它一定是在处理消息队列中的消息,因此不会处于空闲等待状态。
//frameworks/base/core/java/android/os/MessageQueue.java
public final class MessageQueue {
    ......
    private native static void nativeWake(int ptr);
    ......
    boolean enqueueMessage(Message msg, long when) {
        ......
        synchronized (this) {
            ......
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            /* 1.根据实际情况将一个消息插入到一个消息队列中:
            (1)消息队列为空;
            (2)插入的消息处理时间为0;
            (3)插入的消息的处理时间,小于保存在消息队列头
            的消息的处理时间;
            (4)插入的消息的处理时间,大于等于保存在消息队
            列头的消息的处理时间;*/
            if (p == null || when == 0 || when < p.when) {
                // (1)~(3):插入的消息保存在消息队列
                // 的头部
                msg.next = p;
                mMessages = msg;
                // mBlocked记录了目标线程是否处于睡眠等待
                needWake = mBlocked;
            } else {
                // (4):插入的消息保存在消息队列的某一
                // 个位置上。通过循环找出。
                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;
            }

            /* 2.把消息加入到消息队列后,可能需要唤醒
            目标线程,分两种情况:
            (1)插入的消息在消息队列中间;
            (2)插入的消息在消息队列头部。
            对于(1):由于消息队列头部消息没有变化,
            所以无论如何都不需要唤醒目标线程。
            对于(2):头部消息发生了变化,需要唤醒目
            标线程,以便对于头部新消息进行处理,如果
            已唤醒就不用了 */
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }
    ......
}

5——7 C++层

android_os_MessageQueue.cpp NativeMessageQueue Looper android_os_MessageQueue_nativeWake wake wake android_os_MessageQueue.cpp NativeMessageQueue Looper
5. android_os_MessageQueue.cpp : android_os_MessageQueue_nativeWake
//home/yy/aosp/frameworks/base/core/jni/android_os_MessageQueue.cpp
......
static void android_os_MessageQueue_nativeWake(JNIEnv* env, 
                                               jclass clazz, 
                                               jint ptr) {
    /* 1.转型 */
    NativeMessageQueue* nativeMessageQueue = 
        reinterpret_cast<NativeMessageQueue*>(ptr);
    /* 2.唤醒 */
    return nativeMessageQueue->wake();
}
......
6. android_os_MessageQueue.cpp : wake
//frameworks/base/core/jni/android_os_MessageQueue.cpp
......
void NativeMessageQueue::wake() {
    mLooper->wake();
}
......
7. Looper.cpp : wake
//system/core/libutils/Looper.cpp
......
void Looper::wake() {
    ......
    ssize_t nWrite;
    do {
        /* 1.向管道的写端文件描述符写入一个新的数据,
        这时目标线程就会因为管道发生了一个IO写事件而
        被唤醒 */
        nWrite = write(mWakeWritePipeFd, "W", 1);
    } while (nWrite == -1 && errno == EINTR);
    ......
}
......

四、消息处理

  • 在消息循环中,当一个线程没有新的消息需要处理时,就会在 C++ 层的 pollInner 函数中进入睡眠等待状态;
  • 当该线程有新的消息要处理时,它首先会在 C++ 层的 pollInner 函数中唤醒,然后沿着调用链返回到 Java 层的 loop 中,对新消息进行处理。

1——2

Looper Handler loop dispatchMessage Looper Handler
1. Looper.loop
//frameworks/base/core/java/android/os/Looper.java
public class Looper {
	......
    public static void loop() {
        /* 1.得到当前线程下的Looper */
        final Looper me = myLooper();
        ......
        
        /* 2.得到与Looper对应的MessageQueue */
        final MessageQueue queue = me.mQueue;
        Binder.clearCallingIdentity();
        
        /* 3.得到当前线程的唯一标识 */
        final long ident = Binder.clearCallingIdentity();
		
        /* 4.不断取出消息 */
        for (;;) {
            // 从队列中取出一个消息
            Message msg = queue.next();
            // 消息为空,表示退出消息循环
            if (msg == null) {
                return;
            }
            ......
            // 从消息队列中获得消息后,就会调用它的
            // target成员变量
            msg.target.dispatchMessage(msg);
            ......
            // 处理完毕,回收消息
            msg.recycle();
        }
    }
    ......
}
2. Handler.dispatchMessage
//frameworks/base/core/java/android/os/Handler.java
public class Handler {
    ......
    public void dispatchMessage(Message msg) {
        // 如果msg的回调接口不空
        if (msg.callback != null) {
            // 运行这个callback
            handleCallback(msg);
        } else {
            // 如果mCallback不空,说明Handler设置了
            // callback接口,先执行接口方法
            if (mCallback != null) {
                // 如果接口处理完返回true说明它把消息拦截
                // 不再执行Handler自身的处理消息方法
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            // 调用Handler自身处理消息的方法
            handleMessage(msg);
        }
    }
    ......
}
2.1 msg.callback不空
  • Message 类的成员变量 callback 指向的是一个 Runnable 对象,所以这种情况是将消息交给了 Runnable 类的 run 方法执行。
//frameworks/base/core/java/android/os/Handler.java
public class Handler {
    ......
    /* 1.调用post发送一个消息时,Handler会指定一个
    回调接口*/
    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
    ......
    /* 2.将post传来的Runnable对象封装成一个Message */
    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }
    ......
    /* 3.Message的callback执行run方法 */
    private static void handleCallback(Message message) {
        message.callback.run();
    }
    ......
}
2.2 mCallback不空
//frameworks/base/core/java/android/os/Handler.java
public class Handler {
    ......
    /* 1.定义了一个Callback接口 */
    public interface Callback {
        public boolean handleMessage(Message msg);
    }   
    ......
    /* 2.可以调用构造方法,并且指定Callback */
    public Handler(Callback callback) {
        this(callback, false);
    }
    ......
    public Handler(Callback callback, boolean async) {
        ......

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        /* 3.保存在成员变量mCallback中 */
        mCallback = callback;
        mAsynchronous = async;
    }
    ......
}
2.3 handleMessage
//frameworks/base/core/java/android/os/Handler.java
public class Handler {
    ......
    /* 1.是一个空实现。一般用Handler的子类发送消息,
    子类重写该方法处理消息。*/
    public void handleMessage(Message msg) {
    }
    ......
}

五、空闲消息(IdleHandler)

  • 特殊的消息,不用事先发送到一个消息队列,而是由一个线程在空闲时发送出来。
  • 由空闲消息处理器对象来处理,必须实现 IdleHandler 接口。
  • 使用时需要注册到线程的消息队列中,即注册到与这个线程所关联的一个 MessageQueue 对象中。
  • 将不重要、不紧急的事情放在线程空闲时来执行,可以充分地利用线程的空闲时间。
定义与使用
  • MessageQueue 提供了 addIdleHandler 和 removeIdleHandler 来注册和删除 IdleHandler。
//frameworks/base/core/java/android/os/MessageQueue.java
public final class MessageQueue {
    ......
    // 保存线程的空闲消息处理器
    private final ArrayList<IdleHandler> mIdleHandlers = 
        new ArrayList<IdleHandler>();
    
    ......

    public static interface IdleHandler {
        ......
        // 接收空闲消息。
        // 执行这个函数时,如果返回值为false,那么就会将其从mIdleHandlers移除
        // 否则,会在应用程序中继续维护这个IdleHandler,下次空闲时再次执行
        boolean queueIdle();
    }
    
    // 注册一个空闲消息处理器
    public void addIdleHandler(IdleHandler handler) {
        if (handler == null) {
            throw new NullPointerException(
                "Can't add a null IdleHandler");
        }
        synchronized (this) {
            mIdleHandlers.add(handler);
        }
    }

    // 注销一个空闲消息处理器
    public void removeIdleHandler(IdleHandler handler) {
        synchronized (this) {
            mIdleHandlers.remove(handler);
        }
    }
    ......
}
消息循环的处理
  • pendingIdleHandlerCount 为 0 的两种情况:
    • 消息队列中没有注册的空闲消息处理器;
    • 已经发送过一个线程空闲消息。
//frameworks/base/core/java/android/os/MessageQueue.java
public final class MessageQueue {
    ......
    Message next() {
        // 用来保存:注册到消息队列中,IdleHandler的个数
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        ......
        for (;;) {
            ......
            nativePollOnce(mPtr, nextPollTimeoutMillis);

            synchronized (this) {
                ......
                
                // --- IdleHandler的处理 --------------------------------
                
                /* 1.进入阻塞状态前,检查有没有IdleHandler需要执行 */
                if (pendingIdleHandlerCount < 0
                    && (mMessages == null || now < mMessages.when)) {
                    // 得到IdleHandler的数量
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                
                /* 2.pendingIdleHandlerCount<=0说明没有
                IdleHandler需要执行的 */
                if (pendingIdleHandlerCount <= 0) {
                    // 直接进入下次循环阻塞线程
                    mBlocked = true;
                    continue;
                }

                /* 3.执行到此处说明有待处理的IdleHandler,那就从
                IdleHandler集合列表中取出 */
                if (mPendingIdleHandlers == null) {
                    // 初始化
                    mPendingIdleHandlers = new
                        IdleHandler[Math.max(
                            pendingIdleHandlerCount, 4)];
                }
                
                /* 4.从IdleHandler集合中获取待处理的IdleHandler */
                mPendingIdleHandlers =
                    mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            /* 5.执行IdleHanlder */
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                // 取出一个IdleHandler
                final IdleHandler idler = mPendingIdleHandlers[i];
                // 释放掉引用
                mPendingIdleHandlers[i] = null; // 释放引用handler

                // IdleHandler的执行模式,true执行一次,false总是执行
                boolean keep = false;
                try {
                    // 执行,并得到执行模式
                    keep = idler.queueIdle();
                } ......

                // 通过执行模式判断是否需要移除
                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            /* 6.处理完了所有IdleHandler把数量清零 */
            pendingIdleHandlerCount = 0;
			......
        }
    }
    ......
}

六、总结

  • Android 应用程序的消息处理机制由消息循环、消息发送和消息处理三个部分组成的。
  • Android 应用程序的主线程在进入消息循环过程前,会在内部创建一个 Linux 管道(Pipe),这个管道的作用是,使得 Android 应用程序的主线程,在消息队列为空时可以进入空闲等待状态,并且使得当应用程序的消息队列,在有消息需要处理时唤醒应用程序的主线程。
  • Android 应用程序的主线程进入空闲等待状态的方式,实际上就是在管道的读端等待管道中有新的内容可读,具体来说就是是通过 Linux 系统的 Epoll 机制中的 epoll_wait 函数进行的。
  • 当往 Android 应用程序的消息队列中加入新的消息时,会同时往管道中的写端写入内容,通过这种方式就可以唤醒正在等待消息到来的应用程序主线程。
  • 当应用程序主线程在进入空闲等待前,会认为当前线程处理空闲状态,于是就会调用那些已经注册了的 IdleHandler 接口,使得应用程序有机会在空闲的时候处理一些事情。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/690775.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STM32单片机(三)第二节:GPIO输出练习2(LED流水灯)

❤️ 专栏简介&#xff1a;本专栏记录了从零学习单片机的过程&#xff0c;其中包括51单片机和STM32单片机两部分&#xff1b;建议先学习51单片机&#xff0c;其是STM32等高级单片机的基础&#xff1b;这样再学习STM32时才能融会贯通。 ☀️ 专栏适用人群 &#xff1a;适用于想要…

3 分钟为英语学习神器 Anki 部署一个专属同步服务器

原文链接&#xff1a;https://icloudnative.io/posts/anki-sync-server/ Anki 介绍 Anki 是一个辅助记忆软件&#xff0c;其本质是一个卡片排序工具--即依据使用者对卡片上的自定义内容进行主动测试、自我评判后&#xff0c;其内部算法根据评判结果更改每张卡片下次测试时间的…

将win上的文件传输到Ubuntu虚拟机

首先获取Ubuntu系统的ip地址&#xff0c;在Ubuntu的Terminal中输入ifconfig&#xff0c;可以看到Ubuntu的ip地址 可以看到我电脑的ip地址是10.0.2.15。更改虚拟机的网络连接 这里以VirtualBox为例&#xff0c;打开VirtualBox设置&#xff0c;选择网络&#xff0c;将连接方式改…

React.JS实战项目(三):图书购物网站

React.JS实战项目(三):图书购物网站 1、菜单 首页图书新书购物车2、首页 首页视频预览 首页预览 首页主要展示了友情链接、图书分类、好书推荐、新书广场等等信息。 首页部分代码展示 <Row><Col

SpringSecurity整合ssm

SpringSecurity 1. SpringSecurity 框架简介 Spring 是非常流行和成功的 Java 应用开发框架&#xff0c;Spring Security 正是 Spring 家族中的成员。Spring Security 基于 Spring 框架&#xff0c;提供了一套 Web 应用安全性的完整解决方 案。 正如你可能知道的关于安全方面…

nexus 配置pypi代理

在研发环境中由于网络限制&#xff0c;无法访问外网&#xff0c;但经常使用npm、maven、pip等工具&#xff0c;这种场景中使用nexus 做代理是一个比较好的解决办法。 在配置pypi代理时&#xff0c;和配置npm、maven代理有所不同&#xff0c;在配置远程地址时&#xff0c;需要将…

我的IDEA插件

文章目录 前言一、.ignore二、Adapter for Eclipse Code Formatter三、Convert YAML and Properties File四、EasyCode五、Free MyBatis Tool六、Maven Helper七、Rainbow Brackets 前言 目前使用比较顺手的插件&#xff0c;具体使用方法自行查阅 一、.ignore git 忽略文件&…

【算法与数据结构】344、LeetCode反转字符串

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;关于变量交换有两种办法&#xff0c;一种是最常见的引入一个临时变量方法&#xff0c;另一种是使用位运…

【Java面试题】Java基础——集合

文章目录 集合的形式List和Set的区别ArrayList和LinkedList的区别ArrayList和数组的区别ArrayList的扩容机制是什么&#xff1f;ArrayList有哪些特点List和Map的区别如何让map存储有序数据如何创建Map?常用的Map有哪些?如何在HashMap中插入一个数据遍历一个 List 有哪些不同的…

高清音频文件如何压缩?分享轻松压缩音频文件的方法!

如何进行音频压缩&#xff1f;在我们日常生活中&#xff0c;音频文件扮演着重要的角色&#xff0c;我们可以通过它们享受音乐、收听播客或处理语音录音等。然而&#xff0c;有时候这些音频文件的大小可能会成为问题&#xff0c;特别是当我们需要在有限的存储空间中存储更多的文…

深入浅出设计模式 - 原型模式

博主介绍&#xff1a; ✌博主从事应用安全和大数据领域&#xff0c;有8年研发经验&#xff0c;5年面试官经验&#xff0c;Java技术专家✌ Java知识图谱点击链接&#xff1a;体系化学习Java&#xff08;Java面试专题&#xff09; &#x1f495;&#x1f495; 感兴趣的同学可以收…

STM32模拟I2C获取TCS34725光学颜色传感器数据

STM32模拟I2C获取TCS34725光学颜色传感器数据 TCS34725是RGB三色颜色传感器&#xff0c;和TCS34727都属于TCS3472系列&#xff0c;在电气特性上略有差别&#xff0c;TCS34727相比TCS34725在I2C总线的访问电平上可以更低&#xff0c;而在I2C软件访问地址方面则一致。 TCS3472内…

leetcode:1431. 拥有最多糖果的孩子(python3解法)

难度&#xff1a;简单 给你一个数组 candies 和一个整数 extraCandies &#xff0c;其中 candies[i] 代表第 i 个孩子拥有的糖果数目。 对每一个孩子&#xff0c;检查是否存在一种方案&#xff0c;将额外的 extraCandies 个糖果分配给孩子们之后&#xff0c;此孩子有 最多 的糖…

Spring Boot中的@RequestMapping注解,如何使用

Spring Boot中的RequestMapping注解 介绍 Spring Boot是一个流行的Java框架&#xff0c;它提供了许多方便的注解和工具&#xff0c;使得Web应用程序的开发变得更加容易。其中&#xff0c;RequestMapping注解是Spring Boot中最常用的注解之一&#xff0c;它可以帮助开发者定义…

django旅游推荐系统-计算机毕设 附源码82884

django旅游推荐系统 摘 要 随着社会的快速发展和人们生活水平的不断提高&#xff0c;旅游已逐渐成为人们生活的重要组成部分&#xff0c;用户能够获取旅游信息的渠道也随信息技术的广泛应用而增加。大量未经过滤的信息在展示给用户的同时&#xff0c;也淹没了用户真正感兴趣的信…

10个图像处理的Python库

在这篇文章中&#xff0c;我们将整理计算机视觉项目中常用的Python库&#xff0c;如果你想进入计算机视觉领域&#xff0c;可以先了解下本文介绍的库&#xff0c;这会对你的工作很有帮助。 1、PIL/Pillow Pillow是一个通用且用户友好的Python库&#xff0c;提供了丰富的函数集…

【MOOC 测验】第5章 链路层

1、局域网的协议结构一般不包括&#xff08; &#xff09; A. 数据链路层B. 网络层C. 物理层D. 介质访问控制层 逻辑链路控制子层、介质访问控制子层、物理层 2、下列关于二维奇偶校验的说法&#xff0c;正确的是&#xff08; &#xff09; A. 可以检测和纠正双比特差错B…

OV Image Sensor PLL设置

本文讨论OV的Image Sensor PLL的配置。 1.PLL的组成和功能 如图为OS08A10的框图&#xff0c;由图可知&#xff0c;Image Sensor其实是一个模数混合的电路&#xff0c;PLL提供了诸如ADC,gain control,MIPI,I2C等电路所用的时钟。 既然 Image Sensor的PLL是Image Senor非常重要…

详解Vue组件系统

Vue渲染的两大基础方式 new 一个Vue的实例 这个我们一般会使用在挂载根节点这一初始化操作上&#xff1a; new Vue({el: #app }) 复制 注册组件并使用 通过Vue.component&#xff08;&#xff09;去注册一个组件&#xff0c;你就可以全局地使用它了&#xff0c;具体体现在…

什么是信号槽机制,如何实现,有什么用?(Qt面试题)

1. 什么是信号槽机制&#xff1f; 信号槽机制&#xff08;Signal-Slot mechanism&#xff09;是一种在软件开发中常用的设计模式&#xff0c;用于实现对象间的通信和事件处理。该机制最初由Qt框架引入并广泛应用&#xff0c;后来也被其他编程框架和库所采用。 信号槽机制通过定…