尾声
最后,我再重复一次,如果你想成为一个优秀的 Android 开发人员,请集中精力,对基础和重要的事情做深度研究。
对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。 整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。
这里,笔者分享一份从架构哲学的层面来剖析的视频及资料分享给大家梳理了多年的架构经验,筹备近6个月最新录制的,相信这份视频能给你带来不一样的启发、收获。
Android进阶学习资料库
一共十个专题,包括了Android进阶所有学习资料,Android进阶视频,Flutter,java基础,kotlin,NDK模块,计算机网络,数据结构与算法,微信小程序,面试题解析,framework源码!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
mDispatcherIsAliveCondition.broadcast();
// 当mInboundQueue中有事件产生时,获得锁并处理相关事件
if (!haveCommandsLocked()) {
dispatchOnceInnerLocked(&nextWakeupTime);
}
} // release lock
...
//inputReader读取事件最后把事件放入minBoundQueue后,调用loop::wake唤醒inputDispatcher
mLooper->pollOnce(timeoutMillis);
}
bool InputDispatcher::haveCommandsLocked() const {
return !mCommandQueue.isEmpty();
}
线程执行Looper->pollOnce,进入epoll\_wait等待状态,当发生以下任一情况则退出等待状态:
* **callback**:通过回调方法来唤醒;
* **timeout**:到达nextWakeupTime时间,超时唤醒;
* **wake**: 主动调用Looper的wake()方法;
### 1.1 dispatchOnceInnerLocked
`dispatchOnceInnerLocked`会从`mInboundQueue`队头取出`EntryEvent`赋值给`mPendingEvent`,并根据输入事件的类型作不同的处理,处理完成之后再释放`mPendingEvent`,相关代码位于`frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp`:
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
// 获取当前时间
nsecs_t currentTime = now();
…
// 优化app切换延迟,当切换超时,则抢占分发,丢弃其他所有即将要处理的事件。
bool isAppSwitchDue = mAppSwitchDueTime <= currentTime;
if (mAppSwitchDueTime < *nextWakeupTime) {
*nextWakeupTime = mAppSwitchDueTime;
}
...
// mPendingEvent初始化为空
if (!mPendingEvent) {
if (mInboundQueue.empty()) {
if (isAppSwitchDue) {
// The inbound queue is empty so the app switch key we were waiting
// for will never arrive. Stop waiting for it.
resetPendingAppSwitchLocked(false);
isAppSwitchDue = false;
}
...
if (!mPendingEvent) {
// 如果mInboundQueue队列为空,没有事件需要处理时直接返回
return;
}
} else {
// 从mInboundQueue取出头部的事件
mPendingEvent = mInboundQueue.front();
mInboundQueue.pop_front();
traceInboundQueueLengthLocked();
}
...
}
...
// 事件分发
switch (mPendingEvent->type) {
// 分发配置改变事件
case EventEntry::Type::CONFIGURATION_CHANGED: {
...
}
// 分发输入设备重置事件
case EventEntry::Type::DEVICE_RESET: {
...
}
// 分发焦点事件
case EventEntry::Type::FOCUS: {
...
}
// 分发多点触控时,触控点改变事件
case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
...
}
// 分发拖拽事件
case EventEntry::Type::DRAG: {
...
}
// 分发按键事件
case EventEntry::Type::KEY: {
...
}
// 分发触控事件
case EventEntry::Type::MOTION: {
// 将mPendingEvent强转为MotionEntry
std::shared_ptr<MotionEntry> motionEntry =
std::static_pointer_cast<MotionEntry>(mPendingEvent);
if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) {
dropReason = DropReason::APP_SWITCH;
}
if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *motionEntry)) {
dropReason = DropReason::STALE;
}
if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) {
dropReason = DropReason::BLOCKED;
}
// 如果是Motion Event则会走dispatchMotionLocked分支
done = dispatchMotionLocked(currentTime, motionEntry, &dropReason, nextWakeupTime);
break;
}
// 分发传感器事件
case EventEntry::Type::SENSOR: {
...
}
}
if (done) {
if (dropReason != DropReason::NOT_DROPPED) {
dropInboundEventLocked(*mPendingEvent, dropReason);
}
mLastDropReason = dropReason;
// 处理完成之后释放mPendingEvent
releasePendingEventLocked();
*nextWakeupTime = LONG_LONG_MIN;
}
}
从上述代码中可以看出,当inputDispatcher获取到一个事件后,会根据事件类型不同进行分发,目前有以下几种事件会被分发:
* 配置改变事件[EventEntry::Type::CONFIGURATION\_CHANGED]
* 输入设备重置事件[EventEntry::Type::DEVICE\_RESET]
* 焦点事件[EventEntry::Type::FOCUS]
* 触控点改变事件[EventEntry::Type::POINTER\_CAPTURE\_CHANGED]
* 拖拽事件[EventEntry::Type::DRAG]
* 按键事件[EventEntry::Type::KEY]
* 触控事件[EventEntry::Type::MOTION]
* 传感器事件[EventEntry::Type::SENSOR]
下面以`MotionEvent`事件为例来分析输入事件的处理流程。
### 1.2 dispatchMotionLocked
`dispatchMotionLocked`被用来处理触控事件,相关代码位于`frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp`:
bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr entry,
DropReason* dropReason, nsecs_t* nextWakeupTime) {
…
// 判断是否是Touch事件
bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;
// Identify targets.
std::vector<InputTarget> inputTargets;
bool conflictingPointerActions = false;
InputEventInjectionResult injectionResult;
if (isPointerEvent) {
// 寻找Touch事件的焦点窗口
injectionResult =
findTouchedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime,
&conflictingPointerActions);
} else {
// 寻找非Touch事件的焦点窗口
injectionResult =
findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime);
}
// 对于MotionEvent有4种情况会返回INPUT_EVENT_INJECTION_PENDING状态
// 焦点窗口为空而焦点应用不为空时分两种情况:
// 1.未设置焦点窗口缺失的超时时间;
// 2.焦点窗口缺少的超时时间还未到;
// 3.焦点窗口和焦点应用都不为空时:
// 4.焦点窗口处于暂停状态;
if (injectionResult == InputEventInjectionResult::PENDING) {
return false;
}
...
// 找到目标窗口之后开始分发事件
if (conflictingPointerActions) {
CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
"conflicting pointer actions");
synthesizeCancelationEventsForAllConnectionsLocked(options);
}
dispatchEventLocked(currentTime, entry, inputTargets);
return true;
}
针对`Touch事件`和`非Touch事件`,有两种不同的逻辑来寻找焦点窗口:`findTouchedWindowTargetsLocked`中有很多关于分屏和多`display id`的处理逻辑,相比`findFocusedWindowTargetsLocked`而言代码逻辑稍显复杂,为了聚焦事件分发的主线流程,我们以`findFocusedWindowTargetsLocked`为例来说明焦点窗口的寻找过程;
### 1.3 findFocusedWindowTargetsLocked
`findFocusedWindowTargetsLocked`用于寻找焦点窗口,相关代码位于`frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp`:
int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
const EventEntry& entry,
std::vector& inputTargets,
nsecs_t* nextWakeupTime) {
std::string reason;
// 寻找输入事件的display id,由于Android支持多屏设备,所以可能会有多个display id,默认为0
int32_t displayId = getTargetDisplayId(entry);
// 根据display id查找InputWindowHandle
std::shared_ptr focusedApplicationHandle =
getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
// 根据display id查找InputApplicationHandle
sp focusedApplicationHandle =
getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
// 如果focusedWindowHandle和focusedApplicationHandle同时为空,代表当前即无焦点应用也无焦点窗口,所以直接将此事件丢弃
if (focusedWindowHandle == nullptr && focusedApplicationHandle == nullptr) {
ALOGI("Dropping %s event because there is no focused window or focused application in "
"display %" PRId32 ".",
EventEntry::typeToString(entry.type), displayId);
return INPUT_EVENT_INJECTION_FAILED;
}
// 焦点应用不为空而焦点窗口为空
if (focusedWindowHandle == nullptr && focusedApplicationHandle != nullptr) {
// 未设置焦点窗口缺失的超时时间
if (!mNoFocusedWindowTimeoutTime.has_value()) {
// We just discovered that there's no focused window. Start the ANR timer
// 获取超时时间,默认为5s
const nsecs_t timeout = focusedApplicationHandle->getDispatchingTimeout(
DEFAULT_INPUT_DISPATCHING_TIMEOUT.count());
// 记录超时时间
mNoFocusedWindowTimeoutTime = currentTime + timeout;
// 记录等待焦点窗口的焦点应用
mAwaitedFocusedApplication = focusedApplicationHandle;
ALOGW("Waiting because no window has focus but %s may eventually add a "
"window when it finishes starting up. Will wait for %" PRId64 "ms",
mAwaitedFocusedApplication->getName().c_str(), ns2ms(timeout));
*nextWakeupTime = *mNoFocusedWindowTimeoutTime;
return INPUT_EVENT_INJECTION_PENDING;
} else if (currentTime > *mNoFocusedWindowTimeoutTime) {
// 如果已经超时,则直接丢弃该事件
ALOGE("Dropping %s event because there is no focused window",
EventEntry::typeToString(entry.type));
return INPUT_EVENT_INJECTION_FAILED;
} else {
// 还未到超时时间则继续等待
return INPUT_EVENT_INJECTION_PENDING;
}
}
// 重置超时时间和等待的焦点应用
resetNoFocusedWindowTimeoutLocked();
// Check permissions.
if (!checkInjectionPermission(focusedWindowHandle, entry.injectionState)) {
return INPUT_EVENT_INJECTION_PERMISSION_DENIED;
}
// 焦点窗口已经paused
if (focusedWindowHandle->getInfo()->paused) {
ALOGI("Waiting because %s is paused", focusedWindowHandle->getName().c_str());
return INPUT_EVENT_INJECTION_PENDING;
}
// 成功找到焦点窗口后,将其添加到inputTargets
addWindowTargetLocked(focusedWindowHandle,
InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS,
BitSet32(0), inputTargets);
// Done.
return INPUT_EVENT_INJECTION_SUCCEEDED;
}
这里有两个非常重要的map:`mFocusedWindowHandlesByDisplay`和`mFocusedApplicationHandlesByDisplay`,它们分别存储着当前每个`display`对应的焦点应用和焦点窗口,这些map在`frameworks/native/services/inputflinger/dispatcher/InputDispatcher.h`被声明:
// key为display id,value为InputWindowHandle即焦点窗口
std::unordered_map<int32_t, sp> mFocusedWindowHandlesByDisplay
GUARDED_BY(mLock);
// key为display id,value为InputApplicationHandle即焦点应用
std::unordered_map<int32_t, sp> mFocusedApplicationHandlesByDisplay
GUARDED_BY(mLock);
### 1.4 setInputWindowsLocked
`setInputWindowsLocked`用于设置焦点窗口,相关代码位于`frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp`:
void InputDispatcher::setInputWindowsLocked(
const std::vector<sp>& inputWindowHandles, int32_t displayId) {
…
// 根据displayId从mWindowHandlesByDisplay中查找出当前所有的InputWindowHandle,包括焦点窗口和非焦点窗口
const std::vector<sp<InputWindowHandle>> oldWindowHandles = getWindowHandlesLocked(displayId);
...
sp<InputWindowHandle> newFocusedWindowHandle = nullptr;
bool foundHoveredWindow = false;
for (const sp<InputWindowHandle>& windowHandle : getWindowHandlesLocked(displayId)) {
// Set newFocusedWindowHandle to the top most focused window instead of the last one
// 遍历更新之后的所有窗口列表,并将可见并且获得焦点的窗口置为新的焦点窗口
if (!newFocusedWindowHandle && windowHandle->getInfo()->hasFocus &&
windowHandle->getInfo()->visible) {
newFocusedWindowHandle = windowHandle;
}
if (windowHandle == mLastHoverWindowHandle) {
foundHoveredWindow = true;
}
}
if (!foundHoveredWindow) {
mLastHoverWindowHandle = nullptr;
}
// 根据displayId从mFocusedWindowHandlesByDisplay中查找出当前的焦点窗口
sp<InputWindowHandle> oldFocusedWindowHandle =
getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
// 不同的InputWindowHandle有不同的token
if (!haveSameToken(oldFocusedWindowHandle, newFocusedWindowHandle)) {
if (oldFocusedWindowHandle != nullptr) {
if (DEBUG_FOCUS) {
ALOGD("Focus left window: %s in display %" PRId32,
oldFocusedWindowHandle->getName().c_str(), displayId);
}
// 根据InputWindowHandle中的Token获取到对应的额InputChannel
sp<InputChannel> focusedInputChannel =
getInputChannelLocked(oldFocusedWindowHandle->getToken());
if (focusedInputChannel != nullptr) {
CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
"focus left window");
synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options);
// 往mInboundQueue里添加失去焦点的FocusEntry
enqueueFocusEventLocked(*oldFocusedWindowHandle, false /*hasFocus*/);
}
// 从mFocusedWindowHandlesByDisplay中移除历史的InputWindowHandle
mFocusedWindowHandlesByDisplay.erase(displayId);
}
if (newFocusedWindowHandle != nullptr) {
if (DEBUG_FOCUS) {
ALOGD("Focus entered window: %s in display %" PRId32,
newFocusedWindowHandle->getName().c_str(), displayId);
}
// 更新mFocusedWindowHandlesByDisplay
mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle;
// 往mInboundQueue里添加得到焦点的FocusEntry
enqueueFocusEventLocked(*newFocusedWindowHandle, true /*hasFocus*/);
}
// 往mCommandQueue里添加焦点改变的CommandEntry,通知上层焦点窗口改变
if (mFocusedDisplayId == displayId) {
onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle);
}
}
}
每个`display`都对应若干个`InputWindowHandle`和一个焦点`InputWindowHandle`,此方法会根据传入的`InputWindowHandle`列表来更新`mWindowHandlesByDisplay`和`mFocusedWindowHandlesByDisplay`,调用流程如下所示:
–>frameworks/native/services/surfaceflinger/SurfaceFlinger.onMessageInvalidate
–>frameworks/native/services/surfaceflinger/SurfaceFlinger.updateInputFlinger
–>frameworks/native/services/surfaceflinger/SurfaceFlinger.updateInputWindowInfo
–>frameworks/native/services/inputflinger/IInputFlinger.setInputWindows
–>frameworks/native/services/inputflinger/InputManager.setInputWindows
–>frameworks/native/services/inputflinger/InputDispatcher.setInputWindows
–>frameworks/native/services/inputflinger/InputDispatcher.setInputWindowsLocked
### 1.5 setFocusedApplication
`setFocusedApplication`用于设置焦点应用,相关代码位于`frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp`:
void InputDispatcher::setFocusedApplication(
int32_t displayId, const std::shared_ptr& inputApplicationHandle) {
if (DEBUG_FOCUS) {
ALOGD(“setFocusedApplication displayId=%” PRId32 " %s", displayId,
inputApplicationHandle ? inputApplicationHandle->getName().c_str() : “”);
}
{ // acquire lock
std::scoped_lock _l(mLock);
// 设置焦点应用
setFocusedApplicationLocked(displayId, inputApplicationHandle);
} // release lock
// Wake up poll loop since it may need to make new input dispatching choices.
mLooper->wake();
}
### 1.6 dispatchEventLocked
### 总结
最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的14套腾讯、字节跳动、阿里、百度等**2021最新面试真题解析**,我把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包知识脉络 + 诸多细节。
![2020面试真题解析](https://img-blog.csdnimg.cn/img_convert/6381d7bcc59b6965b9f66f2bad3a2500.webp?x-oss-process=image/format,png)
![腾讯面试真题解析](https://img-blog.csdnimg.cn/img_convert/80ea74cbcfa494911e6ae1e5df97327c.webp?x-oss-process=image/format,png)
![阿里巴巴面试真题解析](https://img-blog.csdnimg.cn/img_convert/9ca63204e1d3e5007b5029400269731d.webp?x-oss-process=image/format,png)
![字节跳动面试真题解析](https://img-blog.csdnimg.cn/img_convert/0dd325548d1f56d6b5f79878c807b593.webp?x-oss-process=image/format,png)
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化学习资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618156601)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
诸多细节。
[外链图片转存中...(img-7Ujouilz-1715272098913)]
[外链图片转存中...(img-2YbQveLC-1715272098913)]
[外链图片转存中...(img-KCwl3KnW-1715272098913)]
[外链图片转存中...(img-sOIeA8tX-1715272098914)]
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化学习资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618156601)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**