原文转载:https://www.cnblogs.com/tiger-wang-ms/p/6517048.html
首先要关注的就是 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 的启动。
二、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() 流程。
点击下方链接收费获取 Android 进阶材料:
https://shimo.im/docs/tXXKHgdjPYj6WT8d/