Android高手进阶之Activity.setContentView渲染流程详解

开发 前端
setContentView(R.layout.activity_main)这么简简单单的一段代码做了事情可不简单;接下来我们会跟着源码大概走一遍这个方法,一起总结下。

[[418895]]

前言

setContentView(R.layout.activity_main)这么简简单单的一段代码做了事情可不简单;

接下来我们会跟着源码大概走一遍这个方法,一起总结下

一、DecorView的创建

Activity.setContentView

  1. public void setContentView(@LayoutRes int layoutResID) { 
  2.      getWindow().setContentView(layoutResID); //最终调用mWindow的setContentView方法 
  3.      initWindowDecorActionBar(); 
  4.  } 

getWindow返回的是mWindow, mWindow在Activity的attach方法里被赋值,是个PhoneWindow对象。

(PhoneWindow是Window的唯一实现类)

  1. final void attach(Context context, ActivityThread aThread, 
  2.           Instrumentation instr, IBinder token, int ident, 
  3.           Application application, Intent intent, ActivityInfo info, 
  4.           CharSequence title, Activity parent, String id, 
  5.           NonConfigurationInstances lastNonConfigurationInstances, 
  6.           Configuration config, String referrer, IVoiceInteractor voiceInteractor, 
  7.           Window window, ActivityConfigCallback activityConfigCallback) { 
  8.       attachBaseContext(context); 
  9.       mFragments.attachHost(null /*parent*/); 
  10.       mWindow = new PhoneWindow(this, window, activityConfigCallback); 
  11.       ... 
  12.       mWindow.setWindowManager( //设置WindowManager 
  13.               (WindowManager)context.getSystemService(Context.WINDOW_SERVICE), 
  14.               mToken, mComponent.flattenToString(), 
  15.               (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0); 
  16.       if (mParent != null) { 
  17.           mWindow.setContainer(mParent.getWindow()); 
  18.       } 
  19.       mWindowManager = mWindow.getWindowManager(); 
  20.   } 

PhoneWindow.setContentView

几个关键变量

1.mDecor 是Window的最顶层的View,是个FrameLayout。

2.mContentParent 是用来真正装载Activity传入的布局文件的容器,本身是个ViewGroup。

  1. public void setContentView(int layoutResID) { 
  2.        // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window 
  3.        // decor, when theme attributes and the like are crystalized. Do not check the feature 
  4.        // before this happens. 
  5.        if (mContentParent == null) { 
  6.            installDecor(); //如果mContentParent为空,则执行installDecor方法 
  7.        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) { 
  8.            mContentParent.removeAllViews(); //否则remove掉mContentParent的所有子view 
  9.        } 
  10.        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { 
  11.            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID, 
  12.                    getContext()); 
  13.            transitionTo(newScene); 
  14.        } else { 
  15.            mLayoutInflater.inflate(layoutResID, mContentParent); //将activity传入的布局文件加载到mContentParent里 
  16.        } 
  17.        mContentParent.requestApplyInsets(); 
  18.        final Callback cb = getCallback(); 
  19.        if (cb != null && !isDestroyed()) { 
  20.            cb.onContentChanged(); 
  21.        } 
  22.        mContentParentExplicitlySet = true
  23.    } 

PhoneWindow.installDecor

  1. private void installDecor() { 
  2.        mForceDecorInstall = false
  3.        if (mDecor == null) { 
  4.            mDecor = generateDecor(-1); //如果之前没有创建,直接创建一个 
  5.            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); 
  6.            mDecor.setIsRootNamespace(true); 
  7.            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) { 
  8.                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable); 
  9.            } 
  10.        } else { 
  11.            mDecor.setWindow(this); //将PhoneWindow传递给DecorView 
  12.        } 
  13.        if (mContentParent == null) { 
  14.            mContentParent = generateLayout(mDecor); //赋值mContentParent 
  15.        ... 
  16.    } 

