【转】Android笔记:触摸事件的分析与总结----Touch事件分发方法dispatchTouchEvent()源码分析

简介:

触摸事件学习系列文章详见:

《Android Touch事件学习系列汇总》


当前文章的源码基于Android 4.0 (Android 14)


一、Android如何分发事件

    从上一篇文章《Android Touch事件学习 7 交给哪个视图处理事件?》可以简单通过LOG角度了解下调用次序,本片对其具体分发的源码进行解读。

wKioL1QjhA_R90OxAACqAyjiRiU792.jpg


wKiom1QjhACgFDVdAAC0MQWExkE946.jpg


查看点击绿色视图的LOG

1
2
3
4
5
6
7
8
9
RelativeLayout dispatchTouchEvent ACTION_DOWN
LinearLayout dispatchTouchEvent ACTION_DOWN
CustomView dispatchTouchEvent ACTION_DOWN
CustomView onTouchEvent ACTION_DOWN
 
RelativeLayout dispatchTouchEvent ACTION_UP
LinearLayout dispatchTouchEvent ACTION_UP
CustomView dispatchTouchEvent ACTION_UP
CustomView onTouchEvent ACTION_UP



二、ViewGroup的子类(各种布局)如何分发事件?

    ViewGroup的子类都是布局,例如:RelativeLaout、LiearLayout、FrameLayout、GridLayout、AbsoluteLayout等都是其子类。


    结合上面的图片可以看到dispatchTouchEvent是针对UI树形结构由上向下传递,执行每一个View的dispatchTouchEvent方法,其虽然是View视图的方法,但是ViewGroup对其进行了覆写,ViewGroup.dispatchTouchEvent复杂一点,按照上面的图片和LOG来看是先执行RelativeLayout.dispatchTouchEvent,之后执行LinearLayout.dispatchTouchEvent方法,因为RelativeLayout与LinearLayout都没有覆写此方法且都是ViewGroup的子类,所以这两个ViewGroup在传递视图的时候都是执行ViewGroup.dispatchTouchEvent。


     下面就先来看看ViewGroup.dispatchTouchEvent的源码,其中注释中标注数字的地方是在后面会显示相应的解释或者方法源码等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
     /**
      * {@inheritDoc}
      */
     @Override
     public  boolean  dispatchTouchEvent(MotionEvent ev)
     {
         // 1. 用于测试目,直接忽略
         if  (mInputEventConsistencyVerifier !=  null )
         {
             mInputEventConsistencyVerifier.onTouchEvent(ev,  1 );
         }
         
         boolean  handled =  false ;
         // 2. 未被其他窗口遮盖
         if  (onFilterTouchEventForSecurity(ev))
         {
             final  int  action = ev.getAction();
             final  int  actionMasked = action & MotionEvent.ACTION_MASK;
             
             // 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.
                 // 3. 清理触摸操作的所有痕迹,即派发取消操作
                 cancelAndClearTouchTargets(ev);
                 resetTouchState();
             }
             
             // 检测是否拦截Touch Event
             // Check for interception.
             final  boolean  intercepted;
             if  (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget !=  null )
             {
                 // 是否允许拦截,可以通过requestDisallowInterceptTouchEvent方法设置
                 final  boolean  disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) !=  0 ;
                 if  (!disallowIntercept)
                 {
                     // 允许拦截
                     intercepted = onInterceptTouchEvent(ev);
                     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 ;
             }
             
             // Check for cancelation.
             final  boolean  canceled = resetCancelNextUpFlag( this ) || actionMasked == MotionEvent.ACTION_CANCEL;
             
             // Update list of touch targets for pointer down, if needed.
             final  boolean  split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) !=  0 ;
             TouchTarget newTouchTarget =  null ;
             boolean  alreadyDispatchedToNewTouchTarget =  false ;
             // 如果Touch Event没有取消并且没有被拦截,才会考虑是否向其子视图派发
             if  (!canceled && !intercepted)
             {
                 if  (actionMasked == MotionEvent.ACTION_DOWN || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                         || actionMasked == MotionEvent.ACTION_HOVER_MOVE)
                 {
                     final  int  actionIndex = ev.getActionIndex();  // always 0 for
                                                                  // down
                     final  int  idBitsToAssign = split ?  1  << ev.getPointerId(actionIndex) : TouchTarget.ALL_POINTER_IDS;
                     
                     // Clean up earlier touch targets for this pointer id in
                     // case they
                     // have become out of sync.
                     removePointersFromTouchTargets(idBitsToAssign);
                     
                     final  int  childrenCount = mChildrenCount;
                     if  (childrenCount !=  0 )
                     {
                         // Find a child that can receive the event.
                         // Scan children from front to back.
                         final  View[] children = mChildren;
                         final  float  x = ev.getX(actionIndex);
                         final  float  y = ev.getY(actionIndex);
                         
                         // 遍历当前ViewGroup的所有子视图
                         for  ( int  i = childrenCount -  1 ; i >=  0 ; i--)
                         {
                             final  View child = children[i];
                             // 4 与5
                             if  (!canViewReceivePointerEvents(child) || !isTransformedTouchPointInView(x, y, child,  null ))
                             {
                                 // 满足以上条件,没必要接收Touch Event
                                 continue ;
                             }
                             
                             // 如果是在子视图上触摸,经过以上过滤条件,只有当前手指正下方的子视图才会获取到此事件
                             // 子视图是否在TouchTarget中
                             newTouchTarget = getTouchTarget(child);
                             if  (newTouchTarget !=  null )
                             {
                                 // Child is already receiving touch within its
                                 // bounds.
                                 // Give it the new pointer in addition to the
                                 // ones it is handling.
                                 newTouchTarget.pointerIdBits |= idBitsToAssign;
                                 break ;
                             }
                             
                             resetCancelNextUpFlag(child);
                             // 6. 执行触摸操作(会传递到最终视图的onTouchEvent方法)
                             if  (dispatchTransformedTouchEvent(ev,  false , child, idBitsToAssign))
                             {
                                 // Child wants to receive touch within its
                                 // bounds.
                                 mLastTouchDownTime = ev.getDownTime();
                                 mLastTouchDownIndex = i;
                                 mLastTouchDownX = ev.getX();
                                 mLastTouchDownY = ev.getY();
                                 // 会改变mFirstTouchTarget的值
                                 newTouchTarget = addTouchTarget(child, idBitsToAssign);
                                 alreadyDispatchedToNewTouchTarget =  true ;
                                 break ;
                             }
                         }
                     }
                     
                     // 没有发现可以接收事件的子视图
                     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;
                     }
                 }
             }
             
             // 子视图未消耗Touch Event 或者 被拦截未向子视图派发Touch Event
             // Dispatch to touch targets.
             if  (mFirstTouchTarget ==  null )
             {
                 // 由当前ViewGroup处理Touch Event
                 // No touch targets so treat this as an ordinary view.
                 handled = dispatchTransformedTouchEvent(ev, canceled,  null , TouchTarget.ALL_POINTER_IDS);
             }
             else
             {
                 // Dispatch to touch targets, excluding the new touch target if
                 // we already
                 // dispatched to it. Cancel touch targets if necessary.
                 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;
                         if  (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits))
                         {
                             handled =  true ;
                         }
                         if  (cancelChild)
                         {
                             if  (predecessor ==  null )
                             {
                                 mFirstTouchTarget = next;
                             }
                             else
                             {
                                 predecessor.next = next;
                             }
                             target.recycle();
                             target = next;
                             continue ;
                         }
                     }
                     predecessor = target;
                     target = next;
                 }
             }
             
             // Update list of touch targets for pointer up or cancel, if needed.
             if  (canceled || actionMasked == MotionEvent.ACTION_UP || actionMasked == MotionEvent.ACTION_HOVER_MOVE)
             {
                 resetTouchState();
             }
             else  if  (split && actionMasked == MotionEvent.ACTION_POINTER_UP)
             {
                 final  int  actionIndex = ev.getActionIndex();
                 final  int  idBitsToRemove =  1  << ev.getPointerId(actionIndex);
                 removePointersFromTouchTargets(idBitsToRemove);
             }
         }
         
         // 1. 用于测试目,直接忽略
         if  (!handled && mInputEventConsistencyVerifier !=  null )
         {
             mInputEventConsistencyVerifier.onUnhandledEvent(ev,  1 );
         }
         return  handled;
     }


