2024年安卓最全那天有人问我,Android 的事件到底是怎么来的?,阿里巴巴四面面试

最后

现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水!

为什么某些人会一直比你优秀,是因为他本身就很优秀还一直在持续努力变得更优秀,而你是不是还在满足于现状内心在窃喜!

Android架构师之路很漫长,一起共勉吧!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

context = getContext();

} else {

context = new DecorContext(applicationContext, this);

if (mTheme != -1) {

context.setTheme(mTheme);

}

}

} else {

context = getContext();

}

return new DecorView(context, featureId, this, getAttributes());

}

2.2. activity.makeVisible()过程

void makeVisible() {

if (!mWindowAdded) {

ViewManager wm = getWindowManager();

//此时调用的其实事WindowManager.addView()

wm.addView(mDecor, getWindow().getAttributes());

mWindowAdded = true;

}

mDecor.setVisibility(View.VISIBLE);

}

3. WindowManager是接口,找到其实现类并调用WindowManagerImpl.addView()

@Override

public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {

applyDefaultToken(params);

//此时调用的是WindowManagerGlobal.addView()

mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,

mContext.getUserId());

}

4. WindowManagerGlobal.addView() 初始化ViewRootImpl,然后调用root.setView()

public void addView(View view, ViewGroup.LayoutParams params,

Display display, Window parentWindow, int userId) {

synchronized (mLock) {

//初始化ViewRootImpl

root = new ViewRootImpl(view.getContext(), display);

view.setLayoutParams(wparams);

mViews.add(view);

mRoots.add(root);

mParams.add(wparams);

// do this last because it fires off messages to start doing things

try {

root.setView(view, wparams, panelParentView, userId);

} catch (RuntimeException e) {

// BadTokenException or InvalidDisplayException, clean up.

if (index >= 0) {

removeViewLocked(index, true);

}

throw e;

}

}

}

5. ViewRootImpl.setView(view,…,…) 创建InputChannel, InputQueue, WindowInputEventReceiver

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView, int userId) {

synchronized (this) {

if (mView == null) {

mView = view;

// Schedule the first layout -before- adding to the window

// manager, to make sure we do the relayout before receiving

// any other events from the system.

requestLayout();

InputChannel inputChannel = null;

if ((mWindowAttributes.inputFeatures

& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {

inputChannel = new InputChannel();

}

if (inputChannel != null) {

if (mInputQueueCallback != null) {

//创建对象

mInputQueue = new InputQueue();

mInputQueueCallback.onInputQueueCreated(mInputQueue);

}

mInputEventReceiver = new WindowInputEventReceiver(inputChannel,

Looper.myLooper());

}

}

}

6. 其实Android事件的源头来自于用户输入行为,由硬件进行捕获,一般会保存在 dev/input 节点下,后续组装成KeyEvent/MotionEvent对象,经Native进入Java的InputEventReceiver.dispatchInputEvent()中。

我们先分析InputEventReceiver

private void dispatchInputEvent(int seq, InputEvent event) {

mSeqMap.put(event.getSequenceNumber(), seq);

onInputEvent(event);

}

public void onInputEvent(InputEvent event) {

finishInputEvent(event, false);

}

由于InputEventReceiver是abstract类,因此需要找到对应的实现类,此时第5步中 WindowInputEventReceiver 即是实现类,因此找到重写的onInputEvent()方法进行下一步分析。

7. ViewRootImpl.WindowInputEventReceiver extends InputEventReceiver,连带调用了enqueueInputEvent()->doProcessInputEvents-> deliverInputEvent(q),方法中获取到mFirstPostImeInputStage对象其实为ViewPostImeInputStage。

@Override

public void onInputEvent(InputEvent event) {

Trace.traceBegin(Trace.TRACE_TAG_VIEW, “processInputEventForCompatibility”);

List processedEvents;

try {

processedEvents =

mInputCompatProcessor.processInputEventForCompatibility(event);

} finally {

Trace.traceEnd(Trace.TRACE_TAG_VIEW);

}

if (processedEvents != null) {

if (processedEvents.isEmpty()) {

// InputEvent consumed by mInputCompatProcessor

finishInputEvent(event, true);

} else {

for (int i = 0; i < processedEvents.size(); i++) {

enqueueInputEvent(

processedEvents.get(i), this,

QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY, true);

}

}

} else {

enqueueInputEvent(event, this, 0, true);

}

}

void enqueueInputEvent(InputEvent event, InputEventReceiver receiver, int flags, boolean processImmediately) {

QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);

// Always enqueue the input event in order, regardless of its time stamp.

// We do this because the application or the IME may inject key events

// in response to touch events and we want to ensure that the injected keys

// are processed in the order they were received and we cannot trust that

// the time stamp of injected events are monotonic.

QueuedInputEvent last = mPendingInputEventTail;

if (last == null) {

mPendingInputEventHead = q;

mPendingInputEventTail = q;

} else {

last.mNext = q;

mPendingInputEventTail = q;

}

mPendingInputEventCount += 1;

Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,

mPendingInputEventCount);

if (processImmediately) {

doProcessInputEvents();

} else {

scheduleProcessInputEvents();

}

}

void doProcessInputEvents() {

// Deliver all pending input events in the queue.

while (mPendingInputEventHead != null) {

QueuedInputEvent q = mPendingInputEventHead;

mPendingInputEventHead = q.mNext;

if (mPendingInputEventHead == null) {

mPendingInputEventTail = null;

}

q.mNext = null;

mPendingInputEventCount -= 1;

Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,

mPendingInputEventCount);

long eventTime = q.mEvent.getEventTimeNano();

long oldestEventTime = eventTime;

if (q.mEvent instanceof MotionEvent) {

MotionEvent me = (MotionEvent)q.mEvent;

if (me.getHistorySize() > 0) {

oldestEventTime = me.getHistoricalEventTimeNano(0);

}

}

mChoreographer.mFrameInfo.updateInputEventTime(eventTime, oldestEventTime);

deliverInputEvent(q);

}

// We are done processing all input events that we can process right now

// so we can clear the pending flag immediately.

if (mProcessInputEventsScheduled) {

mProcessInputEventsScheduled = false;

mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);

}

}

