android的窗口创建过程

待补充!

一个窗口本质上是一个view,而Window类只是一个应用窗口的抽象。

①    启动activity的代码本质,是一个创建activity的过程,是由ActivityThread完成的。其代码如下:

2109        Activity activity = null;
2110        try {
2111            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
2112            activity = mInstrumentation.newActivity(
2113                    cl, component.getClassName(), r.intent);
2114            StrictMode.incrementExpectedActivityCount(activity.getClass());
2115            r.intent.setExtrasClassLoader(cl);
2116            if (r.state != null) {
2117                r.state.setClassLoader(cl);
2118            }
2119        } catch (Exception e) {
2120            if (!mInstrumentation.onException(activity, e)) {
2121                throw new RuntimeException(
2122                    "Unable to instantiate activity " + component
2123                    + ": " + e.toString(), e);
2124            }
2125        }

使用ClassLoader从程序文件中装载指定的activity对应的class文件。

②    构造好指定的activity对象后,接着调用activity的attach方法,代码如下:

2144                activity.attach(appContext, this, getInstrumentation(), r.token,
2145                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
2146                        r.embeddedID, r.lastNonConfigurationInstances, config);

attach方法的第一个作用是为刚刚构造好的activity设置内部变量,不是现在的重点,忽略。第二个作用就是为该activity创建Window对象,代码如下:

5180    final void attach(Context context, ActivityThread aThread,
5181            Instrumentation instr, IBinder token, int ident,
5182            Application application, Intent intent, ActivityInfo info,
5183            CharSequence title, Activity parent, String id,
5184            NonConfigurationInstances lastNonConfigurationInstances,
5185            Configuration config) {
5186        attachBaseContext(context);
5187
5188        mFragments.attachActivity(this, mContainer, null);
5189       
5190        mWindow = PolicyManager.makeNewWindow(this);
5191        mWindow.setCallback(this);

Window对象是由PolicyManager的静态方法makeNewWindow()完成的。PolicyManager会根据不同的类型来创建不同的产品类型窗口(可以看看策略设计模式),这里其代码只是创建了一个PhoneWindow对象而已。当前的Framework中仅仅只有两种Window的具体实现:PhoneWindow—手机,MidWindow—便携上网设备,这里也可以看出android设计的初衷。Window对象创建完成之后,将其值赋值给Activity的内部变量mWindow,并设置Window的Callback回调接口为当前的activity对象(显然activity需要实现这个接口),这就是为什么用户消息能够传递到Activity中的原因。

③    创建好Window对象之后,需要给Window对象的mWindowManager赋值。这个也不是此文重点,但是仍然说明一下,赋值代码如下:

mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null) {
     mWindow.setContainer(mParent.getWindow());
}
mWindowManager = mWindow.getWindowManager();

Window的setWindowManager方法里面:

mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);

createLocalWindowManager方法的实现:

public WindowManagerImpl createPresentationWindowManager(Display display) {
      return new WindowManagerImpl(display, mParentWindow);
}

所以最终,Window是创建了一个WindowManagerImpl对象,WindowManager是一个接口。

④    配置好Activity和Window之后,接下来需要给窗口添加真正的显示元素View或者ViewGroup了。ActivityThead启动activity的一系列流程,都在其performLaunchActivity()中。在attach()之后便会执行到callActivityOnCreate()方法,这个方法会触发Activity的onCreate方法。而在onCreate方法里面,我们会setContentView(),看看其代码:

public void setContentView(int layoutResID) {
     getWindow().setContentView(layoutResID);
     initActionBar();
}

这个方法调用Window对象的setContentView方法,接下来我们要分析的就是看Window是如何把layout.xml作为Window界面的显示元素。

PhoneWindow的setContentView()方法如下:

284     public void setContentView(int layoutResID) {
285         if (mContentParent == null) {
286             installDecor();
287         } else {
288             mContentParent.removeAllViews();
289         }
290         mLayoutInflater.inflate(layoutResID, mContentParent);
291         final Callback cb = getCallback();
292         if (cb != null && !isDestroyed()) {
293             cb.onContentChanged();
294         }
295     }

代码首先调用installDecor()方法为Window类安装一个窗口修饰,所谓的窗口修饰就是界面上常见的标题栏,我们写的layout.xml将会被包含到窗口修饰中,称为窗口内容。窗口修饰及窗口内容关系如下图:

Framework中定义了多种窗口修饰,installDecor()代码如下:

3075    private void installDecor() {
3076        if (mDecor == null) {
3077            mDecor = generateDecor();
3078            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
3079            mDecor.setIsRootNamespace(true);
3080            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
3081                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
3082            }
3083        }
3084        if (mContentParent == null) {
3085            mContentParent = generateLayout(mDecor);

该代码主要完成了三个工作:

  1. generateDecor()生成一个DecorView对象,赋值给mDecor,mDecor并不是窗口修饰,而是窗口修饰的唯一子视图。
  2. 根据用户指定的参数来选择不同的窗口修饰,然后将窗口修饰作为mDecor的子窗口,这是在generateLayout()中调用mDecor.addView()完成的。
  3. 给mContentParent变量赋值,generateLayout返回值的由来:
...
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); ... return contentParent;

那么这个ID_ANDROID_CONTENT是什么呢?

public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;

就是找一个id=content的布局,为什么返回这个以及返回这个做什么,请看下面!

其中第二个工作非常重要,根据用户指定的参数来选择不用的窗口修饰实际上就是指定activity的样式。

generateLayout()中调用mDecor.addView()的view是怎么来的?看如下代码:

int layoutResource;
...
int features = getLocalFeatures();//这里就是我们在onCreate里写requestFeature()起作用的原因。
2967        if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
2968            if (mIsFloating) {
2969                TypedValue res = new TypedValue();
2970                getContext().getTheme().resolveAttribute(
2971                        com.android.internal.R.attr.dialogTitleIconsDecorLayout, res, true);
2972                layoutResource = res.resourceId;
2973            } else {
2974                layoutResource = com.android.internal.R.layout.screen_title_icons;
2975            }
2976            // XXX Remove this once action bar supports these features.
2977            removeFeature(FEATURE_ACTION_BAR);
2978            // System.out.println("Title Icons!");
2979        } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
2980                && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
2983            layoutResource = com.android.internal.R.layout.screen_progress;
2985        } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
2988            if (mIsFloating) {
2989                TypedValue res = new TypedValue();
2990                getContext().getTheme().resolveAttribute(
2991                        com.android.internal.R.attr.dialogCustomTitleDecorLayout, res, true);
2992                layoutResource = res.resourceId;
2993            } else {
2994                layoutResource = com.android.internal.R.layout.screen_custom_title;
2995            }
2997            removeFeature(FEATURE_ACTION_BAR);
2998        } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
3001            if (mIsFloating) {
3002                TypedValue res = new TypedValue();
3003                getContext().getTheme().resolveAttribute(
3004                        com.android.internal.R.attr.dialogTitleDecorLayout, res, true);
3005                layoutResource = res.resourceId;
3006            } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
3007                layoutResource = com.android.internal.R.layout.screen_action_bar;
3008            } else {
3009                layoutResource = com.android.internal.R.layout.screen_title;
3010            }
3012        } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
3013            layoutResource = com.android.internal.R.layout.screen_simple_overlay_action_mode;
3014        } else {
3016            layoutResource = com.android.internal.R.layout.screen_simple;
3018        }

上面的代码就是,根据不同的修饰来加载不同的布局:

3022        View in = mLayoutInflater.inflate(layoutResource, null);
3023        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
3024
3025        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);

加载完布局之后,将布局添加到DecorVeiw中,并将布局中id=content的布局返回出去。

R.layout.screen_simple的代码:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:fitsSystemWindows="true" 
    android:orientation="vertical"> 

    <ViewStub android:id="@+id/action_mode_bar_stub" 
              android:inflatedId="@+id/action_mode_bar" 
              android:layout="@layout/action_mode_bar" 
              android:layout_width="match_parent" 
              android:layout_height="wrap_content" /> 

    <FrameLayout 
         android:id="@android:id/content" 
         android:layout_width="match_parent" 
         android:layout_height="match_parent" 
         android:foregroundInsidePadding="false" 
         android:foregroundGravity="fill_horizontal|top" 
         android:foreground="?android:attr/windowContentOverlay" /> 

</LinearLayout> 

如此,回到setContentView方法,就是将id=content的布局返回了,并且作为我们写的layout.xml的父布局,窗口的视图设置就完成了。

原文地址:https://www.cnblogs.com/aprz512/p/4606025.html