1. View.mInputEventConsistencyVerifier

1
2
3
4
5
6
     /**
      * Consistency verifier for debugging purposes.
      * @hide
      */
     protected  final  InputEventConsistencyVerifier mInputEventConsistencyVerifier =
             InputEventConsistencyVerifier.isInstrumentationEnabled() ?  new  InputEventConsistencyVerifier( this 0 ) :  null ;


2.  View.onFilterTouchEventForSecurity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
     /**
      * Filter the touch event to apply security policies.
     
      * @param event
      *            The motion event to be filtered.
      * @return True if the event should be dispatched, false if the event should
      *         be dropped.
     
      * @see #getFilterTouchesWhenObscured
      */
     public  boolean  onFilterTouchEventForSecurity(MotionEvent event)
     {
         // noinspection RedundantIfStatement
         // 当前被其他窗口遮挡时需要过滤触摸事件。
         if  ((mViewFlags & FILTER_TOUCHES_WHEN_OBSCURED) !=  0  && (event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) !=  0 )
         {
             // Window is obscured, drop this touch.
             return  false ;
         }
         return  true ;
     }


3. ViewGroup.cancelAndClearTouchTargets

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
     /**
      * Cancels and clears all touch targets.
      */
     private  void  cancelAndClearTouchTargets(MotionEvent event)
     {
         // ## 如果指向其他视图,这里进行统一清理
         if  (mFirstTouchTarget !=  null )
         {
             boolean  syntheticEvent =  false ;
             if  (event ==  null )
             {
                 // 如果参数为空
                 final  long  now = SystemClock.uptimeMillis();
                 // 使用静态方法一个MotionEvent对象,动作为ACTION_CANCEL
                 event = MotionEvent.obtain(now, now, MotionEvent.ACTION_CANCEL,  0 .0f,  0 .0f,  0 );
                 // 输入源是一个触摸屏定位设备。
                 event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
                 syntheticEvent =  true ;
             }
             
             for  (TouchTarget target = mFirstTouchTarget; target !=  null ; target = target.next)
             {
                 resetCancelNextUpFlag(target.child);
                 // 6 向下传递触摸事件(当前传递取消操作)
                 dispatchTransformedTouchEvent(event,  true , target.child, target.pointerIdBits);
             }
             // 清理所有触摸目标
             clearTouchTargets();
             
             // 注销拷贝的对象
             if  (syntheticEvent)
             {
                 event.recycle();
             }
         }
     }


