Android知识笔记:记录 2 个 “容易误解,送大厂面经一份

本文深入探讨了Android中触摸事件从DecorView到Activity,再通过PhoneWindow返回DecorView的传递过程,揭示了解耦设计背后的原理。同时,文章提到了RecyclerView卡片中资源释放的问题,讨论了在何时以及如何正确地取消倒计时或动画,以确保功能正常且避免内存泄漏。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Native层通过JNI执行Framework层的InputEventReceiver.dispachInputEvent(),而真正调用的是继承了InputEventReceiver的ViewRootImpl.WindowInputEventReceiver。

所以这里执行的WindowInputEventReceiver的dispachInputEvent():

final class WindowInputEventReceiver extends InputEventReceiver {

public void onInputEvent(InputEvent event) {

enqueueInputEvent(event, this, 0, true);

}

}

ViewRootImpl

void enqueueInputEvent(InputEvent event,

InputEventReceiver receiver, int flags, boolean processImmediately) {

if (processImmediately) {

//关键点:执行Input事件

doProcessInputEvents();

} else {

//走一遍Handler延迟处理事件

scheduleProcessInputEvents();

}

}

void doProcessInputEvents() {

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);

}

}

private void deliverInputEvent(QueuedInputEvent q) {

Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, “deliverInputEvent”,

q.mEvent.getSequenceNumber());

if (mInputEventConsistencyVerifier != null) {

mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);

}

InputStage stage;

if (q.shouldSendToSynthesizer()) {

stage = mSyntheticInputStage;

} else {

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

}

if (stage != null) {

//关键点:上面决定将事件派发到那个InputStage中处理

stage.deliver(q);

} else {

finishInputEvent(q);

}

}

ViewRootImpl.ViewPostImeInputStage

前面事件会派发到ViewRootImpl.ViewPostImeInputStage中处理,它的父类InputStage.deliver()方法会调用apply()来处理Touch事件:

@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) {

//关键点:执行分发touch事件

return processPointerEvent(q);

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

return processTrackballEvent(q);

} else {

return processGenericMotionEvent(q);

}

}

}

private int processPointerEvent(QueuedInputEvent q) {

final MotionEvent event = (MotionEvent)q.mEvent;

//关键点:mView分发Touch事件,mView就是DecorView

boolean handled = mView.dispatchPointerEvent(event);

maybeUpdatePointerIcon(event);

maybeUpdateTooltip(event);

}

DecorView

如果你熟悉安卓的Window,Activity和Dialog对应的ViewRootImpl成员mView就是DecorView,View的dispatchPointerEvent()代码如下:

//View.java

public final boolean dispatchPointerEvent(MotionEvent event) {

if (event.isTouchEvent()) {

//分发Touch事件

return dispatchTouchEvent(event);

} else {

return dispatchGenericMotionEvent(event);

}

}

因为DecorView继承FrameLayout,上面所以会调用DecorView的dispatchTouchEvent():

@Override

public boolean dispatchTouchEvent(MotionEvent ev) {

final Window.Callback cb = mWindow.getCallback();

return cb != null && !mWindow.isDestroyed() && mFeatureId < 0

? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);

}

上面Window.Callback都被Activity和Dialog实现,所以变量cb可能就是Activity和Dialog。

Activity

当上面cb是Activity时,执行Activity的dispatchTouchEvent():

public boolean dispatchTouchEvent(MotionEvent ev) {

if (ev.getAction() == MotionEvent.ACTION_DOWN) {

onUserInteraction();

}

if (getWindow().superDispatchTouchEvent(ev)) {//关键点:getWindow().superDispatchTouchEvent(ev)

return true;

}

return onTouchEvent(ev);

}

如果你熟悉安卓的Window,Activity的getWindow()拿到的就是PhoneWindow,下面是PhoneWindow的代码:

//PhoneWindow.java

@Override

public boolean superDispatchTouchEvent(MotionEvent event) {

//调用DecorView的superDispatchTouchEvent

return mDecor.superDispatchTouchEvent(event);

}

下面是DecorView.superDispatchTouchEvent()代码:

//DecorView.java

public boolean superDispatchTouchEvent(MotionEvent event) {

//调用ViewGroup的dispatchTouchEvent()开始我们常见的分发Touch事件

return super.dispatchTouchEvent(event);

}

流程图

答:为什么要DecorView -> Activity -> PhoneWindow -> DecorView传递事件?

解耦!

ViewRootImpl并不知道有Activity这种东西存在!它只是持有了DecorView。

所以,不能直接把触摸事件送到Activity.dispatchTouchEvent();

那么,既然触摸事件已经到了Activity.dispatchTouchEvent()中了,为什么不直接分发给DecorView,而是要通过PhoneWindow来间接发送呢?

因为Activity不知道有DecorView!但是,Activity持有PhoneWindow ,而PhoneWindow当

《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》

开源分享完整内容戳这里

然知道自己的窗口里有些什么了,所以能够把事件派发给DecorView。

在Android中,Activity并不知道自己的Window中有些什么,这样耦合性就很低了。

我们换一个Window试试?

不管Window里面的内容如何,只要Window仍然符合Activity制定的标准,那么它就能在Activity中很好的工作。当然,这就是解耦所带来的扩展性的好处。

以上回答感谢:蔡徐坤打篮球。


2. RecyclerView卡片中持有的资源,到底该什么时候释放?

之前我们讨论过 View的onAttachedToWindow ,onDetachedFromWindow 调用时机 。

这个机制在RecyclerView卡片中还适用吗?

例如我们在RecyclerView的Item的onBindViewHolder时,利用一个CountDownTimer去做一个倒计时显示 / 或者是有一个属性动画效果?

到底在什么时候可以cancel掉这个倒计时/ 动画,而不影响功能了(滑动到用户可见范围内,倒计时/动画 运作正常)?

有什么方法可以和onBindViewHolder 对应吗?就像onAttachedToWindow ,onDetachedFromWindow这样 。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值