PhoneWindow.installDecor

  1. protected DecorView generateDecor(int featureId) { 
  2.        // System process doesn't have application context and in that case we need to directly use 
  3.        // the context we have. Otherwise we want the application context, so we don't cling to the 
  4.        // activity. 
  5.        Context context; 
  6.        if (mUseDecorContext) { 
  7.            Context applicationContext = getContext().getApplicationContext(); 
  8.            if (applicationContext == null) { 
  9.                context = getContext(); 
  10.            } else { 
  11.                context = new DecorContext(applicationContext, getContext()); 
  12.                if (mTheme != -1) { 
  13.                    context.setTheme(mTheme); 
  14.                } 
  15.            } 
  16.        } else { 
  17.            context = getContext(); 
  18.        } 
  19.        return new DecorView(context, featureId, this, getAttributes()); //创建DecorView 
  20.    } 

PhoneWindow.generateLayout

这一步是挑选合适的DecorView布局文件并将其添加大盘DecorView,同时给mContentParent赋值。

  1. protected ViewGroup generateLayout(DecorView decor) { 
  2.         // Apply data from current theme. 
  3.         TypedArray a = getWindowStyle(); 
  4.         ...... 
  5.         // Inflate the window decor. 
  6.         //根据不同的features来选择DecorView的布局 
  7.         int layoutResource; 
  8.         int features = getLocalFeatures(); 
  9.         // System.out.println("Features: 0x" + Integer.toHexString(features)); 
  10.         if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) { 
  11.             layoutResource = R.layout.screen_swipe_dismiss; 
  12.             setCloseOnSwipeEnabled(true); 
  13.         } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) { 
  14.             if (mIsFloating) { 
  15.                 TypedValue res = new TypedValue(); 
  16.                 getContext().getTheme().resolveAttribute( 
  17.                         R.attr.dialogTitleIconsDecorLayout, res, true); 
  18.                 layoutResource = res.resourceId; 
  19.             } else { 
  20.                 layoutResource = R.layout.screen_title_icons; 
  21.             } 
  22.             // XXX Remove this once action bar supports these features. 
  23.             removeFeature(FEATURE_ACTION_BAR); 
  24.             // System.out.println("Title Icons!"); 
  25.         } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0 
  26.                 && (features & (1 << FEATURE_ACTION_BAR)) == 0) { 
  27.             // Special case for a window with only a progress bar (and title). 
  28.             // XXX Need to have a no-title version of embedded windows. 
  29.             layoutResource = R.layout.screen_progress; 
  30.             // System.out.println("Progress!"); 
  31.         } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) { 
  32.             // Special case for a window with a custom title. 
  33.             // If the window is floating, we need a dialog layout 
  34.             if (mIsFloating) { 
  35.                 TypedValue res = new TypedValue(); 
  36.                 getContext().getTheme().resolveAttribute( 
  37.                         R.attr.dialogCustomTitleDecorLayout, res, true); 
  38.                 layoutResource = res.resourceId; 
  39.             } else { 
  40.                 layoutResource = R.layout.screen_custom_title; 
  41.             } 
  42.             // XXX Remove this once action bar supports these features. 
  43.             removeFeature(FEATURE_ACTION_BAR); 
  44.         } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) { 
  45.             // If no other features and not embedded, only need a title. 
  46.             // If the window is floating, we need a dialog layout 
  47.             if (mIsFloating) { 
  48.                 TypedValue res = new TypedValue(); 
  49.                 getContext().getTheme().resolveAttribute( 
  50.                         R.attr.dialogTitleDecorLayout, res, true); 
  51.                 layoutResource = res.resourceId; 
  52.             } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) { 
  53.                 layoutResource = a.getResourceId( 
  54.                         R.styleable.Window_windowActionBarFullscreenDecorLayout, 
  55.                         R.layout.screen_action_bar); 
  56.             } else { 
  57.                 layoutResource = R.layout.screen_title; 
  58.             } 
  59.             // System.out.println("Title!"); 
  60.         } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) { 
  61.             layoutResource = R.layout.screen_simple_overlay_action_mode; 
  62.         } else { 
  63.             // Embedded, so no decoration is needed. 
  64.             layoutResource = R.layout.screen_simple; 
  65.             // System.out.println("Simple!"); 
  66.         } 
  67.         mDecor.startChanging(); 
  68.         mDecor.onResourcesLoaded(mLayoutInflater, layoutResource); //挑选出来的布局添加到DecorView中 
  69.         ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); //从DecorView找出id为com.android.internal.R.id.content的容器,提供给Activity使用。 
  70.         if (contentParent == null) { 
  71.             throw new RuntimeException("Window couldn't find content container view"); 
  72.         } 
  73.         ...... 
  74.         return contentParent; 
  75.     } 