4.canViewReceivePointerEvents

1
2
3
4
5
6
7
8
9
10
     /**
      * Returns true if a child view can receive pointer events.
     
      * @hide
      */
     private  static  boolean  canViewReceivePointerEvents(View child)
     {
         // 当前视图显示或者正在执行动画,才可以接受触摸事件
         return  (child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() !=  null ;
     }


5. isTransformedTouchPointInView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
     /**
      * Returns true if a child view contains the specified point when
      * transformed into its coordinate space. Child must not be null.
     
      * @hide
      */
     protected  boolean  isTransformedTouchPointInView( float  x,  float  y, View child, PointF outLocalPoint)
     {
         // 视图有scrollTo或者scrollBy造成的滚动偏移也需要计算在内
         float  localX = x + mScrollX - child.mLeft;
         float  localY = y + mScrollY - child.mTop;
         if  (!child.hasIdentityMatrix() && mAttachInfo !=  null )
         {
             final  float [] localXY = mAttachInfo.mTmpTransformLocation;
             localXY[ 0 ] = localX;
             localXY[ 1 ] = localY;
             child.getInverseMatrix().mapPoints(localXY);
             localX = localXY[ 0 ];
             localY = localXY[ 1 ];
         }
         // 触摸点是否在当前子视图内
         final  boolean  isInView = child.pointInView(localX, localY);
         if  (isInView && outLocalPoint !=  null )
         {
             outLocalPoint.set(localX, localY);
         }
         return  isInView;
     }


6. dispatchTransformedTouchEvent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
     /**
      * Transforms a motion event into the coordinate space of a particular child
      * view, filters out irrelevant pointer ids, and overrides its action if
      * necessary. If child is null, assumes the MotionEvent will be sent to this
      * ViewGroup instead.
      */
     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);
             if  (child ==  null )
             {
                 // 调用View类的方法,其内部会调用View.onTouchEvent
                 handled =  super .dispatchTouchEvent(event);
             }
             else
             {
                 // 如果有子视图的话,向下传递取消动作
                 handled = child.dispatchTouchEvent(event);
             }
             event.setAction(oldAction);
             // 针对取消操作特别处理
             return  handled;
         }
         
         // Calculate the number of pointers to deliver.
         final  int  oldPointerIdBits = event.getPointerIdBits();
         final  int  newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
         
         // If for some reason we ended up in an inconsistent state where it
         // looks like we
         // might produce a motion event with no pointers in it, then drop the
         // event.
         if  (newPointerIdBits ==  0 )
         {
             return  false ;
         }
         
         // If the number of pointers is the same and we don't need to perform
         // any fancy
         // irreversible transformations, then we can reuse the motion event for
         // this
         // dispatch as long as we are careful to revert any changes we make.
         // Otherwise we need to make a copy.
         final  MotionEvent transformedEvent;
         if  (newPointerIdBits == oldPointerIdBits)
         {
             if  (child ==  null  || child.hasIdentityMatrix())
             {
                 if  (child ==  null )
                 {
                     // 调用View类的方法,其内部会调用View.onTouchEvent
                     handled =  super .dispatchTouchEvent(event);
                 }
                 else
                 {
                     final  float  offsetX = mScrollX - child.mLeft;
                     final  float  offsetY = mScrollY - child.mTop;
                     event.offsetLocation(offsetX, offsetY);
                     
                     // 如果当前视图通过scrollTo或scrollBy对子视图进行滚动
                     // 向下传递的是x,y偏移mScrollX, mScrollY后的值
                     handled = child.dispatchTouchEvent(event);
                     
                     // 一次手势操作,例如滚动视图,MotionEvent参数都是同一个对象
                     // 如果有疑问的话,可以在onTouchEvent中打印其hashCode看下
                     // 所以这里要对之前做出针对其子视图的偏移就还原,便于之后使用
                     event.offsetLocation(-offsetX, -offsetY);
                 }
                 // 如果以上消耗了当前触摸事件,直接返回
                 return  handled;
             }
             // 获取当前参数的拷贝,对其做得修改不会影响当前参数对象
             transformedEvent = MotionEvent.obtain(event);
         }
         else
         {
             transformedEvent = event.split(newPointerIdBits);
         }
         
         // 与以上操作雷同,不具体解释
         // newPointerIdBits != oldPointerIdBits ?1
         // Perform any necessary transformations and dispatch.
         if  (child ==  null )
         {
             handled =  super .dispatchTouchEvent(transformedEvent);
         }
         else
         {
             final  float  offsetX = mScrollX - child.mLeft;
             final  float  offsetY = mScrollY - child.mTop;
             transformedEvent.offsetLocation(offsetX, offsetY);
             if  (!child.hasIdentityMatrix())
             {
                 transformedEvent.transform(child.getInverseMatrix());
             }
             
             handled = child.dispatchTouchEvent(transformedEvent);
         }
         
         // Done.
         transformedEvent.recycle();
         return  handled;
     }


