ActivityのWindow作成プロセス
10546 ワード
ActivityのWindow作成プロセス
Adroidではウィンドウの概念を表し、表示可能なビューはすべてWindowにマウントされている必要があります.たとえば、Activity、Toast、Dialog、PopupWindowなどのコントロールには、ユーザーに表示されるビューを搭載したWindowがあります.Windowはタップクラスで、その直接サブクラスはPhoneWindowです.Windowは、タッチイベント、キーボードの応答イベントなどのイベントを受け入れることができる.イベント伝達メカニズムでは、イベントをまずWindowに伝達し、その後WindowからWindowにマウントされたDecorViewに伝達し、その後対応するViewに伝達する.その後、Activity#setContentView(layoutId)内部でもPhoneWindow#setContentView(layoutId)を呼び出して実現する.
1、setContentView(layoutId)からActivityにおけるwindowの作成を知る
1.1、 setContentView(layoutId)
public void setContentView(@LayoutRes int layoutResID) {
//getWindow activity Window PhoneWindow
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
1.2、Windowはどこで実例化されますか?
getWindow()はActivityに依存するWindowオブジェクトですが、どこで作成されますか?ソースコードを見ると、PhoneWindowがActivity#attach()で呼び出されていることがわかります.この方法で主に行うことは,(1)PhoneWindowオブジェクトの作成,(2)PhoneWindowオブジェクトの作成,(3)PhoneWindowオブジェクトの作成,(3)PhoneWindowオブジェクトの作成,(3)PhoneWindowオブジェクトの作成,(3)PhoneWindowオブジェクト(2)イベントリスニング、タッチイベントの受信、キーボード入力などのイベントを設定する.(3)activityのメンバー変数に値を付与する.一方,attach()メソッドはActivity Thread#performLaunchActivity()メソッドで呼び出され,ここではActivityのライフサイクルの解析に関し,ここでは議論の終わりではなく,先にスキップする.今、activityのwindowがどこで作成されたのか分かりました.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) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
mWindow = new PhoneWindow(this);//(1) PhoneWindow
mWindow.setCallback(this);// (2)
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);
}
if (info.uiOptions != 0) {
mWindow.setUiOptions(info.uiOptions);
}
// (3)activity
mUiThread = Thread.currentThread();
mMainThread = aThread;
mInstrumentation = instr;
mToken = token;
mIdent = ident;
mApplication = application;
mIntent = intent;
mReferrer = referrer;
mComponent = intent.getComponent();
mActivityInfo = info;
mTitle = title;
mParent = parent;
mEmbeddedID = id;
mLastNonConfigurationInstances = lastNonConfigurationInstances;
if (voiceInteractor != null) {
if (lastNonConfigurationInstances != null) {
mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
} else {
mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
Looper.myLooper());
}
}
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();
mCurrentConfig = config;
}
1.2.PhoneWindow setContentView(layoutId)について引き続き理解する
ソースコードを参照する前に、いくつかの概念を理解する必要があります.
public void setContentView(@LayoutRes int layoutResID) {
//getWindow activity Window PhoneWindow
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
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) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
mWindow = new PhoneWindow(this);//(1) PhoneWindow
mWindow.setCallback(this);// (2)
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);
}
if (info.uiOptions != 0) {
mWindow.setUiOptions(info.uiOptions);
}
// (3)activity
mUiThread = Thread.currentThread();
mMainThread = aThread;
mInstrumentation = instr;
mToken = token;
mIdent = ident;
mApplication = application;
mIntent = intent;
mReferrer = referrer;
mComponent = intent.getComponent();
mActivityInfo = info;
mTitle = title;
mParent = parent;
mEmbeddedID = id;
mLastNonConfigurationInstances = lastNonConfigurationInstances;
if (voiceInteractor != null) {
if (lastNonConfigurationInstances != null) {
mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
} else {
mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
Looper.myLooper());
}
}
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();
mCurrentConfig = config;
}
初回呼び出し時にmContentParentがnullであるかどうかを検出し、その値がnullである場合、generateDecor()にルートビューDecorをロードします.次にmContentParentを呼び出す.addView(view,params)は、setContentView(layoutId)対応のViewをmContentParentにマウントします.
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
// 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) {//mContentParent DecorView View
installDecor();// Activity
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
view.setLayoutParams(params);
final Scene newScene = new Scene(mContentParent, view);
transitionTo(newScene);
} else {
mContentParent.addView(view, params);// mContentParent setContentView(layoutId) View。
}
mContentParent.requestApplyInsets();
}
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor(); // Activity
...
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor); // mContentParent
...
}
}
// Decor
protected ViewGroup generateLayout(DecorView decor) {
...
View in = mLayoutInflater.inflate(layoutResource, null);
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
...
}
1.3.ActivityのonContentChanged()メソッドはActivityビューの変更を通知する
Decorの初期化が完了すると、Callback#onContentChanged()でDecorの初期化が完了することを示す.このCallbackはActivity#attach()メソッドで設定されています.ActivityではCallbackインタフェースが実装されているため、このメソッドはActivityのonContentChanged()メソッドにコールバックされますが、このメソッドは空の実装であり、ユーザーはこのメソッドを書き換えることができます.
final Callback cb = getCallback();// DecorView onContentChanged() 。
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
1.4、WindowにDecorViewを追加する
Windowが作成され、対応するDecorViewも初期化され、次はWindowManagerの役割を果たします.それはWindowにDecorViewを追加することです.このプロセスはActivityのライフサイクルのコールバックに関連しており、ここでは一旦スキップし、WindowにDecorViewを追加するソースコードに直接切り込みます.
1.4.1、 ActivityThread#handleResumeActivity
ActivityのライフサイクルのコールバックはIPCプロセスであり、Activity Thread#handleResumeActivity()はActivity#onResume()のライフサイクルを担当するコールバックである.handleResumeActivityメソッドの内部でwmを呼び出す.addView(decor, l); Activity対応のWindowにDecorViewを追加します.このプロセスは実際にはWindowManagerで実行され、各WindowはWindowManagerに対応し、WindowManagerを介してWindowのaddView、removeView、またはupdateViewLayout操作に移行します.
//#handleResumeActivity
final Activity a = r.activity;
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);// DecorView
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;
wm.addView(decor, l);// Window View
}
}
1.4.2、 Wm.addViewのプロセス
WindowManagerはインタフェースなので、addViewの操作にはサブViewが実装されていますが、サブViewは誰ですか?次のコードでは、WindowManagerの実装クラスがWindowManagerImplであることがわかります.
public final class WindowManagerImpl implements WindowManager {
}
1.4.3、 WindowManagerImpl.addViewのプロセス
WindowManagerImpl#addViewの操作は、WindowManagerGlobalのクラスに渡されて実行されます.
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mDisplay, mParentWindow);
}
1.4.4、 WindowManagerGlobal.addViewのプロセス
WindowManagerGlobalはオブジェクトを一例で提供し,内部ではmView,mRoots,mParams,mDyingViewsなどの集合を維持している.
//
public static WindowManagerGlobal getInstance() {
synchronized (WindowManagerGlobal.class) {
if (sDefaultWindowManager == null) {
sDefaultWindowManager = new WindowManagerGlobal();
}
return sDefaultWindowManager;
}
}
Window対応のViewRootImplオブジェクトをaddViewに作成します.現在のビューの情報をmViews,mRoots,mParams,mDyingViewsに保存します.最後にrootを呼び出します.setView(view, wparams, panelParentView);
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
//
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
int index = findViewLocked(view, false);
if (index >= 0) {
if (mDyingViews.contains(view)) {
// Don't wait for MSG_DIE to make it's way through root's queue.
mRoots.get(index).doDie();
} else {
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
// The previous removeView() had not completed executing. Now it has.
}
// ViewRootImpl 。
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
//
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
}
// View
root.setView(view, wparams, panelParentView);
}
1.4.5、DecorViewは本当に表示された
Window対応のビューが用意されていますが、まだ表示されていません.次に、Activity Thread#handleResumeActivityコードを見続けます.Activity#makeVisible()メソッドが呼び出されます.1.4.1、Activity Thread#handleResumeActivityではDecorViewがinvisibleに設定ため、このときDecorはViewに設定.VISIBLE.これでViewは本当に表示されます.
//ActivityThread#handleResumeActivity
r.activity.mVisibleFromServer = true;
if (r.activity.mVisibleFromClient) {
r.activity.makeVisible();
}
//Activity#makeVisible()
void makeVisible() {
if (!mWindowAdded) {// DecorView Window
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);// View
}