R.layout.test

这一步纯粹是为了看下DecorView布局文件长啥样

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  2.     android:layout_width="match_parent" 
  3.     android:layout_height="match_parent" 
  4.     android:fitsSystemWindows="true" 
  5.     android:orientation="vertical"
  6.     <ViewStub android:id="@+id/action_mode_bar_stub" 
  7.               android:inflatedId="@+id/action_mode_bar" 
  8.               android:layout="@layout/action_mode_bar" 
  9.               android:layout_width="match_parent" 
  10.               android:layout_height="wrap_content" 
  11.               android:theme="?attr/actionBarTheme" /> 
  12.     <!--真正存放Activity布局的容器--> 
  13.     <FrameLayout 
  14.          android:id="@android:id/content" 
  15.          android:layout_width="match_parent" 
  16.          android:layout_height="match_parent" 
  17.          android:foregroundInsidePadding="false" 
  18.          android:foregroundGravity="fill_horizontal|top" 
  19.          android:foreground="?android:attr/windowContentOverlay" /> 
  20. </LinearLayout> 

DecorView.onResourcesLoaded

  1. void onResourcesLoaded(LayoutInflater inflater, int layoutResource) { 
  2.        if (mBackdropFrameRenderer != null) { 
  3.            loadBackgroundDrawablesIfNeeded(); 
  4.            mBackdropFrameRenderer.onResourcesLoaded( 
  5.                    this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable, 
  6.                    mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState), 
  7.                    getCurrentColor(mNavigationColorViewState)); 
  8.        } 
  9.        mDecorCaptionView = createDecorCaptionView(inflater); 
  10.        final View root = inflater.inflate(layoutResource, null); //解析出布局文件 
  11.        if (mDecorCaptionView != null) { 
  12.            if (mDecorCaptionView.getParent() == null) { 
  13.                addView(mDecorCaptionView, 
  14.                        new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); 
  15.            } 
  16.            mDecorCaptionView.addView(root, 
  17.                    new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT)); 
  18.        } else { 
  19.            // Put it below the color views. 
  20.            addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); //添加到DecorView 
  21.        } 
  22.        mContentRoot = (ViewGroup) root; //mContentRoot保存的是整个跟布局容器 
  23.        initializeElevation(); 
  24.    } 

 

二、DecorView绘制到屏幕

Activity 执行到 onCreate 时并不可见,只有执行完 onResume 之后 Activity 中的内容才是屏幕可见状态。onCreate 阶段只是初始化了 Activity 需要显示的内容,而在 onResume 阶段才会将 PhoneWindow 中的 DecorView 真正的绘制到屏幕上。