三、View并且非ViewGroup子类,如何分发事件?

 图片是点击绿色按钮,并显示出相应的LOG,并且与文章顶部的图片是对应的

wKioL1QjhgnAEJuiAABKDf6H_xM297.gif

    之前分析了ViewGroup是如何分发事件的,可以解释RelativeLayout与LinearLayout如何分发事件的,下面看下View.dispatchTouchEvent的源码,可以了解下View或者ViewGroup(其中可以调用super.dispatchTouchEvent(event))是如何处理事件的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
     /**
      * Pass the touch screen motion event down to the target view, or this view
      * if it is the target.
     
      * @param event
      *            The motion event to be dispatched.
      * @return True if the event was handled by the view, false otherwise.
      */
     public  boolean  dispatchTouchEvent(MotionEvent event)
     {
         // 用于测试目,直接忽略
         if  (mInputEventConsistencyVerifier !=  null )
         {
             mInputEventConsistencyVerifier.onTouchEvent(event,  0 );
         }
         
         // 上面代码的第2个标注。 未被其他窗口遮盖
         if  (onFilterTouchEventForSecurity(event))
         {
             // noinspection SimplifiableIfStatement
             if  (mOnTouchListener !=  null  && (mViewFlags & ENABLED_MASK) == ENABLED && mOnTouchListener.onTouch( this , event))
             {
                 // 如果有监听器,执行监听器的,不在执行当前视图的onTouchEvent方法
                 return  true ;
             }
             // 执行当前视图的onTouchEvent方法
             if  (onTouchEvent(event))
             {
                 return  true ;
             }
         }
         
         // 用于测试目,直接忽略
         if  (mInputEventConsistencyVerifier !=  null )
         {
             mInputEventConsistencyVerifier.onUnhandledEvent(event,  0 );
         }
         return  false ;
     }


