导图社区 Android导图
这是一篇关于Android导图的思维导图,包括:1.Android系统初始化流程;2.zygote启动流程;3. binder流程;4.UI的添加和绘制;5.事件。
编辑于2022-09-05 12:21:58Android学习
1. Android系统初始化流程
AMS
Launcher.java
public final class Launcher extends Activity implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, View.OnTouchListener { Launcher就是展示手机桌面的应用程序,所以当我们点击我们自己的自己安装的app的图标的时候,会触发Launcher.java的Launcher就是系统桌面应用程序,所以当我们点击我们自己的自己安装的app的图标的时候,会触发Launcher.java(其继承了Activity)onClick(View v)方法,因此这里也是一个app启动时候的入口
OnClick(View)
该回调为点击该应用在桌面的图标时的回调函数
startActivitySafely(启动activiy)
startActivity(冷启动)
startMainActivity(热启动)
WMS
PMS
其他
2. zygote启动流程
init.cpp
init.rc
import /init.${ro.zygote}.rc //这里先执行了init.zygote*.rc 这里的*号指的是不同的版本,根据不同的系统,可以是64也可以是32,还可以是什么都没有
开启servicemanager(该进程负责管理所有系统服务)
init.zygote64(即启动zygote进程)
在init.zygote64中,我们会看到其中用service启动了zygote进程,并且这个进程可执行文件的路径为/system/bin/app_process64,当然也可以理解为启动了/system/bin/app_process64,其别名为zygote service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server app_process64对应的源码文件为app_main.cpp
app_main.cpp
ZygoteInit.java
ZygoteInit.java只是zygote进程源码app_main.cpp调用的一个java代码,而zygoteinit.java中的mian方法也只是被当作普通方法在调用
预加载: preload(bootTimingsTraceLog)
static void preload(TimingsTraceLog bootTimingsTraceLog) { Log.d(TAG, "begin preload"); bootTimingsTraceLog.traceBegin("BeginIcuCachePinning"); beginIcuCachePinning(); bootTimingsTraceLog.traceEnd(); // BeginIcuCachePinning bootTimingsTraceLog.traceBegin("PreloadClasses"); preloadClasses(); //加载系统class文件 bootTimingsTraceLog.traceEnd(); // PreloadClasses bootTimingsTraceLog.traceBegin("PreloadResources"); preloadResources(); //加载资源文件 bootTimingsTraceLog.traceEnd(); // PreloadResources Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadAppProcessHALs"); nativePreloadAppProcessHALs(); Trace.traceEnd(Trace.TRACE_TAG_DALVIK); Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadOpenGL"); preloadOpenGL(); Trace.traceEnd(Trace.TRACE_TAG_DALVIK); preloadSharedLibraries(); //加载so文件 preloadTextResources(); // Ask the WebViewFactory to do any initialization that must run in the zygote process, // for memory sharing purposes. WebViewFactory.prepareWebViewInZygote(); endIcuCachePinning(); warmUpJcaProviders(); Log.d(TAG, "end preload"); sPreloadComplete = true; }
创建用于通信的socket zygoteServer.registerServerSocketFromEnv(socketName)
创建systemserver进程: forkSystemServer(abiList, socketName, zygoteServer)
systemserver.java: main方法
traceBeginAndSlog("StartServices"); startBootstrapServices(); //底层服务(这包括fw层的服务,比如大名鼎鼎的AMS) startCoreServices(); //用户层服务 startOtherServices(); //应用层服务 SystemServerInitThreadPool.shutdown();
AMS
PMS
pms的作用:管理、扫描、验证apk
扫描data/app下的apk文件,并返回Package对象
开启一个监听ams创建新app进程的socket信息的循环 zygoteServer.runSelectLoop(abiList)
子主题
面试题
为什么ams与zygote进程通信使用的是socket而不是binder
首先,由于是c/s结构,所以只能在binder和socket之间二选一
其次,binder虽然高效,但是binder不够安全,这里的交互安全第一
3. binder流程
app_main.cpp(zygote源码)
应用启动的时候会调用: virtual void onStarted()
sp<ProcessState> proc = ProcessState::self();
ProcessState::ProcessState(const char *driver) 这里会创建binder的共享内存
ProcessState::ProcessState(const char *driver) : mDriverName(String8(driver)) , mDriverFD(open_driver(driver)) , mVMStart(MAP_FAILED) , mThreadCountLock(PTHREAD_MUTEX_INITIALIZER) , mThreadCountDecrement(PTHREAD_COND_INITIALIZER) , mExecutingThreadsCount(0) , mMaxThreads(DEFAULT_MAX_BINDER_THREADS) , mStarvationStartTimeMs(0) , mManagesContexts(false) , mBinderContextCheckFunc(NULL) , mBinderContextUserData(NULL) , mThreadPoolStarted(false) , mThreadPoolSeq(1) { if (mDriverFD >= 0) { // mmap the binder, providing a chunk of virtual address space to receive transactions. mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0); if (mVMStart == MAP_FAILED) { // *sigh* ALOGE("Using %s failed: unable to mmap transaction memory.\n", mDriverName.c_str()); close(mDriverFD); mDriverFD = -1; mDriverName.clear(); } } LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver could not be opened. Terminating."); } 这里的 mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0) 就是创建binder的共享内存的地方
4. UI的添加和绘制
UI 添加
activity.java: setContentView(@LayoutRes int layoutResID)
getWindow().setContentView(layoutResID) 这里getWindow()得到的实际是phonewindow
PhoneWindow: setContentView(int layoutResID)
public void setContentView(int layoutResID) { // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window // decor, when theme attributes and the like are crystalized. Do not check the feature // before this happens. if (mContentParent == null) { //第一次创建的时候为null,并且在installDecor()中创建实例对象 //初始化顶层布局:将mContentParent初始化 installDecor(); } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mContentParent.removeAllViews(); } if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID, getContext()); transitionTo(newScene); } else { //加载我们自己的控件的layout mLayoutInflater.inflate(layoutResID, mContentParent); } mContentParent.requestApplyInsets(); final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); } mContentParentExplicitlySet = true; }
installDecor(): 初始化顶层布局
mDecor = generateDecor(-1); 创建根布局对象实例
mContentParent = generateLayout(mDecor) 创建父容器对象实例
generateLayout(DecorView decor) 这个函数就主要完成了根布局和父容器 的布局文件id的加载
protected ViewGroup generateLayout(DecorView decor) { // Apply data from current theme. TypedArray a = getWindowStyle(); if (false) { System.out.println("From style:"); String s = "Attrs:"; for (int i = 0; i < R.styleable.Window.length; i++) { s = s + " " + Integer.toHexString(R.styleable.Window[i]) + "=" + a.getString(i); } System.out.println(s); } mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false); int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR) & (~getForcedWindowFlags()); if (mIsFloating) { setLayout(WRAP_CONTENT, WRAP_CONTENT); setFlags(0, flagsToUpdate); } else { setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate); } if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) { requestFeature(FEATURE_NO_TITLE); } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) { // Don't allow an action bar if there is no title. requestFeature(FEATURE_ACTION_BAR); } if (a.getBoolean(R.styleable.Window_windowActionBarOverlay, false)) { requestFeature(FEATURE_ACTION_BAR_OVERLAY); } if (a.getBoolean(R.styleable.Window_windowActionModeOverlay, false)) { requestFeature(FEATURE_ACTION_MODE_OVERLAY); } if (a.getBoolean(R.styleable.Window_windowSwipeToDismiss, false)) { requestFeature(FEATURE_SWIPE_TO_DISMISS); } if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) { setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags())); } if (a.getBoolean(R.styleable.Window_windowTranslucentStatus, false)) { setFlags(FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_STATUS & (~getForcedWindowFlags())); } if (a.getBoolean(R.styleable.Window_windowTranslucentNavigation, false)) { setFlags(FLAG_TRANSLUCENT_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION & (~getForcedWindowFlags())); } if (a.getBoolean(R.styleable.Window_windowOverscan, false)) { setFlags(FLAG_LAYOUT_IN_OVERSCAN, FLAG_LAYOUT_IN_OVERSCAN&(~getForcedWindowFlags())); } if (a.getBoolean(R.styleable.Window_windowShowWallpaper, false)) { setFlags(FLAG_SHOW_WALLPAPER, FLAG_SHOW_WALLPAPER&(~getForcedWindowFlags())); } if (a.getBoolean(R.styleable.Window_windowEnableSplitTouch, getContext().getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.HONEYCOMB)) { setFlags(FLAG_SPLIT_TOUCH, FLAG_SPLIT_TOUCH&(~getForcedWindowFlags())); } a.getValue(R.styleable.Window_windowMinWidthMajor, mMinWidthMajor); a.getValue(R.styleable.Window_windowMinWidthMinor, mMinWidthMinor); if (DEBUG) Log.d(TAG, "Min width minor: " + mMinWidthMinor.coerceToString() + ", major: " + mMinWidthMajor.coerceToString()); if (a.hasValue(R.styleable.Window_windowFixedWidthMajor)) { if (mFixedWidthMajor == null) mFixedWidthMajor = new TypedValue(); a.getValue(R.styleable.Window_windowFixedWidthMajor, mFixedWidthMajor); } if (a.hasValue(R.styleable.Window_windowFixedWidthMinor)) { if (mFixedWidthMinor == null) mFixedWidthMinor = new TypedValue(); a.getValue(R.styleable.Window_windowFixedWidthMinor, mFixedWidthMinor); } if (a.hasValue(R.styleable.Window_windowFixedHeightMajor)) { if (mFixedHeightMajor == null) mFixedHeightMajor = new TypedValue(); a.getValue(R.styleable.Window_windowFixedHeightMajor, mFixedHeightMajor); } if (a.hasValue(R.styleable.Window_windowFixedHeightMinor)) { if (mFixedHeightMinor == null) mFixedHeightMinor = new TypedValue(); a.getValue(R.styleable.Window_windowFixedHeightMinor, mFixedHeightMinor); } if (a.getBoolean(R.styleable.Window_windowContentTransitions, false)) { requestFeature(FEATURE_CONTENT_TRANSITIONS); } if (a.getBoolean(R.styleable.Window_windowActivityTransitions, false)) { requestFeature(FEATURE_ACTIVITY_TRANSITIONS); } mIsTranslucent = a.getBoolean(R.styleable.Window_windowIsTranslucent, false); final Context context = getContext(); final int targetSdk = context.getApplicationInfo().targetSdkVersion; final boolean targetPreHoneycomb = targetSdk < android.os.Build.VERSION_CODES.HONEYCOMB; final boolean targetPreIcs = targetSdk < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH; final boolean targetPreL = targetSdk < android.os.Build.VERSION_CODES.LOLLIPOP; final boolean targetHcNeedsOptions = context.getResources().getBoolean( R.bool.target_honeycomb_needs_options_menu); final boolean noActionBar = !hasFeature(FEATURE_ACTION_BAR) || hasFeature(FEATURE_NO_TITLE); if (targetPreHoneycomb || (targetPreIcs && targetHcNeedsOptions && noActionBar)) { setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_TRUE); } else { setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_FALSE); } if (!mForcedStatusBarColor) { mStatusBarColor = a.getColor(R.styleable.Window_statusBarColor, 0xFF000000); } if (!mForcedNavigationBarColor) { mNavigationBarColor = a.getColor(R.styleable.Window_navigationBarColor, 0xFF000000); mNavigationBarDividerColor = a.getColor(R.styleable.Window_navigationBarDividerColor, 0x00000000); } WindowManager.LayoutParams params = getAttributes(); // Non-floating windows on high end devices must put up decor beneath the system bars and // therefore must know about visibility changes of those. if (!mIsFloating) { if (!targetPreL && a.getBoolean( R.styleable.Window_windowDrawsSystemBarBackgrounds, false)) { setFlags(FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS & ~getForcedWindowFlags()); } if (mDecor.mForceWindowDrawsStatusBarBackground) { params.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND; } } if (a.getBoolean(R.styleable.Window_windowLightStatusBar, false)) { decor.setSystemUiVisibility( decor.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); } if (a.getBoolean(R.styleable.Window_windowLightNavigationBar, false)) { decor.setSystemUiVisibility( decor.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR); } if (a.hasValue(R.styleable.Window_windowLayoutInDisplayCutoutMode)) { int mode = a.getInt(R.styleable.Window_windowLayoutInDisplayCutoutMode, -1); if (mode < LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT || mode > LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER) { throw new UnsupportedOperationException("Unknown windowLayoutInDisplayCutoutMode: " + a.getString(R.styleable.Window_windowLayoutInDisplayCutoutMode)); } params.layoutInDisplayCutoutMode = mode; } if (mAlwaysReadCloseOnTouchAttr || getContext().getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.HONEYCOMB) { if (a.getBoolean( R.styleable.Window_windowCloseOnTouchOutside, false)) { setCloseOnTouchOutsideIfNotSet(true); } } if (!hasSoftInputMode()) { params.softInputMode = a.getInt( R.styleable.Window_windowSoftInputMode, params.softInputMode); } if (a.getBoolean(R.styleable.Window_backgroundDimEnabled, mIsFloating)) { /* All dialogs should have the window dimmed */ if ((getForcedWindowFlags()&WindowManager.LayoutParams.FLAG_DIM_BEHIND) == 0) { params.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND; } if (!haveDimAmount()) { params.dimAmount = a.getFloat( android.R.styleable.Window_backgroundDimAmount, 0.5f); } } if (params.windowAnimations == 0) { params.windowAnimations = a.getResourceId( R.styleable.Window_windowAnimationStyle, 0); } // The rest are only done if this window is not embedded; otherwise, // the values are inherited from our container. if (getContainer() == null) { if (mBackgroundDrawable == null) { if (mBackgroundResource == 0) { mBackgroundResource = a.getResourceId( R.styleable.Window_windowBackground, 0); } if (mFrameResource == 0) { mFrameResource = a.getResourceId(R.styleable.Window_windowFrame, 0); } mBackgroundFallbackResource = a.getResourceId( R.styleable.Window_windowBackgroundFallback, 0); if (false) { System.out.println("Background: " + Integer.toHexString(mBackgroundResource) + " Frame: " + Integer.toHexString(mFrameResource)); } } if (mLoadElevation) { mElevation = a.getDimension(R.styleable.Window_windowElevation, 0); } mClipToOutline = a.getBoolean(R.styleable.Window_windowClipToOutline, false); mTextColor = a.getColor(R.styleable.Window_textColor, Color.TRANSPARENT); } // Inflate the window decor. //资源id int layoutResource; int features = getLocalFeatures(); // System.out.println("Features: 0x" + Integer.toHexString(features)); if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) { layoutResource = R.layout.screen_swipe_dismiss; setCloseOnSwipeEnabled(true); } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) { if (mIsFloating) { TypedValue res = new TypedValue(); getContext().getTheme().resolveAttribute( R.attr.dialogTitleIconsDecorLayout, res, true); layoutResource = res.resourceId; } else { layoutResource = R.layout.screen_title_icons; } // XXX Remove this once action bar supports these features. removeFeature(FEATURE_ACTION_BAR); // System.out.println("Title Icons!"); } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0 && (features & (1 << FEATURE_ACTION_BAR)) == 0) { // Special case for a window with only a progress bar (and title). // XXX Need to have a no-title version of embedded windows. layoutResource = R.layout.screen_progress; // System.out.println("Progress!"); } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) { // Special case for a window with a custom title. // If the window is floating, we need a dialog layout if (mIsFloating) { TypedValue res = new TypedValue(); getContext().getTheme().resolveAttribute( R.attr.dialogCustomTitleDecorLayout, res, true); layoutResource = res.resourceId; } else { layoutResource = R.layout.screen_custom_title; } // XXX Remove this once action bar supports these features. removeFeature(FEATURE_ACTION_BAR); } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) { // If no other features and not embedded, only need a title. // If the window is floating, we need a dialog layout if (mIsFloating) { TypedValue res = new TypedValue(); getContext().getTheme().resolveAttribute( R.attr.dialogTitleDecorLayout, res, true); layoutResource = res.resourceId; } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) { layoutResource = a.getResourceId( R.styleable.Window_windowActionBarFullscreenDecorLayout, R.layout.screen_action_bar); } else { layoutResource = R.layout.screen_title; } // System.out.println("Title!"); } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) { layoutResource = R.layout.screen_simple_overlay_action_mode; } else { // Embedded, so no decoration is needed. layoutResource = R.layout.screen_simple; // System.out.println("Simple!"); } mDecor.startChanging(); //将加载到的基础布局添加到mDecor中(也就是把mDecor的布局文件添加到viewgroup中) mDecor.onResourcesLoaded(mLayoutInflater, layoutResource); //通过系统的content的资源id去实例化这个控件(ID_ANDROID_CONTENT就是mDecor布局文件R.layout.screen_simple中的那个framelayout的id) ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); if (contentParent == null) { throw new RuntimeException("Window couldn't find content container view"); } if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) { ProgressBar progress = getCircularProgressBar(false); if (progress != null) { progress.setIndeterminate(true); } } if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) { registerSwipeCallbacks(contentParent); } // Remaining setup -- of background and title -- that only applies // to top-level windows. if (getContainer() == null) { final Drawable background; if (mBackgroundResource != 0) { background = getContext().getDrawable(mBackgroundResource); } else { background = mBackgroundDrawable; } mDecor.setWindowBackground(background); final Drawable frame; if (mFrameResource != 0) { frame = getContext().getDrawable(mFrameResource); } else { frame = null; } mDecor.setWindowFrame(frame); mDecor.setElevation(mElevation); mDecor.setClipToOutline(mClipToOutline); if (mTitle != null) { setTitle(mTitle); } if (mTitleColor == 0) { mTitleColor = mTextColor; } setTitleColor(mTitleColor); } mDecor.finishChanging(); return contentParent; }
layoutResource = R.layout.screen_simple; 这个 R.layout.screen_simple就是默认的根布局文件了
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource) 将layoutResource加载到根布局mDecor中
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT) 通过系统的content的资源id去实例化这个控件(ID_ANDROID_CONTENT就是mDecor布局文件R.layout.screen_simple中的那个framelayout的id)
mLayoutInflater.inflate(layoutResID, mContentParent) 加载我们自己的控件的layout
LayoutInflater.java: inflate(@LayoutRes int resource, @Nullable ViewGroup root)
inflate(resource, rot, root != null)
XmlResourceParser parser = res.getLayout(resource): 解析我们自己的布局文件,并反射成view
UI绘制
ActivityThread: handleResumeActivity
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward, String reason) { // If we are getting ready to gc after going to the background, well // we are back active so skip it. unscheduleGcIdler(); mSomeActivitiesChanged = true; // TODO Push resumeArgs into the activity for consideration final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason); if (r == null) { // We didn't actually resume the activity, so skipping any follow-up actions. return; } final Activity a = r.activity; if (localLOGV) { Slog.v(TAG, "Resume " + r + " started activity: " + a.mStartedActivity + ", hideForNow: " + r.hideForNow + ", finished: " + a.mFinished); } final int forwardBit = isForward ? WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0; // If the window hasn't yet been added to the window manager, // and this guy didn't finish itself or start another activity, // then go ahead and add the window. boolean willBeVisible = !a.mStartedActivity; if (!willBeVisible) { try { willBeVisible = ActivityManager.getService().willActivityBeVisible( a.getActivityToken()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } if (r.window == null && !a.mFinished && willBeVisible) { r.window = r.activity.getWindow(); View decor = r.window.getDecorView(); decor.setVisibility(View.INVISIBLE); ViewManager wm = a.getWindowManager(); WindowManager.LayoutParams l = r.window.getAttributes(); a.mDecor = decor; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; l.softInputMode |= forwardBit; if (r.mPreserveWindow) { a.mWindowAdded = true; r.mPreserveWindow = false; // Normally the ViewRoot sets up callbacks with the Activity // in addView->ViewRootImpl#setView. If we are instead reusing // the decor view we have to notify the view root that the // callbacks may have changed. ViewRootImpl impl = decor.getViewRootImpl(); if (impl != null) { impl.notifyChildRebuilt(); } } if (a.mVisibleFromClient) { if (!a.mWindowAdded) { a.mWindowAdded = true; //调用wms绘制根布局 wm.addView(decor, l); } else { // The activity will get a callback for this {@link LayoutParams} change // earlier. However, at that time the decor will not be set (this is set // in this method), so no action will be taken. This call ensures the // callback occurs with the decor set. a.onWindowAttributesChanged(l); } } // If the window has already been added, but during resume // we started another activity, then don't yet make the // window visible. } else if (!willBeVisible) { if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set"); r.hideForNow = true; } // Get rid of anything left hanging around. cleanUpPendingRemoveWindows(r, false /* force */); // The window is now visible if it has been added, we are not // simply finishing, and we are not starting another activity. if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) { if (r.newConfig != null) { performConfigurationChangedForActivity(r, r.newConfig); if (DEBUG_CONFIGURATION) { Slog.v(TAG, "Resuming activity " + r.activityInfo.name + " with newConfig " + r.activity.mCurrentConfig); } r.newConfig = null; } if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward=" + isForward); WindowManager.LayoutParams l = r.window.getAttributes(); if ((l.softInputMode & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != forwardBit) { l.softInputMode = (l.softInputMode & (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)) | forwardBit; if (r.activity.mVisibleFromClient) { ViewManager wm = a.getWindowManager(); View decor = r.window.getDecorView(); wm.updateViewLayout(decor, l); } } r.activity.mVisibleFromServer = true; mNumVisibleActivities++; if (r.activity.mVisibleFromClient) { r.activity.makeVisible(); } } r.nextIdle = mNewActivities; mNewActivities = r; if (localLOGV) Slog.v(TAG, "Scheduling idle handler for " + r); Looper.myQueue().addIdleHandler(new Idler()); }
View decor = r.window.getDecorView() 从activity信息中获取decor对象
wm.addView(decor, l) 调用wms开始绘制布局
WindowManagerImpl.java: addView
mGlobal.addView
WindowManagerGlobal.java: addView
root.setView(view, wparams, panelParentView)
ViewRootImpl.java: setView
requestLayout()
scheduleTraversals()
mChoreographer.postCallback
postCallbackDelayed
postCallbackDelayedInternal
scheduleFrameLocked
private void scheduleFrameLocked(long now) { if (!mFrameScheduled) { mFrameScheduled = true; if (USE_VSYNC) { if (DEBUG_FRAMES) { Log.d(TAG, "Scheduling next frame on vsync."); } // If running on the Looper thread, then schedule the vsync immediately, // otherwise post a message to schedule the vsync from the UI thread // as soon as possible. //如果是在主线程,则向底层请求同步信号 //如果是在子线程的绘制消息,则通过发送消息来执行 //这两者最终都会走到scheduleVsyncLocked() //这样就保证了UI始终是友主线程绘制 if (isRunningOnLooperThreadLocked()) { //这里判断是否在主线程 scheduleVsyncLocked(); } else { Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC); msg.setAsynchronous(true); mHandler.sendMessageAtFrontOfQueue(msg); } } else { final long nextFrameTime = Math.max( mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now); if (DEBUG_FRAMES) { Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms."); } Message msg = mHandler.obtainMessage(MSG_DO_FRAME); msg.setAsynchronous(true); mHandler.sendMessageAtTime(msg, nextFrameTime); } } }
scheduleVsyncLocked()
mDisplayEventReceiver.scheduleVsync()
nativeScheduleVsync(mReceiverPtr): 这个方法向底层发一个vsync信息
5. 事件
事件的获取
即从手指与屏幕进行交互开始,事件是如何由底层传入到应用层
ViewRootImpl.java.setView方法
该方法同时也是activity布局在初始化的时候(也就是handleResumeActivity方法)钟会调用到的地方 public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) { mView = view; mAttachInfo.mDisplayState = mDisplay.getState(); mDisplayManager.registerDisplayListener(mDisplayListener, mHandler); mViewLayoutDirectionInitial = mView.getRawLayoutDirection(); mFallbackEventHandler.setView(view); mWindowAttributes.copyFrom(attrs); if (mWindowAttributes.packageName == null) { mWindowAttributes.packageName = mBasePackageName; } attrs = mWindowAttributes; setTag(); if (DEBUG_KEEP_SCREEN_ON && (mClientWindowLayoutFlags & WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) != 0 && (attrs.flags&WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) == 0) { Slog.d(mTag, "setView: FLAG_KEEP_SCREEN_ON changed from true to false!"); } // Keep track of the actual window flags supplied by the client. mClientWindowLayoutFlags = attrs.flags; setAccessibilityFocus(null, null); if (view instanceof RootViewSurfaceTaker) { mSurfaceHolderCallback = ((RootViewSurfaceTaker)view).willYouTakeTheSurface(); if (mSurfaceHolderCallback != null) { mSurfaceHolder = new TakenSurfaceHolder(); mSurfaceHolder.setFormat(PixelFormat.UNKNOWN); mSurfaceHolder.addCallback(mSurfaceHolderCallback); } } // Compute surface insets required to draw at specified Z value. // TODO: Use real shadow insets for a constant max Z. if (!attrs.hasManualSurfaceInsets) { attrs.setSurfaceInsets(view, false /*manual*/, true /*preservePrevious*/); } CompatibilityInfo compatibilityInfo = mDisplay.getDisplayAdjustments().getCompatibilityInfo(); mTranslator = compatibilityInfo.getTranslator(); // If the application owns the surface, don't enable hardware acceleration if (mSurfaceHolder == null) { // While this is supposed to enable only, it can effectively disable // the acceleration too. enableHardwareAcceleration(attrs); final boolean useMTRenderer = MT_RENDERER_AVAILABLE && mAttachInfo.mThreadedRenderer != null; if (mUseMTRenderer != useMTRenderer) { // Shouldn't be resizing, as it's done only in window setup, // but end just in case. endDragResizing(); mUseMTRenderer = useMTRenderer; } } boolean restore = false; if (mTranslator != null) { mSurface.setCompatibilityTranslator(mTranslator); restore = true; attrs.backup(); mTranslator.translateWindowLayout(attrs); } if (DEBUG_LAYOUT) Log.d(mTag, "WindowLayout in setView:" + attrs); if (!compatibilityInfo.supportsScreen()) { attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW; mLastInCompatMode = true; } mSoftInputMode = attrs.softInputMode; mWindowAttributesChanged = true; mWindowAttributesChangesFlag = WindowManager.LayoutParams.EVERYTHING_CHANGED; mAttachInfo.mRootView = view; mAttachInfo.mScalingRequired = mTranslator != null; mAttachInfo.mApplicationScale = mTranslator == null ? 1.0f : mTranslator.applicationScale; if (panelParentView != null) { mAttachInfo.mPanelParentWindowToken = panelParentView.getApplicationWindowToken(); } mAdded = true; int res; /* = WindowManagerImpl.ADD_OKAY; */ // 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();//从这里进去,进入跟底层交互来请求刷新UI的逻辑 if ((mWindowAttributes.inputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) { //创建InputChannel对象 mInputChannel = new InputChannel(); } mForceDecorViewVisibility = (mWindowAttributes.privateFlags & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0; try { mOrigWindowType = mWindowAttributes.type; mAttachInfo.mRecomputeGlobalAttributes = true; collectViewAttributes(); //注册事件输入监听 res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mWinFrame, mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel); } catch (RemoteException e) { mAdded = false; mView = null; mAttachInfo.mRootView = null; mInputChannel = null; mFallbackEventHandler.setView(null); unscheduleTraversals(); setAccessibilityFocus(null, null); throw new RuntimeException("Adding window failed", e); } finally { if (restore) { attrs.restore(); } } if (mTranslator != null) { mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets); } mPendingOverscanInsets.set(0, 0, 0, 0); mPendingContentInsets.set(mAttachInfo.mContentInsets); mPendingStableInsets.set(mAttachInfo.mStableInsets); mPendingDisplayCutout.set(mAttachInfo.mDisplayCutout); mPendingVisibleInsets.set(0, 0, 0, 0); mAttachInfo.mAlwaysConsumeNavBar = (res & WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR) != 0; mPendingAlwaysConsumeNavBar = mAttachInfo.mAlwaysConsumeNavBar; if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow); if (res < WindowManagerGlobal.ADD_OKAY) { mAttachInfo.mRootView = null; mAdded = false; mFallbackEventHandler.setView(null); unscheduleTraversals(); setAccessibilityFocus(null, null); switch (res) { case WindowManagerGlobal.ADD_BAD_APP_TOKEN: case WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN: throw new WindowManager.BadTokenException( "Unable to add window -- token " + attrs.token + " is not valid; is your activity running?"); case WindowManagerGlobal.ADD_NOT_APP_TOKEN: throw new WindowManager.BadTokenException( "Unable to add window -- token " + attrs.token + " is not for an application"); case WindowManagerGlobal.ADD_APP_EXITING: throw new WindowManager.BadTokenException( "Unable to add window -- app for token " + attrs.token + " is exiting"); case WindowManagerGlobal.ADD_DUPLICATE_ADD: throw new WindowManager.BadTokenException( "Unable to add window -- window " + mWindow + " has already been added"); case WindowManagerGlobal.ADD_STARTING_NOT_NEEDED: // Silently ignore -- we would have just removed it // right away, anyway. return; case WindowManagerGlobal.ADD_MULTIPLE_SINGLETON: throw new WindowManager.BadTokenException("Unable to add window " + mWindow + " -- another window of type " + mWindowAttributes.type + " already exists"); case WindowManagerGlobal.ADD_PERMISSION_DENIED: throw new WindowManager.BadTokenException("Unable to add window " + mWindow + " -- permission denied for window type " + mWindowAttributes.type); case WindowManagerGlobal.ADD_INVALID_DISPLAY: throw new WindowManager.InvalidDisplayException("Unable to add window " + mWindow + " -- the specified display can not be found"); case WindowManagerGlobal.ADD_INVALID_TYPE: throw new WindowManager.InvalidDisplayException("Unable to add window " + mWindow + " -- the specified window type " + mWindowAttributes.type + " is not valid"); } throw new RuntimeException( "Unable to add window -- unknown error code " + res); } if (view instanceof RootViewSurfaceTaker) { mInputQueueCallback = ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue(); } if (mInputChannel != null) { if (mInputQueueCallback != null) { mInputQueue = new InputQueue(); mInputQueueCallback.onInputQueueCreated(mInputQueue); } //构建输应用层的输入事件监听(这里只是实例对象,至于如何监听,有空可以看看他是如何跟底层交互的,看看它的调用逻辑就知道了) mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, Looper.myLooper()); } view.assignParent(this); mAddedTouchMode = (res & WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE) != 0; mAppVisible = (res & WindowManagerGlobal.ADD_FLAG_APP_VISIBLE) != 0; if (mAccessibilityManager.isEnabled()) { mAccessibilityInteractionConnectionManager.ensureConnection(); } if (view.getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) { view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); } // Set up the input pipeline. CharSequence counterSuffix = attrs.getTitle(); mSyntheticInputStage = new SyntheticInputStage(); InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage); InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage, "aq:native-post-ime:" + counterSuffix); 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; mFirstPostImeInputStage = earlyPostImeStage; mPendingInputEventQueueLengthCounterName = "aq:pending:" + counterSuffix; } } }
requestLayout()方法
从这里进去,进入跟底层交互来请求刷新UI的逻辑
mWindowSession.addToDisplay方法
//注册事件输入监听(这里主要是跟Linux层打交道,暂不深究) res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mWinFrame, mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
创建mInputEventReceiver
这里是重点,创建了一个用类似于广播的实例,用来监听底层收到并处理过的屏幕事件,然后转发给activity //构建输应用层的输入事件监听(这里只是实例对象,至于如何监听,有空可以看看他是如何跟底层交互的,看看它的调用逻辑就知道了) mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, Looper.myLooper());
onInputEvent方法
final class WindowInputEventReceiver extends InputEventReceiver { public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) { super(inputChannel, looper); } //底层监听到事件后会回调这个函数 @Override public void onInputEvent(InputEvent event, int displayId) { enqueueInputEvent(event, this, 0, true); } @Override public void onBatchedInputEventPending() { if (mUnbufferedInputDispatch) { super.onBatchedInputEventPending(); } else { scheduleConsumeBatchedInput(); } } @Override public void dispose() { unscheduleConsumeBatchedInput(); super.dispose(); } }
enqueueInputEvent方法
doProcessInputEvents()
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 { /*走这里获取其实例 *这里无论是走mFirstPostImeInputStage还是走mFirstInputStage,都会最终通过InputStage的mNext走到 * ViewPostImeInputStage这个InputStage的子类,然后执行其中的重写方法onProcess */ stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage; } if (q.mEvent instanceof KeyEvent) { mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent); } if (stage != null) { handleWindowFocusChanged(); stage.deliver(q); } else { finishInputEvent(q); } }
stage.deliver(q)
这里最终会调用ViewPostImeInputStage的onProcess方法,具体是如何调用到ViewPostImeInputStage这个子类的onProcess方法,可以参考下有道云笔记的妙不可言文件夹下的java子类的链式调用文件
ViewPostImeInputStage.onProcess
processPointerEvent
mView.dispatchPointerEvent(event)
这个mView往前翻可以发现它其实就是DecorView
DecorView.java: dispatchTrackballEvent
cb.dispatchTrackballEvent(ev)
Actvity: dispatchTouchEvent(MotionEvent ev)
public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { onUserInteraction(); } if (getWindow().superDispatchTouchEvent(ev)) { return true; } return onTouchEvent(ev); }
onUserInteraction()
屏幕被按下时触发(这个是一定触发)
getWindow().superDispatchTouchEvent(ev)
if (getWindow().superDispatchTouchEvent(ev)) { return true; } return onTouchEvent(ev); 这里是将事件交个DecorView的父类也就是ViewGroup去处理,如果发现子控件消耗事件则返回true,当返回true的时候,函数直接结束,那么return onTouchEvent(ev)就不会被执行(也就是activity的onTouchEvent方法不会被调用)
return onTouchEvent(ev)
当子控件没有消耗掉事件,也就是getWindow().superDispatchTouchEvent(ev)返回false的时候,才会调用activity的onTouchEvent方法
事件的分发
ViewGroup.java: dispatchTouchEvent方法
从上面的事件获取的调用链,最终会来到这里 这个方法其实就是做了下面几件事情: 1.首先会给当前的target链中的view发送cancel状态,然后将该view的target从target链中移除 2.判断viewgroup是否需要拦截时间自己消费 3.如果不需要拦截,则计算信息,并将被事件影响的view放进新的target链中 4.将事件分发给child view 注:target链并不会每次在每次调用dispatchTouchEvent的时候重新创建,而是在view收到cancel指令的时候才会移除对应的target,这实际上是对target的复用,避免了重复创建target引起的内存抖动
dispatchTouchEvent分析
第一次按下 ,也就是产生down事件的时候清除所有受影响的target 并给他们绑定的view发送cancel指令
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);//遍历所有的touchTarget,让他们中的child执行cancel,然后回收所有的touchTarget, resetTouchState();//初始化Viewgroup的分发状态,并将mFirstTouchTarget置为null } 这里可以看出 ,这段逻辑只在down的时候发生
down发生时,将所有的view添加到target列表中
// 判断条件: 事件是否是DOWN 或 POINTER_DOWN事件 表示新的手指按下了,需要寻找接收事件的view if (actionMasked == MotionEvent.ACTION_DOWN || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN) || actionMasked == MotionEvent.ACTION_HOVER_MOVE) { // 多点触控会有不同的索引,获取索引号 // 该索引位于MotionEvent中的一个数组没索引值就是数组的下标值 // 只有up或者down事件才会携带索引值 final int actionIndex = ev.getActionIndex(); // always 0 for down // 这个整型变量记录了TouchTarget中view所对应的触控点id // 触控点id的范围是0-31,整型变量中一个二进制为1,则对应绑定该id的触控点 // 这里根据是否需要分离,对触控点id进行记录 // 而如果不需要分离,则默认接受所有触控点的事件 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. // down事件表示该触控点事件序列是一个新的序列 // 清除之前绑定到该触控点id的TouchTarget removePointersFromTouchTargets(idBitsToAssign); // 获取childrenCount 的值,表示该viewgroup 内部有多个子view final int childrenCount = mChildrenCount; // 如果子空间数目不为0,而且还没绑定到新的id if (newTouchTarget == null && childrenCount != 0) { // 使用触控点索引获取触控点位置 final float x = ev.getX(actionIndex); final float y = ev.getY(actionIndex); // Find a child that can receive the event. // Scan children from front to back. // 从前到后创建view列表 final ArrayList<View> preorderedList = buildTouchDispatchChildList(); // 判断是否是自定义vie顺序 final boolean customOrder = preorderedList == null && isChildrenDrawingOrderEnabled(); final View[] children = mChildren; // for循环 对所有子view 进行循环遍历 // 由于 以上判断了 view group 不对事件进行拦截,那么在这里就要对viewgroup内部的子view进行遍历,一个个的找能够接受事件的子view // 这里注意 ,它是倒序遍历的,即从最上层的子view 开始往内层遍历,这也符合我们的习惯,因为一般来说我们对屏幕触摸,肯定是希望最上层的view来响应,而不是被覆盖在底层的view来响应 for (int i = childrenCount - 1; i >= 0; i--) { // 行子控件列表中获取到子控件 final int childIndex = getAndVerifyPreorderedIndex( childrenCount, i, customOrder); final View child = getAndVerifyPreorderedView( preorderedList, children, childIndex); // If there is a view that has accessibility focus we want it // to get the event first and if not handled we will perform a // normal dispatch. We may do a double iteration but this is // safer given the timeframe. // 如果是辅助功能事件则优先给对应的target先处理 // 如果该view不处理,再交给其他view处理 if (childWithAccessibilityFocus != null) { if (childWithAccessibilityFocus != child) { continue; } childWithAccessibilityFocus = null; i = childrenCount - 1; } // 判断 触摸点的位置 是否在子view的范围内或者子view是否在在播放动画,如果不符合 则continue,表示这个view不符合条件,开始遍历下一个子view if (!canViewReceivePointerEvents(child) || !isTransformedTouchPointInView(x, y, child, null)) { ev.setTargetAccessibilityFocus(false); continue; } // 检查该子view是否在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. // 链表中已经存在该子view,说明只认识一个多点触摸事件 // 即两次都触摸到同一个view上 // 将新的触控点id绑定到给TouchTarget上 newTouchTarget.pointerIdBits |= idBitsToAssign; break; } //设置取消标志 // 下次再次调用这个方法就会返回true resetCancelNextUpFlag(child); // dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign) // 当传递进来的child 不为null时,就会调用子view 的dispatchTouchEvent(event)方法 // 子view符合所有条件的时候,事件就会在这里传递给了子view来处理,完成了 viewgroup 到 子view的事件传递, // 当事件处理完毕,就会返回一个布尔值handled,该值表示子view死否消耗了事件 // 如果说子view的onTouchEvent()返回true,那就是消耗了事件。需要生成新的TouchTarget 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(); // addTouchTarget(child, idBitsToAssign) // 把 mFirstTouchTarget 指向了child,同时把newTouchTarget也指向了child // 也就是说,如果子view消耗掉了事件,那么mFirstTouchTarget就会执行子view, // 执行玩之后直接break 跳出循环,无需再遍历了 // 保存该view到target链表 newTouchTarget = addTouchTarget(child, idBitsToAssign);//这里mFirstTouchTarget也会指向链表的头部(这是个环形链表),而不再是null // 标记已经分发给子view,退出循环 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. // 没有子view接收down事件,直接选择链表尾的view作为target newTouchTarget = mFirstTouchTarget; while (newTouchTarget.next != null) { newTouchTarget = newTouchTarget.next; } newTouchTarget.pointerIdBits |= idBitsToAssign; } } 在down事件发生时,将 所有的 view都从mChildrenCount中取出,并存放进target链表中去(需要注意的是这里的target信息只有在view接收到cancel事件的时候才会移除,这就是target的复用)
在判定viewgroup未拦截的情况下将事件分发给child 并且判断一次view是否接收到cancel指令,有的话则 清除其对应的 target
// 已经有子view 消费了down事件 TouchTarget predecessor = null; TouchTarget target = mFirstTouchTarget; // 遍历所有的TouchTarget 并把事件分发下去 while (target != null) { final TouchTarget next = target.next; // 如果 子view消耗了 ACTION_DOWN 事件和别的事件,那么 alreadyDispatchedToNewTouchTarget和newTouchTarget已经有值了 // 所以就直接置handled 为true 返回, // 如果 alreadyDispatchedToNewTouchTarget和newTouchTarget 的值为null,那么就不是ACTION_DOWN事件,即是ACTION_MOVE, // ACTION_UP等别的事件的话 就会调用 ***2 出代码,把事件分发给子view if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) { // 表示事件已经在前面处理好了,无需重复处理 handled = true; } else { // 正常分发事件或者分发取消事件 final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted; // 调用了dispatchTransformedTouchEvent方法来分发事件 if (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits)) { // ***2 // 表示事件在前面已经处理了,不需要重复处理 handled = true; } // 如果发送了取消事件,则移除该target if (cancelChild) { if (predecessor == null) { mFirstTouchTarget = next; } else { predecessor.next = next; } target.recycle(); target = next; continue; } } predecessor = target; target = next; }
dispatchTransformedTouchEvent (事件在推向child流程开始)
child.dispatchTouchEvent(event)
public boolean dispatchTouchEvent(MotionEvent event) { // If the event should be handled by accessibility focus first. if (event.isTargetAccessibilityFocus()) { // We don't have focus or no virtual descendant has it, do not handle the event. if (!isAccessibilityFocusedViewOrHost()) { return false; } // We have focus and got the event, then use normal event dispatch. event.setTargetAccessibilityFocus(false); } boolean result = false; if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(event, 0); } final int actionMasked = event.getActionMasked(); if (actionMasked == MotionEvent.ACTION_DOWN) { // Defensive cleanup for new gesture stopNestedScroll(); } if (onFilterTouchEventForSecurity(event)) { if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) { result = true; } //noinspection SimplifiableIfStatement ListenerInfo li = mListenerInfo; //先判断view是否设置了touch监听返回true,有的话,直接return if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { result = true; } //再判断onTouchEvent是否返回了true,是的话,直接return if (!result && onTouchEvent(event)) { result = true; } //从这里可以看出,如果说mOnTouchListener或者onTouchEvent返回了true,则代表着时间被这二者之一消费掉,那么onclick这种就不会被执行,因为直接在这里return了 } if (!result && mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(event, 0); } // Clean up after nested scrolls if this is the end of a gesture; // also cancel it if we tried an ACTION_DOWN but we didn't want the rest // of the gesture. if (actionMasked == MotionEvent.ACTION_UP || actionMasked == MotionEvent.ACTION_CANCEL || (actionMasked == MotionEvent.ACTION_DOWN && !result)) { stopNestedScroll(); } return result; }
mOnTouchListener和onTouchEvent的判断
if (onFilterTouchEventForSecurity(event)) { if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) { result = true; } //noinspection SimplifiableIfStatement ListenerInfo li = mListenerInfo; //先判断view是否设置了touch监听返回true,有的话,直接return if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { result = true; } //再判断onTouchEvent是否返回了true,是的话,直接return if (!result && onTouchEvent(event)) { result = true; } //从这里可以看出,如果说mOnTouchListener或者onTouchEvent返回了true,则代表着时间被这二者之一消费掉,那么onclick这种就不会被执行,因为直接在这里return了 }
up或者 cancel事件处理