在ActivityThread的handleResumeActivity方法中,调用WindowManager将decor作为窗口添加到 WMS 。

  1. @Override 
  2.   public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward, 
  3.           String reason) { 
  4.         ...... 
  5.         if (a.mVisibleFromClient) { 
  6.               if (!a.mWindowAdded) { 
  7.                   a.mWindowAdded = true
  8.                   wm.addView(decor, l); //通过WindowManager将decor添加到WMS 
  9.               } else { 
  10.                   // The activity will get a callback for this {@link LayoutParams} change 
  11.                   // earlier. However, at that time the decor will not be set (this is set 
  12.                   // in this method), so no action will be taken. This call ensures the 
  13.                   // callback occurs with the decor set
  14.                   a.onWindowAttributesChanged(l); 
  15.               } 
  16.           } 
  17.         ...... 

实现WindowManager接口的是WindowManagerImpl类,从WINDOW_SERVICE注册时也能看出来。

  1. registerService(Context.WINDOW_SERVICE, WindowManager.class, new CachedServiceFetcher<WindowManager>() { 
  2.        @Override 
  3.        public WindowManager createService(ContextImpl ctx) { 
  4.            return new WindowManagerImpl(ctx); 
  5.        }}); 

WindowManagerImpl.addView调用mGlobal.addView方法,mGlobal是WindowManagerGlobal类型变量。

  1. @Override 
  2.    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { 
  3.        applyDefaultToken(params); 
  4.        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow); 
  5.    } 

WindowManagerGlobal.addView最终调的是ViewRootImpl的setView方法

  1. public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { 
  2.       ...... 
  3.       ViewRootImpl root; 
  4.       View panelParentView = null
  5.       synchronized (mLock) { 
  6.           ...... 
  7.           root = new ViewRootImpl(view.getContext(), display); //创建ViewRootImpl对象 
  8.           view.setLayoutParams(wparams); 
  9.           mViews.add(view); 
  10.           mRoots.add(root); 
  11.           mParams.add(wparams); 
  12.           // do this last because it fires off messages to start doing things 
  13.           try { 
  14.               root.setView(view, wparams, panelParentView); //最终,调的是ViewRootImpl的setView方法 
  15.           } catch (RuntimeException e) { 
  16.               // BadTokenException or InvalidDisplayException, clean up. 
  17.               if (index >= 0) { 
  18.                   removeViewLocked(indextrue); 
  19.               } 
  20.               throw e; 
  21.           } 
  22.       } 
  23.   } 

ViewRootImpl.setView

  1. public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { 
  2.         synchronized (this) { 
  3.             if (mView == null) { 
  4.                 ...... 
  5.                 int res; /* = WindowManagerImpl.ADD_OKAY; */ 
  6.                 // Schedule the first layout -before- adding to the window 
  7.                 // manager, to make sure we do the relayout before receiving 
  8.                 // any other events from the system. 
  9.                 requestLayout(); //调用此方法后 ViewRootImpl 所关联的 View 也执行 measure - layout - draw 操作,确保在 View 被添加到 Window 上显示到屏幕之前,已经完成测量和绘制操作。 
  10.                 ...... 
  11.                 try { 
  12.                     mOrigWindowType = mWindowAttributes.type; 
  13.                     mAttachInfo.mRecomputeGlobalAttributes = true
  14.                     collectViewAttributes(); 
  15.                     // mWindowSession 的 addToDisplay 方法将 View 添加到 WMS 中。 
  16.                     res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, 
  17.                             getHostVisibility(), mDisplay.getDisplayId(), mWinFrame, 
  18.                             mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, 
  19.                             mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel); 
  20.                 } catch (RemoteException e) { 
  21.                     mAdded = false
  22.                     mView = null
  23.                     mAttachInfo.mRootView = null
  24.                     mInputChannel = null
  25.                     mFallbackEventHandler.setView(null); 
  26.                     unscheduleTraversals(); 
  27.                     setAccessibilityFocus(nullnull); 
  28.                     throw new RuntimeException("Adding window failed", e); 
  29.                 } finally { 
  30.                     if (restore) { 
  31.                         attrs.restore(); 
  32.                     } 
  33.                 } 
  34.             } 
  35.             ...... 
  36.         } 
  37.     } 