四、事件分发简单总结

dispatchTouchEvent
事件派发显示隧道方式、再是冒泡方式
隧道方式传递,直道某一个元素消耗此事件,由上至下逐层分发视图。
冒泡方式传递,当某个视图消耗事件后其return boolean 是与分发相反的方法向上传递。
具体分发给哪一个视图是通过当前触摸点坐标在当前层哪个视图上判断


onInterceptTouchEvent
ViewGroup的方法,如果当前ViewGroup需要拦截传递给其子视图的事件,需要return true






本文转自 glblong 51CTO博客,原文链接:http://blog.51cto.com/glblong/1558006,如需转载请自行联系原作者

目录
相关文章
|
3月前
|
存储 Shell Android开发
基于Android P,自定义Android开机动画的方法
本文详细介绍了基于Android P系统自定义开机动画的步骤,包括动画文件结构、脚本编写、ZIP打包方法以及如何将自定义动画集成到AOSP源码中。
62 2
基于Android P,自定义Android开机动画的方法
|
3月前
|
Android开发
基于android-11.0.0_r39,系统应用的手动签名方法和过程
本文介绍了基于Android 11.0.0_r39版本进行系统应用手动签名的方法和解决签名过程中遇到的错误,包括处理`no conscrypt_openjdk_jni-linux-x86_64`和`RegisterNatives failed`的问题。
142 2
|
2天前
|
缓存 Java Shell
Android 系统缓存扫描与清理方法分析
Android 系统缓存从原理探索到实现。
28 15
Android 系统缓存扫描与清理方法分析
|
21天前
|
Web App开发 安全 程序员
FFmpeg开发笔记(五十五)寒冬里的安卓程序员可进阶修炼的几种姿势
多年的互联网寒冬在今年尤为凛冽,坚守安卓开发愈发不易。面对是否转行或学习新技术的迷茫,安卓程序员可从三个方向进阶:1)钻研谷歌新技术,如Kotlin、Flutter、Jetpack等;2)拓展新功能应用,掌握Socket、OpenGL、WebRTC等专业领域技能;3)结合其他行业,如汽车、游戏、安全等,拓宽职业道路。这三个方向各有学习难度和保饭碗指数,助你在安卓开发领域持续成长。
50 1
FFmpeg开发笔记(五十五)寒冬里的安卓程序员可进阶修炼的几种姿势
|
11天前
|
Linux API 开发工具
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
46 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
|
2月前
|
ARouter 测试技术 API
Android经典面试题之组件化原理、优缺点、实现方法?
本文介绍了组件化在Android开发中的应用,详细阐述了其原理、优缺点及实现方式,包括模块化、接口编程、依赖注入、路由机制等内容,并提供了具体代码示例。
39 2
|
3月前
|
Android开发
Android在rootdir根目录创建自定义目录和挂载点的方法
本文介绍了在Android高通平台的根目录下创建自定义目录和挂载点的方法,通过修改Android.mk文件并使用`LOCAL_POST_INSTALL_CMD`变量在编译过程中添加目录,最终在ramdisk.img的系统根路径下成功创建了`/factory/bin`目录。
154 1
|
3月前
|
开发工具 uml git
AOSP源码下载方法,解决repo sync错误:android-13.0.0_r82
本文分享了下载AOSP源码的方法,包括如何使用repo工具和处理常见的repo sync错误,以及配置Python环境以确保顺利同步特定版本的AOSP代码。
320 0
AOSP源码下载方法,解决repo sync错误:android-13.0.0_r82
|
3月前
|
编解码 安全 Ubuntu
Android Selinux 问题处理笔记
这篇文章是关于处理Android系统中SELinux权限问题的笔记,介绍了如何通过分析SELinux拒绝的日志、修改SELinux策略文件,并重新编译部署来解决权限问题,同时提供了一些SELinux的背景知识和实用工具。
58 0
|
3月前
|
Android开发
Android项目架构设计问题之onFirstItemVisibleChanged方法的调用如何解决
Android项目架构设计问题之onFirstItemVisibleChanged方法的调用如何解决
37 0