前言

本节内容分为上下两章,上章主要介绍布局流程和 xml 解析流程,下章带着大家手写插件化换肤框架的核心实现;
布局流程
想要布局,我们必须要启动 Activity 然后才能进行布局的渲染操作,启动 Activity 我们可以通过 ActivityThread 的 performLaunchActivity 看起,本章不详解 Activity 的启动流程,感兴趣的可以关注我,在 FrameWork 系列我后面会详细讲解,所以 我们进入 performLaunchActivity 方法看下:
typescript
复制代码
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { // ... // 省略部分代码 // 创建 Activity activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); if (activity != null) { Window window = null; if (r.mPendingRemoveWindow != null && r.mPreserveWindow) { window = r.mPendingRemoveWindow; } // ... // 省略部分代码 // 可以看到,attach 的时候,将 window 传递了过去,我们去 attach 看下 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, r.configCallback, r.assistToken); } }
进入 Activity 的 attach 方法看下:
arduino
复制代码
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, ActivityConfigCallback activityConfigCallback, IBinder assistToken) { // ... // 省略部分代码 // window 在 attach 方法里进行了 初始化 mWindow = new PhoneWindow(this, window, activityConfigCallback); // ... // 省略部分代码 }
也就是说 Activity 中 持有着一个 PhoneWindow,这里建立了 Activity 和 Window 的关联;
当 Activity 执行 onCreate 的时候,我们需要调用 setContentView 设置布局,这个setContentView 就是 PhoneWindow 提供的一个方法,我们进入这个方法看下:
scss
复制代码
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) { // 第一个知识点 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 { // 第二个知识点 mLayoutInflater.inflate(layoutResID, mContentParent); } mContentParent.requestApplyInsets(); final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); } mContentParentExplicitlySet = true; }
setContentView 方法中主要关注两个方法的调用,installDecor() 和 mLayoutInflater.inflate();
首先我们来看下 installDecor 都干了什么?
installDecor
scss
复制代码
private void installDecor() { // ... // 省略部分代码 if (mDecor == null) { mDecor = generateDecor(-1); mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); mDecor.setIsRootNamespace(true); } else { mDecor.setWindow(this); } // 使用 DecorView 生成布局 if (mContentParent == null) { mContentParent = generateLayout(mDecor); } // ... // 省略部分代码 }
如果 mDecor 为空,就调用 generateDecor 方法,进行创建,我们进入这个方法看一下:
generateDecor
scss
复制代码
protected DecorView generateDecor(int featureId) { // System process doesn't have application context and in that case we need to directly use // the context we have. Otherwise we want the application context, so we don't cling to the // activity. Context context; if (mUseDecorContext) { Context applicationContext = getContext().getApplicationContext(); if (applicationContext == null) { context = getContext(); } else { context = new DecorContext(a

最低0.47元/天 解锁文章

被折叠的 条评论
为什么被折叠?



