Android窗口機制系列
Android窗口機制(一)初識Android的窗口結構
Android窗口機制(二)Window,PhoneWindow,DecorView,setContentView源碼理解
Android窗口機制(三)Window和WindowManager的創建與Activity
Android窗口機制(四)ViewRootImpl與View和WindowManager
Android窗口機制(五)最終章:WindowManager.LayoutParams和Token以及其他窗口Dialog,Toast
前兩篇文章跟大家介紹了Window,PhoneWindow,DecorView他們間的聯系,以及他們之間的理解。講到Window大家肯定會想到常見的WindowManager,兩者肯定是發生過關系的。此外對于Window和WindowManager的創建問題,正是下面要介紹的。
了解他們前,我們先來看個結構
ViewManager
/** Interface to let you add and remove child views to an Activity. To get an instance
* of this class, call {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
*/
public interface ViewManager
{
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}
ViewManager接口定義了一組規則,也就是add、update、remove的操作View接口。也就是說ViewManager是用來添加和移除activity中View的接口,可以通過Context.getSystemService()獲取實例。
我們看下ViewManager的實現類
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
private static final String TAG = "ViewGroup";
...
public void addView(View child, LayoutParams params) {
addView(child, -1, params);
}
/*
* @param child the child view to add
* @param index the position at which to add the child or -1 to add last
* @param params the layout parameters to set on the child
*/
public void addView(View child, int index, LayoutParams params) {
// addViewInner() will call child.requestLayout() when setting the new LayoutParams
// therefore, we call requestLayout() on ourselves before, so that the child's request
// will be blocked at our level
requestLayout();
invalidate(true);
addViewInner(child, index, params, false);
}
...
可以看到ViewGroup里面實現了ViewManager接口,View通過ViewGroup的addView方法添加到ViewGroup中,而ViewGroup層層嵌套到最頂級都會顯示在在一個窗口Window中
WindowManager
我們還是看下源碼說明
/* The interface that apps use to talk to the window manager.
Use Context.getSystemService(Context.WINDOW_SERVICE) to get one of these.
*/
public interface WindowManager extends ViewManager {
public static class BadTokenException extends RuntimeException{...}
public static class InvalidDisplayException extends RuntimeException{...}
public Display getDefaultDisplay();
public void removeViewImmediate(View view);
public static class LayoutParams extends ViewGroup.LayoutParams
implements Parcelable
可以看到WindowManager是一個接口,而且它繼承與ViewManager。WindowManager字面理解就是窗口管理器,每一個窗口管理器都與一個的窗口顯示綁定。獲取實例可以通過
Context.getSystemService(Context.WINDOW_SERVICE)獲取。既然繼承了ViewManager,那么它也就可以進行添加刪除View的操作了,不過它的操作放在它的實現類WindowManagerImpl里面。成員變量里面
- BadTokenException:則是addView時它的LayoutParams無效則會被拋出,或是添加第二個View的時候沒有移除第一個View則會被拋出
- InvalidDisplayException:如果一個窗口是在一個二級的顯示上而指定的顯示找不到則會被拋出
- getDefaultDisplay:返回當前WindowManager管理的顯示Display
- removeViewImmediate:表示從窗口上移除View,一般是當View調用了onDetachedFromWindow也就是從Window上分開后,把它移除。
- LayoutParams:靜態內部類。顯然是Window的布局參數,里面定義了一系列的窗口屬性。
WindowManagerImpl
WindowManager的實現類
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
private final Display mDisplay;
private final Window mParentWindow;
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mDisplay, mParentWindow);
}
...
@Override
public void removeView(View view) {
mGlobal.removeView(view, false);
}
}
可以看到WindowManagerImpl里面有一個成員變量WindowManagerGlobal,而真正的實現則是在WindowManagerGlobal了,類似代理,只不過WindowManagerGlobal是個沒有實現WindowManager的類的,自己定義了套實現。
public final class WindowManagerGlobal {
private static final String TAG = "WindowManager";
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
...
}
}
聯系
大概了解了上述類的分類和各自的作用,那么他們之間如何聯系,Window如何創建如何與WindowManager綁定與Activity綁定呢,這個時候就需要一個場景來逐一理解。我們都知道每一個Activity都是與一個Window綁定一起的,那么Window的創建以及WindowManager的綁定會不會在創建啟動Activity的過程中就綁定的呢。
對于Activity的啟動過程,是有兩種,一種是點擊程序進入啟動的Activity,另一種而是在已有的Activity中調用startActivity,啟動期間通過Binder驅動ActivityWindowService,ActivityThread,ApplicationThread,ActivityStack ,Activity之間進行通信,為當前Activity創建進程分配任務棧后啟動Activity。這里就跳過前面很多步驟,直接到了ActivityThread.handleLaunchActivity去查看Activity的創建
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
// Initialize before creating the activity
WindowManagerGlobal.initialize();
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
Bundle oldState = r.state;
handleResumeActivity(r.token, false, r.isForward,
...
}
可以看到 WindowManagerGlobal.initialize()則通過WindowManagerGlobal創建了WindowManagerServer,接下來調用了performLaunchActivity
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
Activity activity = null;
try { //Activity通過ClassLoader創建出來
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
} ...
try {
//創建Application
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
...
if (activity != null) {
//創建Activity所需的Context
Context appContext = createBaseContextForActivity(r, activity);
...
//將Context與Activity進行綁定
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);
...
//調用activity.oncreate
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
...
//調用Activity的onstart方法
activity.performStart();
//調用activitu的OnRestoreInstanceState方法進行Window數據恢復
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
r.persistentState);
...
return activity;
}
先通過調用 activity = mInstrumentation.newActivity創建Activity,可以看到里面是通過ClassLoader來加載的
public Activity newActivity(ClassLoader cl, String className,
Intent intent)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
return (Activity)cl.loadClass(className).newInstance();
}
接著創建Activity所需的Application和Context,再調用到activity.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) {
//ContextImpl的綁定
attachBaseContext(context);
//在當前Activity創建Window
mWindow = new PhoneWindow(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
...
//為Window設置WindowManager
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());
}
//創建完后通過getWindowManager就可以得到WindowManager實例
mWindowManager = mWindow.getWindowManager();
mCurrentConfig = config;
}
可以看到在Activity創建到attach的時候,對應的Window窗口也被創建起來,而且Window也與WindowManager綁定。而mWindow,和mWindowManager則是Activity的成員變量??梢钥吹竭@里WindiwManager的創建是context.getSystemService(Context.WINDOW_SERVICE)
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
...
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
接著創建WindowManager的實現類,我們平時在Activity中使用getWindow()和getWindowManager,就是返回對應這兩個成員變量。
回到前面那個方法,調用了activity.attach后創建了Window和WindowManager,之后調用了
mInstrumentation.callActivityOnCreate(activity, r.state ...);
該方法則是調用activity.oncreate方法的
public void callActivityOnCreate(Activity activity, Bundle icicle) {
prePerformCreate(activity);
activity.performCreate(icicle);
postPerformCreate(activity);
}
final void performCreate(Bundle icicle) {
onCreate(icicle);
mActivityTransitionState.readState(icicle);
performCreateCommon();
}
之后直接調用了
activity.performStart();
來調用activity.onstart()方法
同樣之后也調用了
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state...);
看到onRestoreInstanceState是不是很熟悉,沒錯就是Activity數據恢復調用的方法
public void callActivityOnRestoreInstanceState(Activity activity, Bundle savedInstanceState) {
activity.performRestoreInstanceState(savedInstanceState);
}
final void performRestoreInstanceState(Bundle savedInstanceState) {
onRestoreInstanceState(savedInstanceState);
restoreManagedDialogs(savedInstanceState);
}
里面通過Bundle來保存恢復Window窗口信息
performLaunchActivity調用完后回到handleLaunchActivity
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
//初始化WindowManagerGlobal,為之后addView準備
WindowManagerGlobal.initialize();
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
Bundle oldState = r.state;
handleResumeActivity(r.token, false, r.isForward,
...
}
調用了performLauncherActiviy來創建Activity以及Activity所需要的Context,Window,調用了Activity的onCreate,onStart方法,而接下來調用了handleResumeActivity方法
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume) {
//調用activity.onResume,把activity數據記錄更新到ActivityClientRecord
ActivityClientRecord r = performResumeActivity(token, clearHide);
if (r != null) {
final Activity a = r.activity;
//activity.mStartedActivity是用來標記啟動Activity,有沒有帶返回值,一般我們startActivity(intent)是否默認是startActivityForResult(intent,-1),默認值是-1,所以這里mStartedActivity = false
boolean willBeVisible = !a.mStartedActivity;
...
//mFinished標記Activity有沒有結束,而r.window一開始activity并未賦值給ActivityClientRecord,所以這里為null
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;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
//把當前的DecorView與WindowManager綁定一起
wm.addView(decor, l);
}
...
if (!r.activity.mFinished && willBeVisible
&& r.activity.mDecor != null && !r.hideForNow) {
//標記當前的Activity有沒有設置新的配置參數,比如現在手機是橫屏的,而之后你轉成豎屏,那么這里的newCofig就會被賦值,表示參數改變
if (r.newConfig != null) {
r.tmpConfig.setTo(r.newConfig);
if (r.overrideConfig != null) {
r.tmpConfig.updateFrom(r.overrideConfig);
}
//然后調用這個方法回調,表示屏幕參數發生了改變
performConfigurationChanged(r.activity, r.tmpConfig);
...
WindowManager.LayoutParams l = r.window.getAttributes();
...//改變之后update更新當前窗口的DecorView
if (r.activity.mVisibleFromClient) {
ViewManager wm = a.getWindowManager();
View decor = r.window.getDecorView();
wm.updateViewLayout(decor, l);
}
}
//參數沒改變
r.activity.mVisibleFromServer = true;
mNumVisibleActivities++;
if (r.activity.mVisibleFromClient) {
//由于前面設置了INVASIBLE,所以現在要把DecorView顯示出來了
r.activity.makeVisible();
}
}
//通知ActivityManagerService,Activity完成Resumed
ActivityManagerNative.getDefault().activityResumed(token);
}
handleResumeActivity方法一開始就調用了activity = performResumeActivity()方法
public final ActivityClientRecord performResumeActivity(IBinder token,
boolean clearHide) {
ActivityClientRecord r = mActivities.get(token);
...
r.activity.mStartedActivity = false;
r.activity.onStateNotSaved();
r.activity.mFragments.noteStateNotSaved();
... //Activity調用onResume,就不再貼出來了,里面還有判斷要不呀奧onReStart,這個想必知道Activity生命周期的人就秒懂了
r.activity.performResume();
...
r.paused = false;
r.stopped = false;
r.state = null;
r.persistentState = null;
return r;
}
performResumeActivity則是讓Activity調用onResume方法,同時把Activity的信息記錄在ActivityClientRecord
之后進入到這里這個判斷方法。前面代碼注釋也有說到一些,這里再說詳細一點。
boolean willBeVisible = !a.mStartedActivity;
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;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
//把當前的DecorView與WindowManager綁定一起
wm.addView(decor, l);
}
r.window一開始null的,activity并沒有把它的window賦值給它
a.finished表示Activity是否結束
activity.mStartedActivity是用來標記啟動Activity需不需要帶返回值,一般我們startActivity(intent)是否默認是startActivityForResult(intent,-1),默認值是-1,所以這里mStartedActivity = false
if (requestCode >= 0) {
mStartedActivity = true;
}
接著獲取當前Activity的Window進而獲取到DecorView,再獲取當前Activity的WindowManager,將DecorView與WindowManager綁定一起。
注意:
這里的DecorView是setContentView之后的DecorView,也就是裝載我們的布局內容的。前面講到在handleLaucheActivity中,它會先調用performLaunchActivity,再調用handleResumeActivity方法,而在performLaunchActivity方法中先創建Activity對象,接著調用activity.attach方法,來綁定Context,同時在attach中創建了PhoneWindow以及WindowManager,attach之后,就調用了activity.oncreate方法,要知道,我們的setContentView是放在onCreate方法中的。有看過上一篇setContentView的源碼這里應該就會懂。DecorView是PhoneWindow的成員變量,所以setContentView可以說是將DecorView創建添加到Window上面的,調用setContentView后已經是把你的布局文件添加到DecorView了。
回到前面,因為是onCreate之后的,所以這里調用
View decor = r.window.getDecorView();
便可以得到當前Activity.Window下的DecorView,接下來通過創建好的WindowManager將DecorView與它綁定到一起.
之后到了newConfig參數這里,前面注釋已經解釋很清楚
if (!r.activity.mFinished && willBeVisible
&& r.activity.mDecor != null && !r.hideForNow) {
//標記當前的Activity有沒有設置新的配置參數,比如現在手機是橫屏的,而之后你轉成豎屏,那么這里的newCofig就會被賦值,表示參數改變
if (r.newConfig != null) {
r.tmpConfig.setTo(r.newConfig);
if (r.overrideConfig != null) {
r.tmpConfig.updateFrom(r.overrideConfig);
}
//然后調用這個方法回調,表示屏幕參數發生了改變
performConfigurationChanged(r.activity, r.tmpConfig);
...
WindowManager.LayoutParams l = r.window.getAttributes();
...//改變之后update更新當前窗口的DecorView
if (r.activity.mVisibleFromClient) {
ViewManager wm = a.getWindowManager();
View decor = r.window.getDecorView();
wm.updateViewLayout(decor, l);
}
}
這里我們看下performConfigurationChanged,可以先大膽猜測下這個方法肯定是來通知Activity參數改變的一個方法
private static void performConfigurationChanged(ComponentCallbacks2 cb, Configuration config) {
Activity activity = (cb instanceof Activity) ? (Activity) cb : null;
if (shouldChangeConfig) {
cb.onConfigurationChanged(config);
...
}
}
果然,這里調用到了一個接口回調,注意ComponentCallbacks2這個參數,看它傳進來的參數,是r.activity,也就是說Activity里面應該是實現了這個接口,接著通過回調去通知參數更改
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback {...}
果然如此。
之后來到了最后一步,handleResumeActivity方法里的最后
if (r.activity.mVisibleFromClient) {
//由于前面設置了INVASIBLE,所以現在要把DecorView顯示出來了
r.activity.makeVisible();
}
要知道,前面我們的DecorView可是設置了invisible(不知道是不是為了防止更新閃爍的問題),之后可能是要把它設置回來,就是在makevisible方法中
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
可以看到如果當前DecorView還未添加到WindwManager的話,則重新添加,最后設置為VISIBLE。
而我們平常在activity中使用setVisibility,也就是在設置DecorView是VISIBLE還是INVASIBLE
public void setVisible(boolean visible) {
if (mVisibleFromClient != visible) {
mVisibleFromClient = visible;
if (mVisibleFromServer) {
if (visible) makeVisible();
else mDecor.setVisibility(View.INVISIBLE);
}
}
}
至此,Activity被啟動起來,視圖(DecorView)也被創建(Window)管理(WindowManager)起來了。
小結
- 一個流程圖總結上面流程
- ViewManager接口定義了一組規則,也就是add、update、remove的操作View接口。ViewGroup實現了該接口
- WindowManager的實現類是WindowManagerImpl,而它則是通過WindowManagerGlobal代理實現。WindowManager用來在應用與Window之間的接口、窗口順序、消息等的管理
小感言
在看Window和WindowManager的時候,一直不知道要如何根據源碼去學這兩個類。結合以前自己學習的情況,覺得還是得根據里面的使用例子,再結合源碼起來學,才是最有效的。想到Activity啟動會與Window有關,就著手去找,找著找著就越有思路講。這也算是閱讀源碼的方法之一吧。
上面把DecorView添加到WindowManager,調用到的是WindowManagerGlobal.addView方法,而
該方法中真正把View傳遞給WindowManager的是通過ViewRoot的setView()方法,ViewRoot實現了View和WindowManager之間的消息傳遞。下篇文章將介紹ViewRoot與View和WindowManager之間的聯系。
上面有什么口誤或者思路不正確的話還麻煩各位讀者糾正。
Android窗口機制(四)ViewRootImpl與View和WindowManager:http://www.lxweimin.com/p/9da7bfe18374