当前位置: 首页 > article >正文

Handler源码和流程分析

Handler消息机制的关键流程

在这里插入图片描述

Handler消息机制关键类引用关系

在这里插入图片描述

关键说明

  • 子线程是否可以更新UI?
    • SurfaceView,GLSurfaceView均可以在子线程更新
    • 对于通常情况下,如果子线程更新View,会报以下错误,原因为只能在创建视图层次结构的原始线程才能触摸其视图。===>如果子线程创建的视图UI,那么就可以在子线程更新UI,但是创建的View的线程通常为主线程如setContentView()所以只能主线程更新UI,如果子线程我们来创建视图,那么后续也只能在该线程更新UI,不利于维护,所以我们就默认UI不能在子线程更新
void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
            "Only the original thread that created a view hierarchy can touch its views.");
    }
}
  • 一个线程存在几个Looper?几个MessageQqueue?几个Handler?
    • 一个线程只有一个Looper,一个MessageQueue,可以存在多个Handler:Looper构造器为私有,所以只能通过静态方法Looper.myLooper()获取Looper
// 使用ThreadLocacl保存Looper对象,ThreadLocal为线程变量,其填充的变量只属于当前线程,故一个线程有唯一的一个Looper
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
// 获取当前线程的Looper
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}
// Looper对象的创建
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));
}
// MessageQueue对象的创建,其在Looper构造器中,因为Looper唯一,所以MessageQueue也是唯一的
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}
//Handler构造器为public,故其可以创建多个
  • 主线程直接new Handler()可以,子线程为什么不可以?
//ActivityThread的main方法作为程序的主入口,其中已经针对主线程的Looper进行了初始化,故在主线程不需要再次准备
public static void main(String[] args) {
    ...
    Looper.prepareMainLooper();
    ...
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }
    ...
    Looper.loop();
    ...
}
//子线程为在需要时创建,并不会初始化Looper,并开始进行Looper.loop()循环,故才需要调用prepare和loop方法,否则将会排除异常
  • 既然一个线程可以存在多个Handler,多个Handler发送消息,是如何保证线程安全的
//MessageQueue中的enqueueMessage方法是插入消息的方法,该方法通过synchronized进行同步,保证线程安全,其中this就是当前的消息队列
boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }
    synchronized (this) {
       ....
    }
    return true;
}
  • 子线程中维护的Looper,消息队列无消息的时候的处理方案是什么?有什么用?
//Looper.loop()
public static void loop() {
    final Looper me = myLooper();
    ...
    for (;;) {
        // 调用loopOnce取出消息,该接口返回值表示当前循环是否继续
        if (!loopOnce(me, ident, thresholdOverride)) {
            return;
        }
    }
}
//Looper.loopOnce()
 private static boolean loopOnce(final Looper me,
            final long ident, final int thresholdOverride) {
     // 取出下一个消息,如果没有消息,返回当前外循环不在继续,则loop的循环结束  
     Message msg = me.mQueue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return false;
        }
    ...
     msg.recycleUnchecked();
        return true;
    }
//MessageQueue.next():从消息队列中取出下一个消息
 Message next() {
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            // 该方法为阻塞方法,其中nextPollTimeoutMillis为阻塞时间
            nativePollOnce(ptr, nextPollTimeoutMillis);
            // 存在消息
            synchronized (this) {
               ...
            }
            ....
        }
    }
//android_os_MessageQueue.cpp中的方法,其为静态方法,会调用内部的pollOnce方法
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);
}
//android_os_MessageQueue.cpp中的方法,最后调用Looper.cpp中的pollOnce方法
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;
    }
}
// Looper.cpp中的pollOnce方法中存在一个死循环,用于处理事件
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
    int result = 0;
    for (;;) {
        while (mResponseIndex < mResponses.size()) {
            const Response& response = mResponses.itemAt(mResponseIndex++);
            int ident = response.request.ident;
            // 当存在消息时,返回消息标识
            if (ident >= 0) {
               ...
                return ident;
            }
        }
        // 通过pollInner获取当前的消息表示,存在消息则返回,否则继续循环遍历
        if (result != 0) {
            ...
            return result;
        }
        // 取出
        result = pollInner(timeoutMillis);
    }
}
//Looper.cpp中的pollInner()为最终决定是否阻塞的条件
int Looper::pollInner(int timeoutMillis) {
    // Poll.
    int result = POLL_WAKE;
    
    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    // 阻塞消息
    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);

    // No longer idling.
    mPolling = false;

    // Acquire lock.
    mLock.lock();
    // Check for poll error.
    if (eventCount < 0) {
        if (errno == EINTR) {
            goto Done;
        }
        ALOGW("Poll failed with an unexpected error: %s", strerror(errno));
        result = POLL_ERROR;
        goto Done;
    }

    // Check for poll timeout.
    if (eventCount == 0) {
        result = POLL_TIMEOUT;
        goto Done;
    }
