Activity、View、Window之间的关系能够用如下的简要UML关系图表示,在这里贴出来,比较可以帮组后面流程分析部分的阅读。java
1、Activity的启动流程android
在startActivity()后,通过一些逻辑流程会通知到ActivityManagerService(后面以AMS简称),AMS接收到启动acitivty的请求后,会经过跨进程通讯调用AcitivtyThread.handleLauncherActivity()方法,咱们从这里开始分析,首先来看handleLauncherActivity()方法。
windows
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
...// Initialize before creating the activity WindowManagerGlobal.initialize(); Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
reportSizeConfigurations(r);
Bundle oldState = r.state;数组
//该方法会调用到Activity的onResume()方法
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);app
...异步
}ide
... }
这里重点关注三个方法(加粗的地方),首先来看WindowManagerGlobal.initialize(),WindowManagerGlobal是单例模式的,一个进程内只有一个,这里调用该类的初始化方法,后续咱们再对该类的做用和相关方法进行分析;第三个是在建立好Activity后调用Acitivty的onResume()方法。这里咱们来看需关注的第二个方法performLaunchActivity(),代码以下。布局
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
//经过反射方式建立Activity Activity activity = null; try { java.lang.ClassLoader cl = r.packageInfo.getClassLoader(); activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); StrictMode.incrementExpectedActivityCount(activity.getClass()); r.intent.setExtrasClassLoader(cl); r.intent.prepareToEnterProcess(); if (r.state != null) { r.state.setClassLoader(cl); } } catch (Exception e) { if (!mInstrumentation.onException(activity, e)) { throw new RuntimeException( "Unable to instantiate activity " + component + ": " + e.toString(), e); } } try { ...
if (activity != null) { Context appContext = createBaseContextForActivity(r, activity); CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager()); Configuration config = new Configuration(mCompatConfiguration); if (r.overrideConfig != null) { config.updateFrom(r.overrideConfig); } if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity " + r.activityInfo.name + " with config " + config); Window window = null; if (r.mPendingRemoveWindow != null && r.mPreserveWindow) { window = r.mPendingRemoveWindow; r.mPendingRemoveWindow = null; r.mPendingRemoveWindowManager = null; } activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor, window);
...
//调用acitivity的onCreate()方法
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}post
...
}
return activity; }
这个方法主要是读取Acitivity这里利用反射建立出ActivityClientRecord所要求的Activity对象,而后调用了acitivity.attach()方法。注意attach()传入的参数有不少,在performLacunchActivity()方法流程中,调用attach()方前,咱们省略掉的步骤基本都在为这些参数作准备,attach()方法的做用其实就是将这些参数配置到新建立的Activity对象中;而在attach以后则会回调到acitivity的onCreate()方法。咱们进入Activity.java类详细来看下attach方法。动画
此外,在attach以前会初始化一个Window对象,Window.java是一个抽象类,表明了一个矩形不可见的容器,主要负责加载显示界面,每一个Activity都会对应了一个Window对象。若是ActivityClientRecord.mPendingRevomeWindow变量中已经保存了一个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); mFragments.attachHost(null /*parent*/);
mWindow = new PhoneWindow(this, window);//(1) mWindow.setWindowControllerCallback(this); mWindow.setCallback(this); mWindow.setOnWindowDismissedCallback(this); mWindow.getLayoutInflater().setPrivateFactory(this); if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) { mWindow.setSoftInputMode(info.softInputMode); } if (info.uiOptions != 0) { mWindow.setUiOptions(info.uiOptions); }
... //初始化Acitity相关属性 mWindow.setWindowManager( (WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken, mComponent.flattenToString(), (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);//(2) if (mParent != null) { mWindow.setContainer(mParent.getWindow()); } mWindowManager = mWindow.getWindowManager(); 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) preservedWindow.getDecorView(); mElevation = preservedWindow.getElevation(); 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 = preservedWindow.getAttributes().token; } // Even though the device doesn't support picture-in-picture mode, // an user can force using it through developer options. boolean forceResizable = Settings.Global.getInt(context.getContentResolver(), DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0; mSupportsPictureInPicture = forceResizable || context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_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()方法的逻辑,这段代码是在PhonewWindow.java的父类Window.java中代码以下。
public void setWindowManager(WindowManager wm, IBinder appToken, String appName, boolean hardwareAccelerated) { mAppToken = appToken; mAppName = appName; mHardwareAccelerated = hardwareAccelerated || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false); if (wm == null) { wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); } mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this); }
对于wWindowManager变量,实际上这里是建立了一个WindowManagerImpl对象。首先是这种首先获取系统服务的代理到wm上,而后强制转换为WindowManagerImpl调用createLocalWindowManager(),在createLocalWindowManager()实际是执行了一个new WindowManagerImpl()到方法来建立。关于这部分代码看了好久很困惑的一个点,就是为啥要弄个这么复杂的逻辑,直接把上面加粗的代码改成new WindowManagerImpl(...),这养会有什么区别吗?若是有大虾看到这里,但愿能帮我解答。
在WindowManager中保存了对于单例对象WindowManagerGloble的引用,即mGlobal变量。此外,WindowManager.java实现了WindowManager又,而WindowManager继承自ViewManager接口,ViewManager接口方法以下方代码。
public interface ViewManager { /** * Assign the passed LayoutParams to the passed View and add the view to the window. * <p>Throws {@link android.view.WindowManager.BadTokenException} for certain programming * errors, such as adding a second view to a window without removing the first view. * <p>Throws {@link android.view.WindowManager.InvalidDisplayException} if the window is on a * secondary {@link Display} and the specified display can't be found * (see {@link android.app.Presentation}). * @param view The view to be added to this window. * @param params The LayoutParams to assign to view. */ public void addView(View view, ViewGroup.LayoutParams params); public void updateViewLayout(View view, ViewGroup.LayoutParams 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的启动。
2、setContentView()流程
首先,咱们通常在onCreate()里调用setContentView()的方法。
@override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); }
这里实际调用到到地方是Acitivity.java类中的setContentView()方法,以下。
public void setContentView(@LayoutRes int layoutResID) { getWindow().setContentView(layoutResID); initWindowDecorActionBar(); }
这里getWindow()返回的是Acitivity.java类中的mWindow变量,就是Activity建立时一块儿建立的PhoneWindow对象,进入到
@Override public void setContentView(int layoutResID) { if (mContentParent == null) { installDecor(); } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mContentParent.removeAllViews();//若是屡次调用setContentView则会执行removeAllView操做 } if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { //过渡动画机制相关 final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID, getContext()); transitionTo(newScene); } else { mLayoutInflater.inflate(layoutResID, mContentParent); } mContentParent.requestApplyInsets(); final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); } mContentParentExplicitlySet = true; }
代码里涉及到FEATURE_CONTENT_TRANSITIONS的属性,这里是Android的过渡动画相关机制,这里咱们再也不展开详述。通常的Acitivty启动时,会进入mContentParent为null的逻辑,首先调用的是installDecor()方法,完成DecorView的建立工做;以后调用mLayoutInflater.inflate()方法将咱们传入的资源文件转换为view树,装载到mContentParent中。首先来看installDecor()代码。
private void installDecor() { mForceDecorInstall = false; if (mDecor == null) { //建立DecorView mDecor = generateDecor(-1); mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); mDecor.setIsRootNamespace(true); if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) { mDecor.postOnAnimation(mInvalidatePanelMenuRunnable); } } else { mDecor.setWindow(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进行赋值,
//值能够为R.layout.screen_custom_title、R.layout.screen_action_bar等 mDecor.startChanging();
//将layoutRsourece值对应的布局文件加载到DecorView中 mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
//在加载给DecorView的布局文件中有一块id为ID_ANDROID_CONTENT的区域是用于用户显示本身布局的,也是setContextView传入的布局显示的地方
//这块区域会以ViewGroup的形式赋值给mContentParent变量,这个ViewGroup便是用户布局的父布局节点 ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
... //继续一大段的属性配置 mDecor.finishChanging(); return contentParent; }
generateLayout方法实际就是解析出主题的相关属性,根据不一样的主题样式的属性值选择不一样的布局文件设置到DecorView中(DecorView本事就是FrameLayout)。在view的树状结构下,DecorView便是整个Window显示的视图的根节点,在DecorView的子节点中又有一块id为ID_ANDROID_CONTENT的区域有一块区域做为mContentParent变量用于加载用户的布局,与mContentParent平级的视图有ActionBar视图和Title的视图。总结来讲,installDecor()方法实质就是产生mDecor和mContentParent对象。在installDecor以后,会执行到mLayoutInflater.inflate(layoutResID, mContentParent)方法将用户传入的布局转化为view再加入到mContentParent上。这样就完成了setContentView()流程。
3、handleResumeActivity()流程
在文章开头贴出的第一段AcitityThread.handleLauncherActivity()方法的代码中,执行完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 = r.activity;//返回以前建立的Acitivty 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.
// 判断该Acitivity是否可见,mStartedAcitity记录的是一个Activity是否还处于启动状态
// 若是还处于启动状态则mStartedAcitity为true,表示该activity还未启动好,则该Activity还不可见
boolean willBeVisible = !a.mStartedActivity;
// 若是启动的组建不是全屏的,mStartedActivity也会是true,此时依然须要willBeVisible为true如下的if逻辑就是针对这种状况的校订 if (!willBeVisible) { try { willBeVisible = ActivityManagerNative.getDefault().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;
//PreserverWindow,通常指主题换了或者configuration变了状况下的Acitity快速重启机制 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 && !a.mWindowAdded) { a.mWindowAdded = true;
//调用了WindowManagerImpl的addView方法 wm.addView(decor, l); } ... }
重点来看wm.addView()方法,该方法中的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<View> mViews = new ArrayList<View>(); private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>(); private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>(); private final ArraySet<View> mDyingViews = new ArraySet<View>();
三个成员变量mViews、mRoots和mParams分别是类型为View、ViewRootImpl和WindowManager.LayoutParams的数组。这里有这样的逻辑关系,每一个View都对应着惟一的一个ViewRootImpl和WindowManager.LayoutRarams,便是1:1:1的关系。这三个数组长度始终保持一致,而且在同一个位置上存放的是互相关联的View、ViewRootImpl和WindowManager.LayoutParams对象。此外还有一个成员变量mDyView,保存的则是已经不须要但还未被系统会收到View。
View与LayoutParams比较好理解,那ViewRootImpl对象的做用是什么呢?首先WindowManagerImpl是做为管理类,就像主管同样,根据Acitity和Window的调用请求,找到合适的作事的人;DecorView自己是FrameworkLayout,本事是一个View,所表示的是一种静态的结构;因此这里就须要一个真正作事的人,那就是ViewRootImpl类的工做。总结来说ViewRootImpl的功能以下
1. 完成了绘制过程。在ViewRootImpl类中,实现了perfromMeasure()、performDraw()、performLayout()等绘制相关的方法。
2. 与系统服务进行交互,例如与AcitityManagerSerivice,DisplayService、AudioService等进行通讯,保证了Acitity相关功能等正常运转。
3. 触屏事件等分发逻辑的实现
接下来咱们进入WindowManagerGlobal.addView()方法的代码。
public void addView(View view, ViewGroup.LayoutParams 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 (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW && wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { final int count = mViews.size(); for (int i = 0; i < count; i++) { if (mRoots.get(i).mWindow.asBinder() == wparams.token) { panelParentView = mViews.get(i); } } }
root = new ViewRootImpl(view.getContext(), display); view.setLayoutParams(wparams);
//保存互相对应的View、ViewRootImpl、WindowManager.LayoutParams到数组中 mViews.add(view); mRoots.add(root); mParams.add(wparams); // do this last because it fires off messages to start doing things try { root.setView(view, wparams, panelParentView); } catch (RuntimeException e) { // BadTokenException or InvalidDisplayException, clean up. if (index >= 0) { removeViewLocked(index, true); } throw e; } } }
关注代码中加粗的两个方法,首先会建立一个ViewRootImpl对象,而后调用ViewRootImpl.setView方法,其中panelParentView在addView参数为DecorView是为null。进入ViewRootImpl.setView()代码。
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) {
//初始化成员变量mView、mWindowAttraibutes
//mAttachInfo是View类的一个内部类AttachInfo类的对象
//该类的主要做用就是储存一组当View attach给它的父Window的时候Activity各类属性的信息
mView = view; mAttachInfo.mDisplayState = mDisplay.getState(); mDisplayManager.registerDisplayListener(mDisplayListener, mHandler); mViewLayoutDirectionInitial = mView.getRawLayoutDirection(); mFallbackEventHandler.setView(view); mWindowAttributes.copyFrom(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 ((mWindowAttributes.inputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) { 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(), mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, 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(); } } ... } } }
相关变量初始化完成后,便会将mAdded设置为true,表示ViewRootImpl与setView传入的View参数已经作好了关联。以后便会调用requestLayout()方法来请求一次异步刷新,该方法后来又会调用到performTraversals()方法来完成view到绘制工做。注意到这里虽然完成了绘制的工做,可是咱们建立Activity的源头是AMS中发起的,咱们从一开始建立Acitivity到相对应的Window、DecorView这一大套对象时,还并未与AMS进程进行反馈。因此以后便会调用mWindowSession.addToDisplay()方法会执行IPC的跨进程通讯,最终调用到AMS中的addWindow方法来在系统进程中执行相关加载Window的操做。