WindowSession实例获取,是IWindowSession类型,通过Binder机制调用System 进程中的 Session实现。

  1. public static IWindowSession getWindowSession() { 
  2.       synchronized (WindowManagerGlobal.class) { 
  3.           if (sWindowSession == null) { 
  4.               try { 
  5.                   InputMethodManager imm = InputMethodManager.getInstance(); 
  6.                   IWindowManager windowManager = getWindowManagerService(); 
  7.                   sWindowSession = windowManager.openSession( 
  8.                           new IWindowSessionCallback.Stub() { 
  9.                               @Override 
  10.                               public void onAnimatorScaleChanged(float scale) { 
  11.                                   ValueAnimator.setDurationScale(scale); 
  12.                               } 
  13.                           }, 
  14.                           imm.getClient(), imm.getInputContext()); 
  15.               } catch (RemoteException e) { 
  16.                   throw e.rethrowFromSystemServer(); 
  17.               } 
  18.           } 
  19.           return sWindowSession; 
  20.       } 
  21.   } 

addToDisplay真正实现。

  1. @Override 
  2.   public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs, 
  3.           int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets, 
  4.           Rect outStableInsets, Rect outOutsets, 
  5.           DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel, 
  6.           InsetsState outInsetsState) { 
  7.       return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame, 
  8.               outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel, 
  9.               outInsetsState); 
  10.   } 

至此,Window 已经成功的被传递给了 WMS。剩下的工作就全部转移到系统进程中的 WMS 来完成最终的添加操作。

三、触摸事件处理

ViewRootImpl 中的 setView 方法中,除了调用 IWindowSession 执行跨进程添加 View 之外,还有一项重要的操作就是设置输入事件的处理:

  1. public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { 
  2.      ...... 
  3.      // Set up the input pipeline. 
  4.      CharSequence counterSuffix = attrs.getTitle(); 
  5.      mSyntheticInputStage = new SyntheticInputStage(); 
  6.      InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage); 
  7.      InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage, 
  8.              "aq:native-post-ime:" + counterSuffix); 
  9.      InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage); 
  10.      InputStage imeStage = new ImeInputStage(earlyPostImeStage, 
  11.              "aq:ime:" + counterSuffix); 
  12.      InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage); 
  13.      InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage, 
  14.            "aq:native-pre-ime:" + counterSuffix); 
  15.      ...... 
  16.  } 

最终会经过ViewPostImeInputStage的onProcess处理

  1. final class ViewPostImeInputStage extends InputStage { 
  2.         public ViewPostImeInputStage(InputStage next) { 
  3.             super(next); 
  4.         } 
  5.         @Override 
  6.         protected int onProcess(QueuedInputEvent q) { 
  7.             if (q.mEvent instanceof KeyEvent) { 
  8.                 return processKeyEvent(q); 
  9.             } else { 
  10.                 final int source = q.mEvent.getSource(); 
  11.                 if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) { 
  12.                     return processPointerEvent(q); 
  13.                 } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { 
  14.                     return processTrackballEvent(q); 
  15.                 } else { 
  16.                     return processGenericMotionEvent(q); 
  17.                 } 
  18.             } 
  19.         } 

processPointerEvent方法,调用mView的dispatchPointerEvent 分发事件。mView是DecorView对象

  1. private int processPointerEvent(QueuedInputEvent q) { 
  2.            ...... 
  3.            boolean handled = mView.dispatchPointerEvent(event); 
  4.            ...... 
  5.            return handled ? FINISH_HANDLED : FORWARD
  6.        } 

dispatchPointerEvent是View实现的,最终调的是dispatchTouchEvent方法。

  1. public final boolean dispatchPointerEvent(MotionEvent event) { 
  2.        if (event.isTouchEvent()) { 
  3.            return dispatchTouchEvent(event); 
  4.        } else { 
  5.            return dispatchGenericMotionEvent(event); 
  6.        } 
  7.    } 

DecorView.dispatchTouchEvent最终调用PhoneWindow的Callback分发事件。

  1. @Override 
  2.    public boolean dispatchTouchEvent(MotionEvent ev) { 
  3.        final Window.Callback cb = mWindow.getCallback(); 
  4.        return cb != null && !mWindow.isDestroyed() && mFeatureId < 0 
  5.                ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev); 
  6.    } 

