源码分析篇-Android绘制流程(一)窗口启动流程分析

源码分析篇-Android绘制流程(一)窗口启动流程分析

2023年6月27日发(作者:)

源码分析篇-Android绘制流程(⼀)窗⼝启动流程分析 Activity、View、Window之间的关系可以⽤以下的简要UML关系图表⽰,在这⾥贴出来,⽐较能够帮组后⾯流程分析部分的阅读。

⼀、Activity的启动流程  在startActivity()后,经过⼀些逻辑流程会通知到ActivityManagerService(后⾯以AMS简称),AMS接收到启动acitivty的请求后,会通过跨进程通信调⽤LauncherActivity()⽅法,我们从这⾥开始分析,⾸先来看handleLauncherActivity()⽅法。   private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {      ...// Initialize before creating the activity lize(); Activity a = performLaunchActivity(r, customIntent);      if (a != null) {        dConfig = new Configuration(mConfiguration);        reportSizeConfigurations(r);        Bundle oldState = ;        //该⽅法会调⽤到Activity的onResume()⽅法        handleResumeActivity(, false, ard,              !hed && !NotResumed, ocessedSeq, reason);        ...      } ... }  这⾥重点关注三个⽅法(加粗的地⽅),⾸先来看lize(),WindowManagerGlobal是单例模式的,⼀个进程内只有⼀个,这⾥调⽤该类的初始化⽅法,后续我们再对该类的作⽤和相关⽅法进⾏分析;第三个是在创建好Activity后调⽤Acitivty的onResume()⽅法。这⾥我们来看需关注的第⼆个⽅法performLaunchActivity(),代码如下。 private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {     ...

     //通过反射⽅式创建Activity Activity activity = null; try { oader cl = ssLoader(); activity = ivity( cl, ssName(), ); entExpectedActivityCount(ss()); rasClassLoader(cl); eToEnterProcess(); if ( != null) { ssLoader(cl); } } catch (Exception e) { if (!ption(activity, e)) { throw new RuntimeException( "Unable to instantiate activity " + component + ": " + ng(), e); } } try { ...       if (activity != null) { Context appContext = createBaseContextForActivity(r, activity); CharSequence title = bel(kageManager()); Configuration config = new Configuration(mCompatConfiguration); if (deConfig != null) { From(deConfig); } if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity " + + " with config " + config); Window window = null; if (ngRemoveWindow != null && rveWindow) { window = ngRemoveWindow; ngRemoveWindow = null; ngRemoveWindowManager = null; } (appContext, this, getInstrumentation(), , , app, , tyInfo, title, , edID, nConfigurationInstances, config, er, nteractor, window);

          ...          //调⽤acitivity的onCreate()⽅法           if (istable()) {               tivityOnCreate(activity, , tentState);           } else {               tivityOnCreate(activity, );           }          ...       } return activity; }  这个⽅法主要是读取Acitivity这⾥利⽤反射创建出ActivityClientRecord所要求的Activity对象,然后调⽤了()⽅法。注意attach()传⼊的参数有很多,在performLacunchActivity()⽅法流程中,调⽤attach()⽅前,我们省略掉的步骤基本都在为这些参数做准备,attach()⽅法的作⽤其实就是将这些参数配置到新创建的Activity对象中;⽽在attach之后则会回调到acitivity的onCreate()⽅法。我们进⼊类详细来看下attach⽅法。  此外,在attach之前会初始化⼀个Window对象,是⼀个抽象类,代表了⼀个矩形不可见的容器,主要负责加载显⽰界⾯,每个Activity都会对应了⼀个Window对象。如果ngRevomeWindow变量中已经保存了⼀个Window对象,则会在后⾯的attach⽅法中被使⽤,具体使⽤的场景会在后⾯中介绍。 final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token, int ident, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances, Configuration config, String referrer, IVoiceInteractor voiceInteractor, Window window) { attachBaseContext(context); Host(null /*parent*/); mWindow = new PhoneWindow(this, window);//(1) dowControllerCallback(this); lback(this); indowDismissedCallback(this); outInflater().setPrivateFactory(this); if (putMode != _INPUT_STATE_UNSPECIFIED) { tInputMode(putMode); } if (ons != 0) { ptions(ons); }     ... //初始化Acitity相关属性 dowManager( (WindowManager)temService(_SERVICE), mToken, nToString(), ( & _HARDWARE_ACCELERATED) != 0);//(2) if (mParent != null) { tainer(dow()); } mWindowManager = dowManager(); mCurrentConfig = config; }  重点关注初始化window对象的操作,⾸先创建了PhoneWindow对象为activity的mWindow变量,在创建时传⼊了上⼀个activity对应的window对象,之后⼜将这个acitivity设置为window对象的回调。Activity中很多操作view相关的⽅法,例如setContentView()、findViewById()、getLayoutInflater()等,实际上都是直接调⽤到PhoneWindow⾥⾯的相关⽅法。创建完acitivty对应的PhoneWindow之后便会调⽤setWindowManager()⽅法。⾸先来看PhonewWindow构造⽅法。  public PhoneWindow(Context context, Window preservedWindow) { this(context); // Only main activity windows use decor context, all the other windows depend on whatever // context that was given to them. mUseDecorContext = true;if (preservedWindow != null) { //快速重启activity机制 mDecor = (DecorView) orView(); mElevation = vation(); mLoadElevation = false; mForceDecorInstall = true; // If we're preserving window, carry over the app token from the preserved // window, as we'll be skipping the addView in handleResumeActivity(), and // the token will not be updated as for a new window. getAttributes().token = ributes().token; } // Even though the device doesn't support picture-in-picture mode, // an user can force using it through developer options. boolean forceResizable = (tentResolver(), DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0; mSupportsPictureInPicture = forceResizable || kageManager().hasSystemFeature( E_PICTURE_IN_PICTURE); }  ⾸先要关注的就是preserviedWindow参数,这个参数就是上⼀段中提到的mPendingRevomeWindow变量,这个参数在什么时候会不为空呢?其实这⾥的逻辑是⽤来快速重启acitivity的,⽐如你的⼀个activity已经启动了,但是主题换了或者configuration变了,这⾥只需要重新加载资源和view,没必重新再执⾏DecorView的创建⼯作。  另⼀个要关注的就是mDecor变量,这个变量是DecorView类型的,如果这⾥没有初始化的话,则会在调⽤setContentView⽅法中new⼀个DecorView对象出来。DecorView对象继承⾃FrameLayout,所以他本质上还是⼀个view,只是对FrameLayout做了⼀定的包装,例如添加了⼀些与window需要调⽤的⽅法setWindowBackground()、setWindowFrame()等。我们知道,acitivty界⾯的view是呈树状结构的,⽽mDecor变量在这⾥作为activity的界⾯的根view存在。这三个点关系就⽐如,PhoneWindow是⼀块⼿机电⼦屏,DecorView就是电⼦屏要显⽰的内容,Activity就是⼿机电⼦屏安装位置。  再来看创建PhonewWindow之后调⽤的setWindowManager()⽅法的逻辑,这段代码是在的⽗类中代码如下。 public void setWindowManager(WindowManager wm, IBinder appToken, String appName, boolean hardwareAccelerated) { mAppToken = appToken; mAppName = appName; mHardwareAccelerated = hardwareAccelerated || lean(PROPERTY_HARDWARE_UI, false); if (wm == null) { wm = (WindowManager)temService(_SERVICE); } mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this); }  对于wWindowManager变量,实际上这⾥是创建了⼀个WindowManagerImpl对象。⾸先是这种⾸先获取系统服务的代理到wm上,然后强制转换为WindowManagerImpl调⽤createLocalWindowManager(),在createLocalWindowManager()实际是执⾏了⼀个newWindowManagerImpl()到⽅法来创建。关于这部分代码看了很久很困惑的⼀个点,就是为啥要弄个这么复杂的逻辑,直接把上⾯加粗的代码改为new WindowManagerImpl(...),这养会有什么区别吗?如果有⼤虾看到这⾥,希望能帮我解答。  在WindowManager中保存了对于单例对象WindowManagerGloble的引⽤,即mGlobal变量。此外,实现了WindowManager⼜,⽽WindowManager继承⾃ViewManager接⼝,ViewManager接⼝⽅法如下⽅代码。public interface ViewManager{ /** * Assign the passed LayoutParams to the passed View and add the view to the window. *

Throws {@link enException} for certain programming * errors, such as adding a second view to a window without removing the first view. *

Throws {@link dDisplayException} if the window is on a * secondary {@link Display} and the specified display can't be found * (see {@link tation}). * @param view The view to be added to this window. * @param params The LayoutParams to assign to view. */ public void addView(View view, Params params); public void updateViewLayout(View view, Params params); public void removeView(View view);}  在WindowManager对于addView()、updateViewLayout()和removeView()的实现,都是调⽤到mGlobal变量对应的addView()、updateViewLayout()和removeView()⽅法来实现的。这⾥我们  这样我们分析完activity以及对应的window对象的创建,回到performLauncerActivity()⽅法中Activity a = performLaunchActivity(r,customIntent)这⼀步骤,之后便回调activity⽅法的onCreate(),在onCreate()的setContentView⽅法会初始化DecorView,并根据传⼊参数加载布局,详细步骤在下⼀节介绍。  再回到最初的handlerLaunchActivity()⽅法中,通过调⽤performLauncerActivity()创建出⼀个Acitivty对象后,如果创建成功则执⾏handleResumeActivity(),便执⾏到了Acitivity的onResume()⽅法,即是完成了acitivty的启动。

⼆、setContentView()流程  ⾸先,我们⼀般在onCreate()⾥调⽤setContentView()的⽅法。 @override protected void onCreate(Bundle savedInstanceState) {   te(savedInstanceState);   setContentView(ty_main); }  这⾥实际调⽤到到地⽅是类中的setContentView()⽅法,如下。 public void setContentView(@LayoutRes int layoutResID) {   getWindow().setContentView(layoutResID);   initWindowDecorActionBar(); }  这⾥getWindow()返回的是类中的mWindow变量,就是Activity创建时⼀起创建的PhoneWindow对象,进⼊到 @Override public void setContentView(int layoutResID) { if (mContentParent == null) { installDecor(); } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) { AllViews();//如果多次调⽤setContentView则会执⾏removeAllView操作 } if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { //过渡动画机制相关 final Scene newScene = neForLayout(mContentParent, layoutResID, getContext()); transitionTo(newScene); } else { e(layoutResID, mContentParent); } tApplyInsets(); final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { entChanged(); } mContentParentExplicitlySet = true; }  代码⾥涉及到FEATURE_CONTENT_TRANSITIONS的属性,这⾥是Android的过渡动画相关机制,这⾥我们不再展开详述。⼀般的Acitivty启动时,会进⼊mContentParent为null的逻辑,⾸先调⽤的是installDecor()⽅法,完成DecorView的创建⼯作;之后调⽤e()⽅法将我们传⼊的资源⽂件转换为view树,装载到mContentParent中。⾸先来看installDecor()代码。 private void installDecor() { mForceDecorInstall = false; if (mDecor == null) { //创建DecorView mDecor = generateDecor(-1); cendantFocusability(_AFTER_DESCENDANTS); ootNamespace(true); if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) { Animation(mInvalidatePanelMenuRunnable); } } else { dow(this); } if (mContentParent == null) { mContentParent = generateLayout(mDecor); ... }  在这个⽅法⼜两个主要步骤,⾸先是使⽤generateDecor()⽅法创建了DecorView对象,generateDecor()⽅法⽐较简单,主要就是执⾏new DecorView(context, featureId, this, getAttributes())⽅法,这⾥不再贴出代码;重点来看generateLayout()⽅法,这个⽅法⽣成的mContentParent是作为来我们后续加载加载的⽤户的布局的⽗布局存在的。 protected ViewGroup generateLayout(DecorView decor) { // Apply data from current theme.     //获取当前主题的相关属性 TypedArray a = getWindowStyle(); ... //⼀⼤段的根据获取到到主题属性,解析保存到PhonwWindow的相关参数的变量中 int layoutResource; int features = getLocalFeatures();

     ... //⼀⼤段根据PhoneWindow的设定好的属性(features和mIsFloating)的判断,为layoutResource进⾏赋值, //值可以为_custom_title、_action_bar等 hanging();     //将layoutRsourece值对应的布局⽂件加载到DecorView中 urcesLoaded(mLayoutInflater, layoutResource);     //在加载给DecorView的布局⽂件中有⼀块id为ID_ANDROID_CONTENT的区域是⽤于⽤户显⽰⾃⼰布局的,也是setContextView传⼊的布局显⽰的地⽅     //这块区域会以ViewGroup的形式赋值给mContentParent变量,这个ViewGroup即是⽤户布局的⽗布局节点 ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);

     ... //继续⼀⼤段的属性配置 Changing(); return contentParent; }  generateLayout⽅法实际就是解析出主题的相关属性,根据不同的主题样式的属性值选择不同的布局⽂件设置到DecorView中(DecorView本事就是FrameLayout)。在view的树状结构下,DecorView即是整个Window显⽰的视图的根节点,在DecorView的⼦节点中⼜有⼀块id为ID_ANDROID_CONTENT的区域有⼀块区域作为mContentParent变量⽤于加载⽤户的布局,与mContentParent平级的视图有ActionBar视图和Title的视图。总结来说,installDecor()⽅法实质就是产⽣mDecor和mContentParent对象。在installDecor之后,会执⾏到e(layoutResID, mContentParent)⽅法将⽤户传⼊的布局转化为view再加⼊到mContentParent上。这样就完成了setContentView()流程。

三、handleResumeActivity()流程  在⽂章开头贴出的第⼀段LauncherActivity()⽅法的代码中,执⾏完performLaunchAcitity()创建好Acitivity后,便会执⾏到handleResumeActivity()⽅法,该⽅法代码如下。 final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) { ...// TODO Push resumeArgs into the activity for consideration // 该⽅法执⾏过程中会调⽤到Acitity的onResume()⽅法,返回的ActivityClientRecord对象描述的即是创建好的Activity     r = performResumeActivity(token, clearHide, reason); if (r != null) { final Activity a = ty;//返回之前创建的Acitivty if (localLOGV) Slog.v( TAG, "Resume " + r + " started activity: " + edActivity + ", hideForNow: " + rNow + ", finished: " + hed); final int forwardBit = isForward ? _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.       // 判断该Acitivity是否可见,mStartedAcitity记录的是⼀个Activity是否还处于启动状态       // 如果还处于启动状态则mStartedAcitity为true,表⽰该activity还未启动好,则该Activity还不可见       boolean willBeVisible = !edActivity;       // 如果启动的组建不是全屏的,mStartedActivity也会是true,此时依然需要willBeVisible为true以下的if逻辑就是针对这种情况的校正 if (!willBeVisible) { try { willBeVisible = ault().willActivityBeVisible( ivityToken()); } catch (RemoteException e) { throw wFromSystemServer(); } } if ( == null && !hed && willBeVisible) { = dow(); View decor = orView(); ibility(BLE); ViewManager wm = dowManager(); Params l = ributes(); = decor; = _BASE_APPLICATION; putMode |= forwardBit;          //PreserverWindow,⼀般指主题换了或者configuration变了情况下的Acitity快速重启机制 if (rveWindow) { wAdded = true; rveWindow = 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 = wRootImpl(); if (impl != null) { ChildRebuilt(); } } if (leFromClient && !wAdded) { wAdded = true;            //调⽤了WindowManagerImpl的addView⽅法 w(decor, l); }   ... }  重点来看w()⽅法,该⽅法中的decor参数为Acitity对应的Window中的视图DecorView,wm为在创建PhoneWindow是创建的WindowManagerImpl对象,该对象的addView⽅法实际调⽤到到是单例对象WindowManagerGlobal的addView⽅法(前⽂有提到)。在看addView代码前,我先来看看WindowManagerGlobal对象成员变量。 private static WindowManagerGlobal sDefaultWindowManager; private static IWindowManager sWindowManagerService; private static IWindowSession sWindowSession; private final Object mLock = new Object(); private final ArrayList mViews = new ArrayList(); private final ArrayList mRoots = new ArrayList(); private final ArrayList mParams = new ArrayList(); private final ArraySet mDyingViews = new ArraySet();  三个成员变量mViews、mRoots和mParams分别是类型为View、ViewRootImpl和Params的数组。这⾥有这样的逻辑关系,每个View都对应着唯⼀的⼀个ViewRootImpl和Rarams,即是1:1:1的关系。这三个数组长度始终保持⼀致,并且在同⼀个位置上存放的是互相关联的View、ViewRootImpl和Params对象。此外还有⼀个成员变量mDyView,保存的则是已经不需要但还未被系统会收到View。  View与LayoutParams⽐较好理解,那ViewRootImpl对象的作⽤是什么呢?⾸先WindowManagerImpl是作为管理类,就像主管⼀样,根据Acitity和Window的调⽤请求,找到合适的做事的⼈;DecorView本⾝是FrameworkLayout,本事是⼀个View,所表⽰的是⼀种静态的结构;所以这⾥就需要⼀个真正做事的⼈,那就是ViewRootImpl类的⼯作。总结来讲ViewRootImpl的功能如下1. 完成了绘制过程。在ViewRootImpl类中,实现了perfromMeasure()、performDraw()、performLayout()等绘制相关的⽅法。2. 与系统服务进⾏交互,例如与AcitityManagerSerivice,DisplayService、AudioService等进⾏通信,保证了Acitity相关功能等正常运转。3. 触屏事件等分发逻辑的实现  接下来我们进⼊w()⽅法的代码。 public void addView(View view, Params params, Display display, Window parentWindow) { ... ViewRootImpl root; View panelParentView = null; synchronized (mLock) {       ... // If this is a panel window, then find the window it is being // attached to for future reference.       // 如果当前添加的是⼀个⼦视图,则还需要找他他的⽗视图       //这⾥我们分析的是添加DecorView的逻辑,没有⽗视图,故不会⾛到这⾥,panelParentView为null       if ( >= _SUB_WINDOW && <= _SUB_WINDOW) { final int count = (); for (int i = 0; i < count; i++) { if ((i).er() == ) { panelParentView = (i); } } } root = new ViewRootImpl(text(), display); outParams(wparams);       //保存互相对应的View、ViewRootImpl、Params到数组中 (view); (root); (wparams); // do this last because it fires off messages to start doing things try { w(view, wparams, panelParentView); } catch (RuntimeException e) { // BadTokenException or InvalidDisplayException, clean up. if (index >= 0) { removeViewLocked(index, true); } throw e; } } }  关注代码中加粗的两个⽅法,⾸先会创建⼀个ViewRootImpl对象,然后调⽤w⽅法,其中panelParentView在addView参数为DecorView是为null。进⼊w()代码。

public void setView(View view, Params attrs, View panelParentView) { synchronized (this) { if (mView == null) {          //初始化成员变量mView、mWindowAttraibutes          //mAttachInfo是View类的⼀个内部类AttachInfo类的对象 //该类的主要作⽤就是储存⼀组当View attach给它的⽗Window的时候Activity各种属性的信息 mView = view; ayState = te(); erDisplayListener(mDisplayListener, mHandler); mViewLayoutDirectionInitial = LayoutDirection(); w(view); om(attrs); ... //继续初始化⼀些变量,包含针对panelParentView不为null时的⽗窗⼝的⼀些处理          mAdded = true; // 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.          // 这⾥调⽤异步刷新请求,最终会调⽤performTraversals⽅法来完成View的绘制          requestLayout(); if ((eatures & _FEATURE_NO_INPUT_CHANNEL) == 0) { mInputChannel = new InputChannel(); } mForceDecorViewVisibility = (eFlags & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0; try { mOrigWindowType = ; puteGlobalAttributes = true; collectViewAttributes(); res = isplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), playId(), ntInsets, eInsets, ts, mInputChannel); } catch (RemoteException e) { mAdded = false; mView = null; iew = null; mInputChannel = null; w(null); unscheduleTraversals(); setAccessibilityFocus(null, null); throw new RuntimeException("Adding window failed", e); } finally { if (restore) { e(); } } ... } } }  相关变量初始化完成后,便会将mAdded设置为true,表⽰ViewRootImpl与setView传⼊的View参数已经做好了关联。之后便会调⽤requestLayout()⽅法来请求⼀次异步刷新,该⽅法后来⼜会调⽤到performTraversals()⽅法来完成view到绘制⼯作。注意到这⾥虽然完成了绘制的⼯作,但是我们创建Activity的源头是AMS中发起的,我们从⼀开始创建Acitivity到相对应的Window、DecorView这⼀⼤套对象时,还并未与AMS进程进⾏反馈。所以之后便会调⽤isplay()⽅法会执⾏IPC的跨进程通信,最终调⽤到AMS中的addWindow⽅法来在系统进程中执⾏相关加载Window的操作。

发布者:admin,转转请注明出处:http://www.yc00.com/xiaochengxu/1687842263a50010.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信