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)について引き続き理解する


ソースコードを参照する前に、いくつかの概念を理解する必要があります.
  • DecorViewはActivityのルートビューで、FrameLayoutビューです.タイトルバーとコンテンツバーがありますが、バージョンによって異なる場合があります.
  • mParentContentは、DecorViewの直接サブViewであり、Decorのコンテンツバーである.指定されたid:androidがあります.R.id.conntent,setContentView(layoutId)を介したlayoutId対応のViewはmContentParentにマウントされている.

  • 初回呼び出し時に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などの集合を維持している.
  • mViewは、Windowに追加されたViewオブジェクトを保存します.
  • mRootsが保存しているのは、Windowごとに対応するViewRootImplオブジェクトです.
  • mDyingViewは、removeViewが呼び出されたが削除操作が完了していないWindowオブジェクトに対応するViewを保存します.
  • mParamsは、各Windowのレイアウトパラメータを保存します.
  • 
    //  
    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  
    }