当前位置:网站首页>Analyze Android event distribution mechanism according to popular interview questions (I)
Analyze Android event distribution mechanism according to popular interview questions (I)
2022-07-07 09:52:00 【Time swordsman】
( One ) Overview of event distribution mechanism
Interview questions : You know Android Event distribution mechanism ? Please give us a general introduction
After the click event , First pass it on to Activity Of dispatchTouchEvent Method , This will call getWindow().superDispatchTouchEvent(ev), because PhoneWindow yes Android Window The only implementation class of , So it will pass PhoneWindow Medium mDecor.superDispatchTouchEvent(event) To call the parent class's dispatchTouchEvent Method ,mDecor yes DecorView, It is inherited from FrameLayout,FrameLayout Inherited from ViewGroup, because FrameLayout No rewriting dispatchTouchEvent Method , So in DecorView in :
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
Will call ViewGroup Of dispatchTouchEvent Method , In this method onInterecptTouchEvent Method to determine whether to intercept , Without interception , Can traverse ViewGroup Child elements , Intron view Of dispatchTouchEvent Method , If son view Set up onTouchListener, Is executed onTouch Method , And according to onTouch The return value of the method is true still false Decide whether to execute onTouchEvent Method , If it is false, Then continue onTouchEvent Method , stay onTouchEvent Of ACTION_UP Judging from the events , If set onClickListener, Is executed onClick Method .
( Two ) Source code analysis of high-frequency examination sites in the interview
Interview questions 1: If the father view China doesn't intercept down event , Intercept move,up event , In the child view Set up in requestDisallowInterceptTouchEvent(true);( Ask the father view Don't intercept events ) After this sign , Son view To receive move,up Incident ?
analysis : As the case may be :(1) If son view consumption down event , And set up requestDisallowInterceptTouchEvent(true), Son view You can receive move,up event (2) Son view Don't consume down event , Will only receive down event , Will not receive move,up event . Here's why :
(1) Son view consumption down event : In our father view Don't rewrite dispatchTouchEvent Under the circumstances , Will use ViewGroup Of dispatchTouchEvent Method to distribute Events . Let's first understand the relevant parts of this problem through the source code :
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
...// Omitted code
// Handle an initial down.
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Throw away all previous state when starting a new touch gesture.
// The framework may have dropped the up or cancel event for the previous gesture
// due to an app switch, ANR, or some other state change.
cancelAndClearTouchTargets(ev);
resetTouchState();// If it is down When the incident came , Reset Touch The state of
}
private void resetTouchState() {
clearTouchTargets();
resetCancelNextUpFlag(this);
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;// This state can be achieved by
//requestDisallowInterceptTouchEvent(true) This method sets ,
// It will be reset here
mNestedScrollAxes = SCROLL_AXIS_NONE;
}
The above code means when down Some status resets will be done when the event comes , because down Events are the beginning of event processing , So every time down What happened , Are equivalent to the beginning of an event processing , Relevant status needs to be reset .
Then it is to determine whether to intercept the source code of the event part :
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
......// Omitted code
// Check for interception.
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
// If the event is down Words of events ,disallowIntercept
// It will always be false, because down The state of was reset at the beginning
//mFirstTouchTarget It's a linked list , It is used to save the information that the user clicks for the first time , If son view Consuming Events , Will assign a value to this variable , Otherwise, it will not
if (!disallowIntercept) {
// therefore down When an event is , This judgment is bound to enter
intercepted = onInterceptTouchEvent(ev);
// Finally, whether to intercept down The event depends on this , If it intercepts down event
// onInterceptTouchEvent(ev) by true, that
// Son view You won't receive the event , It's no use setting any flags , If we don't intercept ,down
// Events can be passed through , Son view Have the opportunity to deal with Events
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
intercepted = true;
}
After the above code is executed, you will get a intercepted value , This value will directly affect the distribution of events , Also in viewGroup Of dispatchTouchEvent In the method :
if (!canceled && !intercepted) {
// Without interception, you will enter the following code block
....// Omitted code
final ArrayList<View> preorderedList = buildTouchDispatchChildList();// Ergodic view
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();// Check whether the sub view The order of drawing
final View[] children = mChildren;
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = getAndVerifyPreorderedIndex(
childrenCount, i, customOrder);
// Get child view The index of the value
final View child = getAndVerifyPreorderedView(
preorderedList, children, childIndex);
if (!child.canReceivePointerEvents()
|| !isTransformedTouchPointInView(x, y, child, null)) {
// If son view Can't receive the event or it is not within the click range , That is, I didn't click view On , End this cycle , Perform the next loop traversal
continue;
}
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
//newTouchTarget Not empty , Represents the current view The incident has been dealt with , End the cycle directly .
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
resetCancelNextUpFlag(child);
//dispatchTransformedTouchEvent This method is to ask view Whether to deal with the incident , If processed, return true, Do not process the return false
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// Child wants to receive touch within its bounds.
mLastTouchDownTime = ev.getDownTime();
if (preorderedList != null) {
// childIndex points into presorted list, find original index
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
mLastTouchDownIndex = childIndex;
}
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
newTouchTarget = addTouchTarget(child, idBitsToAssign);// If view If you deal with the incident, you will pass addTouchTarget() by mFirstTouchTarget assignment , follow-up move and up Events will not be intercepted .
alreadyDispatchedToNewTouchTarget = true;
break;
}
// The accessibility focus didn't handle the event, so clear
// the flag and do a normal dispatch to all children.
ev.setTargetAccessibilityFocus(false);
}
if (preorderedList != null) preorderedList.clear();
}
if (newTouchTarget == null && mFirstTouchTarget != null) {
// Did not find a child to receive the event.
// Assign the pointer to the least recently added target.
newTouchTarget = mFirstTouchTarget;
while (newTouchTarget.next != null) {
newTouchTarget = newTouchTarget.next;
}
newTouchTarget.pointerIdBits |= idBitsToAssign;
}
}
}
So the situation 1 can be summarized as :
(1)down After the event , First, the relevant state will be reset , such as mFirstTouchTarget,FLAG_DISALLOW_INTERCEPT etc.
(2) Then it will judge whether to intercept the event , If it is down event , It will directly enter the code block of whether to intercept , At this time, I will judge disallowIntercept This flag bit , But because of down event , This flag bit has been reset , therefore down In the event of , This flag bit has always been false, therefore down The event will enter the code block of judgment , from onInterceptTouchEvent Decide whether to intercept , If it intercepts , Son view You won't receive the event , What is discussed here is the case without interception :
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
}
(3) Don't intercept down In the event of ,mFirstTouchTarget Will be assigned , So when move and up When the event comes , You will enter the following code block
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
//mFirstTouchTarget Not empty , Will go into the block of code to determine whether to intercept
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;//disallowIntercept Set to true, Will not enter the following judgment code block , Therefore, the father will not be executed view Of onInterceptTouchEvent(ev) Method , So in the father view It's useless to set any interception in , It can't be implemented anyway . If it is set to false, Immediate son view Set up requestDisallowInterceptTouchEvent(false), Then it will enter the following code block , Whether to intercept or not depends on the parent view Of onInterceptTouchEvent(ev) The method decides
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
}
Through the above source code analysis, we can know , In the father view Don't intercept down At the time of the event , Son view Set up requestDisallowInterceptTouchEvent(true), Then even father view Intercepted move and up Events are useless , Because it can't be executed by the father view Of onInterceptTouchEvent(ev) Method . If son view Set up requestDisallowInterceptTouchEvent(false) after , Then view Whether you can receive the event depends on your father view Of onInterceptTouchEvent Method processing logic , In this question is interception move and up event , So son view You will only receive one father view There is no interception down event , And one. cancel event . Will receive cancel The event is because move and up Events have been intercepted , It will not enter into the event distribution logic , That is, the father view Of :
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
......
if (!canceled && !intercepted) {
......// Omit
}
In the child view In the case of consumption events ( Son view Of dispatchTouchEvent return true)
// Son view Consumer events , be mFirstTouchTarget Not empty , If son view Do not consume events , Then father view I will deal with the incident by myself
if (mFirstTouchTarget == null) {
// No touch targets so treat this as an ordinary view.
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
while (target != null) {
final TouchTarget next = target.next;
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;// Because of intercepting move and up event , So this cancelChild Would be true,
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
......
}
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
// Canceling motions is a special case. We don't need to perform any transformations
// or filtering. The important part is the action, not the contents.
final int oldAction = event.getAction();
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
// cancel It's from above cancelChild, There will be a ACTION_CANCEL event , So son view You will receive this event in the case of consumption event
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}
So in self view In the case of consumption events , Father view Zhongruo move and up The event was intercepted , Then son view I'll get one cancel event .
Interview questions 2: If view Set up onTouchListener,onClickListener,onTouchEvent, Which one will be executed first ?
analysis : In this place , When answering, many people may answer , Execute first onTouchListener Of onTouch Method . And then it's gone , There is nothing wrong with this answer on the surface , But it's easy to kill chatting . The interviewer doesn't know how to ask , I can only spit out three words , Why? ? Based on my interview experience , It's hard to get the approval of the interviewer , Even if you answer correctly, which function to execute first , It will only make the interviewer feel that you are on the back of the test question , Although we often recite , But we need to clarify the principle , Try not to let the interviewer ask too many questions . Strive for an answer and answer all the questions he wants to ask .
Now let's see how to answer this question , The interviewer asks you this question , I just want to check your understanding of the event distribution mechanism . Let's look at the source code first ; space in between view Don't rewrite dispatchTouchEvent When , The event was distributed to view after , Will execute view Of dispatchTouchEvent() Method .
public boolean dispatchTouchEvent(MotionEvent event) {
...// Omit irrelevant code
// It's not hard to see from the code below , When the user sets the onTouchListener When monitoring , Will execute onTouch Method , according to onTouch() Method to determine whether to execute onTouchEvent.
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
// If onTouch() Return to true, The following code will not be executed , because java The condition is short circuited , That is to say if //(!result) On the condition that false, With another condition “&&” If you connect , Then the latter condition will not be implemented , The whole judgment returns false
if (!result && onTouchEvent(event)) {
result = true;
}
From the above code, we can know , If the user has set onTouchListener,onTouchevent and onClickListener Method , Then it will be executed first onTouchListener Medium onTouch Method , And then according to onTouch The return result of determines whether to execute onTouchEvent, hypothesis onTouch() return false, Carry on onTouchEvent(), Let's keep looking at the source code :
public boolean onTouchEvent(MotionEvent event) {
... Omitted code
case MotionEvent.ACTION_UP:
//...... Omitted code
// In the event of ACTION_UP Will carry out clickListener Of onClick() Method .
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClickInternal();
}
}
}
public boolean performClick() {
// We still need to call this method to handle the cases where performClick() was called
// externally, instead of through performClickInternal()
notifyAutofillManagerOnClick();
final boolean result;
final ListenerInfo li = mListenerInfo;
// If the user has set onClickListener Words . Will execute onClick() Method
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
notifyEnterOrExitForAutoFillIfNeeded(true);
return result;
}
Sum up : Our answer should be , If the user has set onTouchListener,onTouchEvent,onClickListener when , First of all, it will execute onTouchEvent Of onTouch Method , according to onTouch The return value of determines whether to execute onTouchEvent Method , If onTouch Method returns false, Then continue onTouchEvent, If onTouchEvent There is no direct return true perhaps false( This will cause the event to fail view Of OnTouchEvent Method , As a result, our settings will not be implemented onClickListener Medium onClick Method ), Then it will be implemented to view Of onTouchEvent Method , Then it will be executed ClickListener Of onClick Method
Length is too long , So the sliding conflict problem of event distribution mechanism is summarized in the next blog , It is also the way of problem plus source code analysis , I hope all friends can put forward valuable suggestions , Make progress together ~~~~, Together read the fucking sorce code!
边栏推荐
- 创建一个长度为6的int型数组,要求数组元素的值都在1-30之间,且是随机赋值。同时,要求元素的值各不相同。
- 如何成为一名高级数字 IC 设计工程师(5-3)理论篇:ULP 低功耗设计技术精讲(下)
- CMD startup software passes in parameters with spaces
- 【BW16 应用篇】安信可BW16模组/开发板AT指令实现MQTT通讯
- How to become a senior digital IC Design Engineer (5-3) theory: ULP low power design technology (Part 2)
- [Frida practice] "one line" code teaches you to obtain all Lua scripts in wegame platform
- flinkcdc采集oracle在snapshot阶段一直失败,这个得怎么调整啊?
- Thinkphp3.2 information disclosure
- 2020CCPC威海 J - Steins;Game (sg函数、线性基)
- Switching value signal anti shake FB of PLC signal processing series
猜你喜欢
Detailed explanation of diffusion model
内存==c语言1
[4g/5g/6g topic foundation -147]: Interpretation of the white paper on 6G's overall vision and potential key technologies -2-6g's macro driving force for development
【frida实战】“一行”代码教你获取WeGame平台中所有的lua脚本
印象笔记终于支持默认markdown预览模式
js逆向教程第二发-猿人学第一题
【BW16 应用篇】安信可BW16模组/开发板AT指令实现MQTT通讯
Dynamics 365Online ApplicationUser创建方式变更
第一讲:寻找矩阵的极小值
[4G/5G/6G专题基础-147]: 6G总体愿景与潜在关键技术白皮书解读-2-6G发展的宏观驱动力
随机推荐
Communication mode between processes
Addition, deletion, modification and query of ThinkPHP database
2020浙江省赛
What development models did you know during the interview? Just read this one
H5 web player easyplayer How does JS realize live video real-time recording?
Deep understanding of UDP, TCP
Vs2013 generate solutions super slow solutions
Gym - 102219j kitchen plates (violent or topological sequence)
In fact, it's very simple. It teaches you to easily realize the cool data visualization big screen
Flex flexible layout
第十四次试验
CentOS installs JDK1.8 and mysql5 and 8 (the same command 58 in the second installation mode is common, opening access rights and changing passwords)
MongoDB怎么实现创建删除数据库、创建删除表、数据增删改查
小程序弹出半角遮罩层
Strategic cooperation subquery becomes the secret weapon of Octopus web browser
基于智慧城市与储住分离数字家居模式垃圾处理方法
How to become a senior digital IC Design Engineer (1-6) Verilog coding Grammar: Classic Digital IC Design
大佬们,请问 MySQL-CDC 有什么办法将 upsert 消息转换为 append only 消
CDZSC_ 2022 winter vacation personal training match level 21 (1)
The industrial chain of consumer Internet is actually very short. It only undertakes the role of docking and matchmaking between upstream and downstream platforms