Done: ;
    // Invoke pending message callbacks.
    mNextMessageUptime = LLONG_MAX;
    while (mMessageEnvelopes.size() != 0) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
        const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0);
        if (messageEnvelope.uptime <= now) {
            // Remove the envelope from the list.
            // We keep a strong reference to the handler until the call to handleMessage
            // finishes.  Then we drop it so that the handler can be deleted *before*
            // we reacquire our lock.
            { // obtain handler
                sp<MessageHandler> handler = messageEnvelope.handler;
                Message message = messageEnvelope.message;
                mMessageEnvelopes.removeAt(0);
                mSendingMessage = true;
                mLock.unlock();

#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
                ALOGD("%p ~ pollOnce - sending message: handler=%p, what=%d",
                        this, handler.get(), message.what);
#endif
                handler->handleMessage(message);
            } // release handler

            mLock.lock();
            mSendingMessage = false;
            result = POLL_CALLBACK;
        } else {
            // The last message left at the head of the queue determines the next wakeup time.
            mNextMessageUptime = messageEnvelope.uptime;
            break;
        }
    }

    // Release lock.
    mLock.unlock();

    // Invoke all response callbacks.
    for (size_t i = 0; i < mResponses.size(); i++) {
        Response& response = mResponses.editItemAt(i);
        if (response.request.ident == POLL_CALLBACK) {
            int fd = response.request.fd;
            int events = response.events;
            void* data = response.request.data;
#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
            ALOGD("%p ~ pollOnce - invoking fd event callback %p: fd=%d, events=0x%x, data=%p",
                    this, response.request.callback.get(), fd, events, data);
#endif
            // Invoke the callback.  Note that the file descriptor may be closed by
            // the callback (and potentially even reused) before the function returns so
            // we need to be a little careful when removing the file descriptor afterwards.
            int callbackResult = response.request.callback->handleEvent(fd, events, data);
            if (callbackResult == 0) {
                removeFd(fd, response.request.seq);
            }

            // Clear the callback reference in the response structure promptly because we
            // will not clear the response vector itself until the next poll.
            response.request.callback.clear();
            result = POLL_CALLBACK;
        }
    }
    return result;
}

  • Message对象池
//默认对象池的数量为50
private static final int MAX_POOL_SIZE = 50;

private static boolean gCheckRecycle = true;
//当前剩余的对象数
private static int sPoolSize = 0;
// 使用对象池创建Message对象,如果当前的消息为空,则new一个
public static Message obtain() {
    synchronized (sPoolSync) {
        if (sPool != null) {
            Message m = sPool;
            sPool = m.next;
            m.next = null;
            m.flags = 0; // clear in-use flag
            sPoolSize--;
            return m;
        }
    }
    return new Message();
}
//回收消息
public void recycle() {
        if (isInUse()) {
            if (gCheckRecycle) {
                throw new IllegalStateException("This message cannot be recycled because it "
                        + "is still in use.");
            }
            return;
        }
        recycleUnchecked();
    }
//真正的回收方法
void recycleUnchecked() {
    // Mark the message as in use while it remains in the recycled object pool.
    // Clear out all other details.
    flags = FLAG_IN_USE;
    what = 0;
    arg1 = 0;
    arg2 = 0;
    obj = null;
    replyTo = null;
    sendingUid = UID_NONE;
    workSourceUid = UID_NONE;
    when = 0;
    target = null;
    callback = null;
    data = null;

    synchronized (sPoolSync) {
        if (sPoolSize < MAX_POOL_SIZE) {
            next = sPool;
            sPool = this;
            sPoolSize++;
        }
    }
}

//消息回收调用时机Looper.loop()方法中,可以看到真实执行回收的调用的时pollOnce()该接口用于判断当前是否处理,决定回收
public static void loop() {
        final Looper me = myLooper();
        ...
        for (;;) {
            if (!loopOnce(me, ident, thresholdOverride)) {
                return;
            }
        }
    }

    private static boolean loopOnce(final Looper me,
            final long ident, final int thresholdOverride) {
        Message msg = me.mQueue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return false;
        }
    ...
        msg.recycleUnchecked();

        return true;
    }

http://www.kler.cn/a/380708.html

相关文章:

  • C#(事件)2
  • uniapp Native.js原生arr插件服务发送广播到uniapp页面中
  • 【点估计】之Python实现
  • vue3入门教程:ref函数
  • Canoe E2E校验自定义Checksum算法
  • 苏黎世联邦理工学院与加州大学伯克利分校推出MaxInfoRL:平衡内在与外在探索的全新强化学习框架
  • 算法: 链表题目练习
  • 前端用docker部署
  • 总是忘记CSS中的transform 和transition的区别
  • 楼梯区域分割系统:Web效果惊艳
  • 【图书管理与推荐系统】Python+Django网页界面+协同过滤推荐算法+网站系统
  • nginx cors配置
  • 【驱动】地平线X3交叉编译工具搭建、源码下载
  • 基于航片的玉米异常情况识别赛题正在报名中 | CCF BDCI进行时
  • element-plus按需引入报错Components is not a function
  • 什么是AOP面向切面编程?怎么简单理解?
  • npm入门教程11:npm私有包和模块
  • 基于Spring Boot的志愿服务管理系统设计与实现,LW+源码+讲解
  • Chrome 130 版本开发者工具(DevTools)更新内容
  • Python学习笔记-生成器的应用与原理
  • CentOS 7 安装 ntp,自动校准系统时间
  • three.js 智慧城市扫光效果
  • Linux系统基础-多线程超详细讲解(5)_单例模式与线程池
  • Android 网易游戏面经
  • 快来了解一下服务器虚拟化!!!
  • FlowDroid 分析最新Android 构建 APK出错