private void deliverInputEvent(QueuedInputEvent q) {

try {

InputStage stage;

if (q.shouldSendToSynthesizer()) {

stage = mSyntheticInputStage;

} else {

//此时返回两个InputStage类型的对象,这个InputStage为abstract因此需要找对应的实现类。

stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;

}

if (q.mEvent instanceof KeyEvent) {

Trace.traceBegin(Trace.TRACE_TAG_VIEW, “preDispatchToUnhandledKeyManager”);

try {

mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent);

} finally {

Trace.traceEnd(Trace.TRACE_TAG_VIEW);

}

}

if (stage != null) {

handleWindowFocusChanged();

stage.deliver(q);

} else {

finishInputEvent(q);

}

} finally {

Trace.traceEnd(Trace.TRACE_TAG_VIEW);

}

}

此时需要注意这个InputStage 的实现类相对比较多,不过可以重点看下setView()方法的末尾,着重看mFirstPostImeInputStage赋值逻辑。

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView, int userId) {

// Set up the input pipeline.

CharSequence counterSuffix = attrs.getTitle();

mSyntheticInputStage = new SyntheticInputStage();

//最终new了ViewPostImeInputStage

InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);

//继续看viewPostImeStage赋值

InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,

“aq:native-post-ime:” + counterSuffix);

//初始化并传入nativePostImeStage,需要查看nativePostImeStage赋值逻辑,继续往上看。

InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);

InputStage imeStage = new ImeInputStage(earlyPostImeStage,

“aq:ime:” + counterSuffix);

InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);

InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,

“aq:native-pre-ime:” + counterSuffix);

mFirstInputStage = nativePreImeStage;

//这是赋值位置,可以看以上逻辑是怎么赋值earlyPostImeStage对象

mFirstPostImeInputStage = earlyPostImeStage;

mPendingInputEventQueueLengthCounterName = “aq:pending:” + counterSuffix;

