待补充!
一个窗口本质上是一个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);
该代码主要完成了三个工作:
- generateDecor()生成一个DecorView对象,赋值给mDecor,mDecor并不是窗口修饰,而是窗口修饰的唯一子视图。
- 根据用户指定的参数来选择不同的窗口修饰,然后将窗口修饰作为mDecor的子窗口,这是在generateLayout()中调用mDecor.addView()完成的。
- 给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的父布局,窗口的视图设置就完成了。