PhoneWindow的Callback是在Activity的attach时设置的

  1. final void attach(Context context, ActivityThread aThread, 
  2.            Instrumentation instr, IBinder token, int ident, 
  3.            Application application, Intent intent, ActivityInfo info, 
  4.            CharSequence title, Activity parent, String id, 
  5.            NonConfigurationInstances lastNonConfigurationInstances, 
  6.            Configuration config, String referrer, IVoiceInteractor voiceInteractor, 
  7.            Window window, ActivityConfigCallback activityConfigCallback) { 
  8.        attachBaseContext(context); 
  9.        mFragments.attachHost(null /*parent*/); 
  10.        mWindow = new PhoneWindow(this, window, activityConfigCallback); 
  11.        mWindow.setWindowControllerCallback(this); 
  12.        mWindow.setCallback(this); //将Activity对象传递给PhoneWindow 
  13.        ...... 
  14.    } 

下面就回到了我们熟悉的Activity的dispatchTouchEvent方法:

  1. public boolean dispatchTouchEvent(MotionEvent ev) { 
  2.        if (ev.getAction() == MotionEvent.ACTION_DOWN) { 
  3.            onUserInteraction(); 
  4.        } 
  5.        if (getWindow().superDispatchTouchEvent(ev)) { //调用PhoneWindow的superDispatchTouchEvent方法 
  6.            return true
  7.        } 
  8.        return onTouchEvent(ev); 
  9.    } 

PhoneWindow.superDispatchTouchEvent,归根结底还是调的DecorView的superDispatchTouchEvent方法

  1. @Override 
  2.   public boolean superDispatchTouchEvent(MotionEvent event) { 
  3.       return mDecor.superDispatchTouchEvent(event); 
  4.   } 

DecorView.superDispatchTouchEvent,调用ViewGroup的dispatchTouchEvent方法。

  1. public boolean superDispatchTouchEvent(MotionEvent event) { 
  2.        return super.dispatchTouchEvent(event); 
  3.    } 

总结

1.整个过程Activity参与度很低,基本靠PhoneWindow实现。

2.onCreate阶段创建了DecorView,onResume阶段将DecorView添加到WMS并展示,ViewRootImple对象也是onResume阶段创建的,所以也解释了onCreate阶段子线程加载view并不会报错。

3.ViewRootImpl 的 setView 方法中主要完成两件事情:View 渲染(requestLayout)以及接收触屏事件。

4.一个 Activity 中有一个 window,也就是 PhoneWindow 对象,每一个 PhoneWindow 对应一个 ViewRootImple 对象。

本文转载自微信公众号「Android开发编程」

 

责任编辑:姜华 来源: Android开发编程
相关推荐

2021-08-10 20:41:33

AndroidApp流程

2021-08-11 17:15:17

AndroidActivity场景

2021-09-02 07:00:01

Glide流程Android

2021-09-29 09:42:32

AndroidViewDragHel拖动上下滑卡片

2021-08-25 07:43:17

AndroidSurfaceViewTextureView

2021-09-14 07:06:12

Android磁盘缓存

2021-08-24 07:53:28

AndroidActivity生命周期

2021-09-07 06:40:25

AndroidLiveData原理

2021-10-03 15:08:32

Android

2021-09-01 06:48:16

AndroidGlide缓存

2015-07-28 17:11:00

编程技术提升

2021-08-17 13:41:11

AndroidView事件

2014-07-15 17:17:31

AdapterAndroid

2021-07-14 14:27:01

AndroidAOPhugo

2014-05-27 15:36:01

AndroidActivity启动模式

2023-11-15 08:28:13

PythonVTK

2021-09-16 06:44:04

Android进阶流程

2021-09-17 06:55:50

AndroidLayoutView

2015-09-16 13:54:30

Android性能优化渲染

2021-10-14 14:32:27

AndroidActiviAPI
点赞
收藏

51CTO技术栈公众号