Android基础进阶 - 消息机制 之Native层分析,社招面试心得

Message next() {

//native层NativeMessageQueue的指针
final long ptr = mPtr;
if (ptr == 0) {
return null;
}


for (;😉 {

//阻塞操作,当等待nextPollTimeoutMillis时长,或者消息队列被唤醒
//nativePollOnce用于“等待”, 直到下一条消息可用为止. 如果在此调用期间花费的时间很长, 表明对应线程没有实际工作要做,或者Native层的message有耗时的操作在执行
nativePollOnce(ptr, nextPollTimeoutMillis);

}

可以看到在MessageQueue#next会调用课程阻塞的native方法nativePollOnce,在MessageQueue#enqueueMessage 中如果需要唤醒会调用native方法nativeWake
问题是怎么阻塞的,怎么唤醒的,为什么要这样设计,直接在Java层完成处理不可以吗?

带着这样的困惑,开始Native层消息机制的分析学习。

2.1 MessageQueue Init流程

图片来自:Android消息机制2-Handler(Native层)

调用Native方法初始化,返回值为native层的NativeMessageQueue指针地址

//android.os.MessageQueue#MessageQueue
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
//调用Native方法初始化,返回值为native层的NativeMessageQueue指针地址
mPtr = nativeInit();
}

android_os_MessageQueue_nativeInit

//java native方法 nativeInit 的jni方法,返回类型long,即 mptr
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
//NativeMessageQueue是一个内部类
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();

//返回为NativeMessageQueue指针地址

return reinterpret_cast(nativeMessageQueue);
}

NativeMessageQueue构造方法

会进行Native层的Looper创建。Java层创建Looper然后再创建MessageQueue,而在Native层则刚刚相反,先创建NativeMessageQueue然后再创建Looper。

// core/jni/android_os_MessageQueue.cpp

//NativeMessageQueue构造方法
//Java层创建Looper然后再创建MessageQueue,
//而在Native层则刚刚相反,先创建NativeMessageQueue然后再创建Looper

//MessageQueue是在Java层与Native层有着紧密的联系,
//但是Native层的Looper与Java层的Looper没有任何关系
NativeMessageQueue::NativeMessageQueue() :
mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
//Looper::getForThread()获取当前线程的Looper,相当于Java层的Looper.myLooper()
mLooper = Looper::getForThread();
if (mLooper == NULL) {
//如果为空, new一个native层的Looper
mLooper = new Looper(false);
//相当于java层的ThreadLocal.set() ?
Looper::setForThread(mLooper);
}
}

Natvie层的Looper的构造
MessageQueue是在Java层与Native层有着紧密的联系,但是Native层的Looper与Java层的Looper没有任何关系

// libutils/Looper.cpp

Looper::Looper(bool allowNonCallbacks)
mAllowNonCallbacks(allowNonCallbacks),
mSendingMessage(false),
mPolling(false),
mEpollRebuildRequired(false),
mNextRequestSeq(0),
mResponseIndex(0),
mNextMessageUptime(LLONG_MAX) {
//eventfd事件句柄,负责线程通信,替换了之前版本的pipe 构造唤醒事件fd
mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
LOG_ALWAYS_FATAL_IF(mWakeEventFd.get() < 0, “Could not make wake event fd: %s”, strerror(errno));

AutoMutex _l(mLock);
//进行epoll句柄的创建和初始化
rebuildEpollLocked();
}

epoll句柄的创建、添加唤醒事件句柄到epoll

//libutils/Looper.cpp

void Looper::rebuildEpollLocked() {

//epoll_create1创建一个epoll句柄实例,并注册wake管道
mEpollFd.reset(epoll_create1(EPOLL_CLOEXEC));

//epoll事件结构体
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.get();

int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &eventItem);

for (size_t i = 0; i < mRequests.size(); i++) {
const Request& request = mRequests.valueAt(i);
struct epoll_event eventItem;
request.initEventItem(&eventItem);

//将唤醒事件句柄(request.fd),添加到epolled句柄(mEpollFd.get()),为epoll添加一个唤醒机制
int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, request.fd, &eventItem);


}
}

Native层的init流程主要内容如下:

  1. NativeQueueMessage和Looper的初始化。
  2. 构建了epoll句柄,向epoll中添加epoll事件注册

2.2 消息读取流程

图片来自:Android消息机制2-Handler(Native层)

nativePollOnce

//core/jni/android_os_MessageQueue.cpp

static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jlong ptr, jint timeoutMillis) {
//将Java层传过来的mPtr 转换为 nativeMessageQueue
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
//调用nativeMessageQueue的pollOnce
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}