if (mView instanceof RootViewSurfaceTaker) {

PendingInsetsController pendingInsetsController =

((RootViewSurfaceTaker) mView).providePendingInsetsController();

if (pendingInsetsController != null) {

pendingInsetsController.replayAndAttach(mInsetsController);

}

}

}

通过以上逻辑可以清晰地看到mFirstPostImeInputStage 即是ViewPostImeInputStage,我们需要跟进ViewPostImeInputStage看下对应的onProcess()方法。

final class ViewPostImeInputStage extends InputStage {

public ViewPostImeInputStage(InputStage next) {

super(next);

}

@Override

protected int onProcess(QueuedInputEvent q) {

if (q.mEvent instanceof KeyEvent) {

return processKeyEvent(q);

} else {

final int source = q.mEvent.getSource();

if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {

return processPointerEvent(q);

} else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {

return processTrackballEvent(q);

} else {

return processGenericMotionEvent(q);

}

}

}

}

以上逻辑中先判断了输入事件是键盘,还是触摸,触摸逻辑中又判断了输入源是屏幕还是trackball(我猜测这判断的是鼠标等设备输入),不过不重要,我们主要看屏幕输入的逻辑,也就是processPointerEvent(q);方法

private int processPointerEvent(QueuedInputEvent q) {

final MotionEvent event = (MotionEvent)q.mEvent;

mAttachInfo.mUnbufferedDispatchRequested = false;

mAttachInfo.mHandlingPointerEvent = true;

//主要看这个地方,mView即是DecorView

boolean handled = mView.dispatchPointerEvent(event);

maybeUpdatePointerIcon(event);

maybeUpdateTooltip(event);

mAttachInfo.mHandlingPointerEvent = false;

if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {

mUnbufferedInputDispatch = true;

if (mConsumeBatchedInputScheduled) {

scheduleConsumeBatchedInputImmediately();

}

}

return handled ? FINISH_HANDLED : FORWARD;

}

到此我需要简单做个总结,来捋一下这个流程,方便大家理解。

其实以上的mView即是DecorView。

该对象是在最开始通过PhoneWindow创建。

由WindowManager.addView(View v…)传递到WindowManagerGlobal.addView(View v…)。

然后通过ViewRootImpl.setView(View v…)传到了ViewRootImpl。

因此ViewRootImpl的变量mView即是DecorView,所以我们需要跟进DecorView.dispatchPointerEvent()

由于DecorView extends View,因此先看View.dispatchPointerEvent()

public final boolean dispatchPointerEvent(MotionEvent event) {

if (event.isTouchEvent()) {

//调用dispatchTouchEvent(),而DecorView重写了dispatchTouchEvent()

return dispatchTouchEvent(event);

} else {

return dispatchGenericMotionEvent(event);

}

}

尾声

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。 整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

最后想要拿高薪实现技术提升薪水得到质的飞跃。最快捷的方式,就是有人可以带着你一起分析,这样学习起来最为高效,所以为了大家能够顺利进阶中高级、架构师,我特地为大家准备了一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。

  • 思维脑图
  • 性能优化学习笔记


  • 性能优化视频

    当你有了学习线路,学习哪些内容,也知道以后的路怎么走了,理论看多了总要实践的。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

最后想要拿高薪实现技术提升薪水得到质的飞跃。最快捷的方式,就是有人可以带着你一起分析,这样学习起来最为高效,所以为了大家能够顺利进阶中高级、架构师,我特地为大家准备了一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。

  • 思维脑图
    [外链图片转存中…(img-SzNLaiMJ-1715764198366)]
  • 性能优化学习笔记
    [外链图片转存中…(img-4vBhf6xK-1715764198366)]
    [外链图片转存中…(img-E1Z5DcBz-1715764198367)]

[外链图片转存中…(img-MqmMjKx6-1715764198367)]
[外链图片转存中…(img-Zy3ZvdEt-1715764198368)]

  • 性能优化视频
    [外链图片转存中…(img-BdZCObXC-1715764198368)]
    当你有了学习线路,学习哪些内容,也知道以后的路怎么走了,理论看多了总要实践的。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值