NativeMessageQueue :: pollOnce

//core/jni/android_os_MessageQueue.cpp
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {

//又调用了Natvie的Looper的pollOnce
mLooper->pollOnce(timeoutMillis);

}

Native层Looper::pollOnce

//libutils/Looper.cpp

/**

  • timeoutMillis:超时时长
  • outFd:发生事件的文件描述符
  • outEvents:当前outFd上发生的事件,包含以下4类事件
    EVENT_INPUT:可读
    EVENT_OUTPUT:可写
    EVENT_ERROR:错误
    EVENT_HANGUP:中断

*outData:上下文数据

/
int Looper::pollOnce(int timeoutMillis, int
outFd, int
outEvents, void** outData) {
int result = 0;
for (;😉 {

//关键实现在pollInner中
result = pollInner(timeoutMillis);
}
}

Looper::pollInner
先会调用epoll_wait进入阻塞专题,唤醒的场景 向epoll中添加的epollevent等待事件发生或者超时触发nativeWake()方法,会向eventfd写入字符,进行唤醒。

然后进性要处理的事件收集,然后在做处理。Natvie的消息的处理顺序如下

  1. 处理Native的Message,调用Native的Handler来处理该Message

  2. 处理Resposne数组,POLL_CALLBACK类型的事件

//libutils/Looper.cpp

int Looper::pollInner(int timeoutMillis) {

// Poll.
int result = POLL_WAKE;
mResponses.clear();
mResponseIndex = 0;

// We are about to idle.
mPolling = true;

struct epoll_event eventItems[EPOLL_MAX_EVENTS];

//等待事件发生或者超时触发nativeWake()方法,会向eventfd写入字符
int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);

// No longer idling.
mPolling = false;

// Acquire lock.
mLock.lock();

// Rebuild epoll set if needed.
if (mEpollRebuildRequired) {
mEpollRebuildRequired = false;
rebuildEpollLocked();
goto Done;
}

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

for (int i = 0; i < eventCount; i++) {
int fd = eventItems[i].data.fd;
uint32_t epollEvents = eventItems[i].events;
if (fd == mWakeEventFd.get()) {
//已经唤醒,如果是EPOLLIN类型事件,读取并清空eventfd中的数据
if (epollEvents & EPOLLIN) {
awoken();
}
} else {
ssize_t requestIndex = mRequests.indexOfKey(fd);
if (requestIndex >= 0) {
int events = 0;
if (epollEvents & EPOLLIN) events |= EVENT_INPUT;
if (epollEvents & EPOLLOUT) events |= EVENT_OUTPUT;
if (epollEvents & EPOLLERR) events |= EVENT_ERROR;
if (epollEvents & EPOLLHUP) events |= EVENT_HANGUP;
pushResponse(events, mRequests.valueAt(requestIndex));
}
}
}

//上面是收集,Done中是处理的部分,很多设计都是采用这种设计,逻辑分离

Done: ; //1. 先处理Native的Message,调用Native的Handler来处理该Message

mNextMessageUptime = LLONG_MAX;
while (mMessageEnvelopes.size() != 0) {

if (messageEnvelope.uptime <= now) {

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

img
img

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V:vip204888 备注Android获取(资料价值较高,非无偿)
img

最后

答应大伙的备战金三银四,大厂面试真题来啦!

这份资料我从春招开始,就会将各博客、论坛。网站上等优质的Android开发中高级面试题收集起来,然后全网寻找最优的解答方案。每一道面试题都是百分百的大厂面经真题+最优解答。包知识脉络 + 诸多细节。
节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

《960全网最全Android开发笔记》

《379页Android开发面试宝典》

包含了腾讯、百度、小米、阿里、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。

如何使用它?
1.可以通过目录索引直接翻看需要的知识点,查漏补缺。
2.五角星数表示面试问到的频率,代表重要推荐指数

《507页Android开发相关源码解析》

只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。

真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。

腾讯、字节跳动、阿里、百度等BAT大厂 2020-2021面试真题解析

资料收集不易,如果大家喜欢这篇文章,或者对你有帮助不妨多多点赞转发关注哦。文章会持续更新的。绝对干货!!!

7页Android开发相关源码解析》**

只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。

真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。

[外链图片转存中…(img-mVRM9s7M-1711548945588)]

腾讯、字节跳动、阿里、百度等BAT大厂 2020-2021面试真题解析

[外链图片转存中…(img-6VexAXAO-1711548945588)]

资料收集不易,如果大家喜欢这篇文章,或者对你有帮助不妨多多点赞转发关注哦。文章会持续更新的。绝对干货!!!

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

  • 21
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值