1. ホーム
  2. アンドロイド

Androidアプリケーションのウィンドウ(Activity)のビューオブジェクト(View)の生成過程の解析

2022-02-24 22:50:02

<スパン        前節でご存知のように、各 アクティビティ コンポーネントには、すべて関連する ウィンドウ オブジェクトで、アプリケーションウィンドウを記述します。各アプリケーションウィンドウは、その中に ビュー オブジェクトで、アプリケーションウィンドウのビューを記述します。アプリケーションウィンドウのビューは、実際には UI コンテンツとレイアウトの 活動内容 コンポーネントの UI コンテンツとレイアウトは ウィンドウ オブジェクトの内部で ビュー オブジェクトを作成します。今回は、アプリケーションウィンドウのビューの作成について詳しく分析します。

書籍「"Androidソースコード・シナリオ解析"」は、着信型プログラマーズサイト( http://0xcc0xcd.com をクリックし、お入りください。

<スパン        の前にある Androidアプリケーションウィンドウ(Activity)実装フレームワークの簡単な紹介と学習計画 前回の記事で述べたように、アプリケーションウィンドウ内に含まれるビューオブジェクトの実際の型はDecorViewです。DecorViewクラスはViewクラスを継承してコンテナ(ViewGroup)として使用され、図1のような実装になっています。


図 1 DecorView クラスの実装

        この図の正確な説明は Androidアプリケーションウィンドウ(Activity)実装フレームワークの簡単な紹介と学習計画 記事中の図5、ここでは詳細な説明は省略します。

        前回から Androidアプリケーションウィンドウ(Activity)実装フレームワークの簡単な紹介と学習計画 の記事で、アプリケーションウィンドウの各ビューオブジェクトには、関連するViewRootオブジェクトがあり、これらの関連付けは、図2に示すように、ウィンドウマネージャによって維持されていることも分かっています。


図 2 アプリケーションウィンドウビューと ViewRoot の関係図

        この図の正確な説明は Androidアプリケーションウィンドウ(Activity)実装フレームワークの簡単な紹介と学習計画 記事中の図6、ここでは詳細な説明は省略します。

        ViewRootは、簡単に言うとMVCモデルにおけるControllerに相当するもので、以下のような責務を担っています。

<スパン         1. アプリケーションウィンドウビューのSurfacesの作成を担当します。

<スパン         2. WindowManagerServiceと連携して、システムのアプリケーションウィンドウを管理します。

<スパン         3. アプリケーションウィンドウビューのUIの管理、レイアウト、レンダリングを担当する。

<スパン         では、アプリケーションウィンドウのビューオブジェクトと、それに関連するViewRootオブジェクトは、いつごろから作成されたのでしょうか。前回の Androidアプリケーションのウィンドウ(Activity)のウィンドウオブジェクト(Window)の生成過程の解析 Activityコンポーネントが起動されると、システムはそのためのWindowオブジェクトを作成し、同時にこのWindowオブジェクトのためのViewオブジェクトも作成することはご存じでしょう。一方、Activityコンポーネントが起動されたとき、そのアプリケーションウィンドウのビューオブジェクトに関連付けられたViewRootオブジェクトがまだ作成されていなければ、次にそのUIをレンダリングできるように、まずそれが作成されるのです。

       先ほどの Androidアプリ起動処理ソースコード解析 の記事では、Activityコンポーネントが初めてActivityThreadクラスのメンバ関数handleLaunchActivityを呼び出して生成・起動することが分かっているので、この関数から解析して、アプリケーションウィンドウのビューオブジェクトとそれに関連する ViewRootオブジェクトの生成過程を図3に示します。

<イグ

図3 アプリケーションウィンドウビューの作成過程

        このプロセスは13のステップに分けられますが、それぞれについて詳しく説明します。

<スパン         ステップ1.ActivityThread.handleLaunchActivityを起動する。

public final class ActivityThread {
    ......

    private final void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ......

        Activity a = performLaunchActivity(r, customIntent);

        if (a ! = null) {
            ......

            handleResumeActivity(r.token, false, r.isForward);

            ......
        }

        ......
    }

    ......
}

        この関数は、frameworks/base/core/java/android/app/ActivityThread.javaに定義されています。

        この関数では、まずActivityThreadクラスのメンバ関数performLaunchActivityを呼び出して、起動するActivityコンポーネントを生成します。Activityコンポーネントを作成したら、ActivityThreadクラスのメンバ関数handleResumeActivityを呼び出して起動することができる。

        次に、ActivityThreadクラスのメンバ関数performLaunchActivityの実装を解析して、アプリケーションウィンドウビューオブジェクトの生成処理を理解し、ActivityThreadクラスのメンバ関数handleResumeActivityに戻り、解析を継続します。その後、ActivityThreadクラスのメンバ関数handleResumeActivityの実装に戻り、アプリケーションウィンドウのビューオブジェクトに関連するViewRootオブジェクトの生成について理解します。

        ステップ2. ActivityThread.performLaunchActivityを実行する。

        この関数は、frameworks/base/core/java/android/app/ActivityThread.java で定義されています。

        このステップは Androidアプリケーションのウィンドウ(Activity)のランタイムコンテキスト(Context)を作成するプロセスの解析 記事のステップ1では、Activityコンポーネントのインスタンスを作成し、そのメンバ関数onCreateを呼び出して、いくつかのカスタム初期化作業を行うという内容です。

<スパン         ステップ3. アクティビティ.onCreate

        この関数は、frameworks/base/core/java/android/app/Activity.java で定義されています。

        このステップは Androidアプリケーションのウィンドウ(Activity)のランタイムコンテキスト(Context)を作成するプロセスの解析 Activityコンポーネントを実装するとき、つまりActivityのサブクラスを実装するときは、通常、メンバ関数onCreateをオーバーライドして、UIの初期化など、カスタマイズした初期化作業を行えるようにします。たとえば、前の Ubuntu上のハードウェアサービスのアプリケーションフレームワーク層を、Android上の組み込みJavaアプリケーションでテストする。 の記事では、ハードウェアサービスをテストするためにHelloというActivityコンポーネントを実装し、そのメンバ関数onCreateは次のようになります。

public class Hello extends Activity implements OnClickListener {  
    ......  
      
    /* Called when the activity is first created. */  
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
  
        ......  
    }  

    ......
}


       親アクティビティクラスから継承したメンバ関数setContentViewの呼び出しにより、アプリケーションウィンドウビューオブジェクトを生成しています。

       次に、Activityクラスのメンバ関数setContentViewの実装の解析に移ります。

       ステップ4.アクティビティ.setContentView

public class Activity extends ContextThemeWrapper
        implements LayoutInflater,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks {
    ......

    private Window mWindow;
    ......

    public Window getWindow() {
        return mWindow;
    }
    ......

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

    ......
}

        この関数は、frameworks/base/core/java/android/app/Activity.java で定義されています。

        Activityクラスのメンバ関数setContentViewは、まず別のメンバ関数getWindowを呼び出してメンバ変数mWindowで記述されたウィンドウオブジェクトを取得し、このウィンドウオブジェクトのメンバ関数setContentViewを呼び出してアプリケーションウィンドウビューオブジェクトを生成する仕事を行います。

        前回の Androidアプリケーションのウィンドウ(Activity)のウィンドウオブジェクト(Window)の生成過程の解析 Activityクラスのメンバ変数mWindowがPhoneWindowオブジェクトを指していることがわかったので、PhoneWindowクラスのメンバ関数setContentViewの実装の解析に移ります。

        ステップ5. PhoneWindow.setContentView

public class PhoneWindow extends Window implements MenuBuilder.Callback {
    ......

    // This is the view in which the window contents are placed.
    // mDecor itself, or a child of mDecor where the contents go.
    private ViewGroup mContentParent;
    ......

    @Override
    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            installDecor();
        } else {
            mContentParent.removeAllViews();
        }
        mLayoutInflater.inflate(layoutResID, mContentParent);
        final Callback cb = getCallback();
        if (cb ! = null) {
            cb.onContentChanged();
        }
    }

    ......
}

        この関数は、frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.javaに定義されています。

        PhoneWindowクラスのメンバ変数mContentParentは、UIコンテナとして使用されるDecorView型のビューオブジェクト、またはこのDecorView型のビューオブジェクトの子ビューオブジェクトを記述するために使用されるものです。値がnullの場合、処理中のアプリケーションウィンドウのビューオブジェクトがまだ作成されていないことを意味し、値がnullの場合、処理中のアプリケーションウィンドウのビューオブジェクトがまだ作成されていないことを意味します。この場合、メンバ関数installDecorが呼び出され、アプリケーションウィンドウのビューオブジェクトが生成される。それ以外の場合は、アプリケーションウィンドウのビューがリセットされることを意味します。リセットする前に、メンバー変数mContentParentによって記述されるViewGroupオブジェクトを最初に呼び出すことによって、元のUI内部空間が取り除かれます。

        次に、メンバ関数installDecorを呼び出してアプリケーションウィンドウのビューオブジェクトを作成し、次にPhoneWindowクラスのメンバ変数mLayoutInflaterで記述されたLayoutInflaterオブジェクトのメンバ関数inflateを呼び出してパラメータlayoutResIDで記述したUIレイアウトを先に作成したアプリケーションウィンドウのビューに設定し、最後にCallbackインターフェースを呼び出してビューのコンテンツが変わったことを対応するアクティビティコンポーネントに通知しています。先ほどの Androidアプリケーションウィンドウ(Activity)ウィンドウオブジェクト(Window)作成プロセス解析 の記事では、Activityコンポーネントが自らCallbackインターフェースを実装し、関連するアプリケーションウィンドウオブジェクトの内部に設定しているため、実際に呼ばれるのはActivityクラスのメンバ関数onContentChangedで、ビュー内容の変更通知が送信されます。

      次に、PhoneWindow クラスのメンバ関数 installDecor の実装を分析し、アプリケーション ウィンドウ ビュー オブジェクトの作成プロセスを引き続き理解することができるようにします。

      ステップ6. PhoneWindow.installDecorをインストールする

public class PhoneWindow extends Window implements MenuBuilder.Callback {
    ......

    // This is the top-level view of the window, containing the window decor.
    private DecorView mDecor;
    ......

    // This is the view in which the window contents are placed.
    // mDecor itself, or a child of mDecor where the contents go.
    private ViewGroup mContentParent;
    ......

    private TextView mTitleView;
    ......

    private CharSequence mTitle = null;
    ......

    private void installDecor() {
        if (mDecor == null) {
            mDecor = generateDecor();
            ......
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);

            mTitleView = (TextView)findViewById(com.android.internal.R.id.title);
            if (mTitleView ! = null) {
                if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) ! = 0) {
                    View titleContainer = findViewById(com.android.internal.R.id.title_container);
                    if (titleContainer ! = null) {
                        titleContainer.setVisibility(View.GONE);
                    } else {
                        mTitleView.setVisibility(View.GONE);
                    }
                    if (mContentParent instanceof FrameLayout) {
                        ((FrameLayout)mContentParent).setForeground(null);
                    }
                } else {
                    mTitleView.setText(mTitle);
                }
            }
        }
    }

    ......
}


        この関数は、frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.javaに定義されています。

        Activityコンポーネントの起動時にアプリケーションウィンドウビューを作成するので、この時点ではPhoneWindowクラスのメンバ変数mDecorの値もnullに等しいと仮定します。 generateDecorでDecorViewオブジェクトを作成し、PhoneWindowクラスのメンバ変数mDecorに保存します。

        PhoneWindowクラスのメンバー関数installDecorは、次に別のメンバー関数generateLayoutを呼び出し、現在のアプリケーションウィンドウのFeatureに基づいて、対応するウィンドウレイアウトファイルをロードします。これらのレイアウトファイルは、frameworks/base/core/res/layoutディレクトリに保存され、idが"content"のレイアウトコントロールが含まれていなければなりません。PhoneWindowクラスのメンバ関数generateLayoutの後、id "content"を持つViewGroupコントロールは、ウィンドウのUIコンテナとして使用されるようになります。PhoneWindowクラスのメンバ関数installDecorで、後者はメンバ変数mContentParentに保存されます。

       PhoneWindowクラスのメンバー関数installDecorは、以前にロードされたウィンドウレイアウトファイルに、idが"title"のTextViewコントロールが含まれているかどうかもチェックします。含まれている場合、それは PhoneWindow クラスのメンバー変数 mTitleView に格納され、現在のアプリケーションウィンドウのタイトルバーが記述されます。しかし、現在のアプリケーション・ウィンドウにタイトル・バーがない場合、つまり、そのFeatureビットFEATURE_NO_TITLEの値が1に等しい場合、PhoneWindowクラスのメンバー関数installDecorは、以前に取得したタイトル・バーを非表示にする必要があるのです。PhoneWindowクラスのメンバ変数mTitleViewによって記述されるタイトルバーは、id値 "title_container"を持つコンテナに含まれることがあり、この場合、タイトルバーコンテナを隠す必要があることに注意してください。一方、現在のアプリケーションウィンドウにタイトルバーがある場合、PhoneWindow クラスのメンバ関数 installDecor は、そのタイトルバーのテキストを設定します。アプリケーションウィンドウのタイトルバーテキストは、PhoneWindowクラスのメンバ変数mTitleに格納され、PhoneWindowクラスのメンバ関数setTitleを呼び出して設定することができます。

       このステップが完了すると、アプリケーションウィンドウビューが作成され、前のステップ1、つまりActivityThreadクラスのメンバ関数handleLaunchActivityに戻って、次はActivityThreadクラスの別のメンバ関数handleResumeActivityを呼び出し、起動中のActivityコンポーネントをアクティベートします。Activityコンポーネントの起動は初めてなので、Activityコンポーネント用にViewRootオブジェクトを作成し、先に作成したアプリケーションウィンドウビューと関連付け、後でViewRootオブジェクトからアプリケーションウィンドウビューのUI表示を制御できるようにします。

       次に、ActivityThreadクラスのメンバ関数handleResumeActivityの実装の解析に移ります。

       ステップ7.activityThread.handleResumeActivity

public final class ActivityThread {
    ......

    final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {
        ......

        ActivityClientRecord r = performResumeActivity(token, clearHide);

        if (r ! = null) {
            final Activity a = r.activity;
            ......

            // If the window hasn't yet been added to the window manager,
            // and this guy didn't finish itself or start another activity,
            // then go ahead and add the window.
            boolean willBeVisible = !a.mStartedActivity;
            if (!willBeVisible) {
                try {
                    willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
                            a.getActivityToken());
                } catch (RemoteException e) {
                }
            }
            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;
                ......
                if (a.mVisibleFromClient) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);
                }
            } 

            ......
        }

        ......
    }
  
    ......
}

        この関数は、frameworks/base/core/java/android/app/ActivityThread.javaで定義されています。

        ActivityThreadクラスのメンバ関数handleResumeActivityは、まず別のメンバ関数performResumeActivityを呼び出してActivityコンポーネントに起動することを通知し、Activityコンポーネントのメンバ関数onResumeが呼び出されるようにする。ActivityThreadクラスのperformResumeActivity関数の戻り値はActivityClientRecordオブジェクトrであり、ActivityClientRecordオブジェクトのメンバー変数activityには、Activityコンポーネントのうち、Activateされているものが記述されている。

        ActivityThreadクラスの関数handleResumeActivityは、アクティブ化されているActivityコンポーネントが次に見えるかどうかを判定します。ActivityクラスのメンバであるmStartedActivityは、Activityコンポーネントが新しいActivityコンポーネントを開始し、その新しいActivityコンポーネントの実行結果を待っているかどうかを記述しています。この場合、Activityコンポーネントのメンバ変数mStartedActivityの値はtrueになり、新しいActivityコンポーネントの実行結果が返されるまで、現在のActivityコンポーネントは不可視のままであることが示される。したがって、Activityコンポーネントaのメンバ変数mStartedActivityの値がtrueに等しい場合、次の不可視状態になり、そうでない場合は可視状態となります。

        Activityコンポーネントaのメンバ変数mStartedActivityの値がtrueに等しい場合、その後の状態は不可視のままとされていますが、起動したActivityコンポーネントのUIが全画面でない可能性もあります。この場合、ActivityコンポーネントaのUIはまだ部分的に見えており、変数willBeVisibleの値もtrueに設定されている。したがって、変数willBeVisibleの値がfalseに等しければ、ActivityThreadクラスのメンバー関数handleResumeActivityはBinderプロセス間通信機構を通じてActivityManagerServiceメンバー関数willActivityBeVisibleを呼び、(実行結果を待っているActivityコンポーネントaが入った)Activityコンポーネントaの上にある他のActivityコンポーネントはフルスクリーンかどうかチェックすることになる。そうでない場合、ActivityManagerServiceのメンバ関数willActivityBeVisibleの戻り値はtrueとなり、Activityコンポーネントaを次に表示する必要があることを示す。

       先ほど取得したActivityClientRecordオブジェクトrには、現在アクティブなActivityコンポーネントaに関連するアプリケーションウィンドウオブジェクトを記述するメンバー変数windowがあります。その値がnullであれば、現在アクティブなActivityコンポーネントaに関連するアプリケーションウィンドウオブジェクトにViewRootオブジェクトが関連づけられていないことを意味します。その値がNULLに等しい場合、現在アクティブなActivityコンポーネントaに関連するアプリケーションウィンドウオブジェクトは、まだそれに関連するViewRootオブジェクトを持っていないことを意味する。さらに、アクティブなActivityコンポーネントaが生きていて次に見える場合、すなわち、ActivityClientRecordオブジェクトrのメンバー変数mFinishedの値がfalseに等しく、先に取得した変数willBeVisibleの値が真に等しい場合、ViewRootオブジェクトを Activityコンポーネントaが関連しているアプリケーションウィンドウビューオブジェクトに関連するA ViewRootオブジェクトに付加する時期であることを示す。

       ActivityコンポーネントのアプリケーションウィンドウのビューオブジェクトとViewRootオブジェクトの関連付けは、Activityコンポーネントが使用するウィンドウマネージャを介して行われます。前回の Androidアプリケーションウィンドウ(Activity)のWindowオブジェクトの生成に関する分析 Activityコンポーネントが使用するローカルウィンドウマネージャは、そのメンバ変数mWindowManagerに格納されており、Activityクラスのメンバ関数getWindowManagerで取得できることはご存じでしょう。次のステップ10では、Activityクラスのメンバ関数getWindowManagerの実装を解析します。

       ここで、アクティビティコンポーネントaのアプリケーションウィンドウビューオブジェクトにViewRootオブジェクトを関連付けたいので、まず、このアプリケーションウィンドウビューオブジェクトを取得する必要があります。上記の手順6で分かるように、アクティビティコンポーネントのアプリケーションウィンドウビューオブジェクトは、関連付けられたアプリケーションウィンドウオブジェクトの中に格納されているので、まず、そのアプリケーションウィンドウオブジェクトを再度取得する必要があります。Activityコンポーネントに関連付けられたアプリケーションウィンドウオブジェクトは、Activityコンポーネントのメンバ関数getWindowを呼び出すことで取得することができます。アプリケーションウィンドウオブジェクト(PhoneWindow型)を取得したら、そのメンバ関数getDecorViewを呼び出して、内部ビューオブジェクトを取得することができます。Step8ではActivityクラスのメンバ関数getWindow、Step9ではPhoneWindowクラスのメンバ関数getDecorViewの実装をそれぞれ解析していきます。

      アプリケーションウィンドウのビューオブジェクトを ViewRoot オブジェクトに関連付ける際、第3のパラメータとしてアプリケーションウィンドウのレイアウトパラメータがあります。これは WindowManager.LayoutParams 型のオブジェクトで、アプリケーションウィンドウのメンバー関数 getAttributes を呼び出すことによって取得できます。つまり、現在アクティブなアクティビティ コンポーネント a がローカル プロセスで表示されているかどうか、つまり、そのメンバー変数 mVisibleFromClient の値が true に等しいかどうかを判断する必要があるのです。もし可視であれば、最後にローカルウィンドウマネージャwmのメンバ(型 可視であれば、最後に先に取得したローカルウィンドウマネージャwmのメンバ関数addView(型LocalWindowManager)を呼び出して、アプリケーションウィンドウのビューオブジェクトをViewRootオブジェクトに関連付ける処理を実行できるようになります。

     次に、ActivityクラスのメンバであるLocalWindowManagerクラスのgetWindow、getDecorView、getWindowManager、addViewの実装を解析してみましょう。

     ステップ8. activity.getWindow

public class Activity extends ContextThemeWrapper (パブリッククラス アクティビティ エクステンション コンテキストテーマラッパー)
        は、LayoutInflaterを実装しています。
        Window.Callback、KeyEvent.Callback、KeyEvent.Callback。
        OnCreateContextMenuListener, ComponentCallbacks {...
    ......

    private Window mWindow;
    ......

    パブリック ウィンドウ getWindow() {
        mWindowを返します。
    }

    ......
}

        この関数は、frameworks/base/core/java/android/app/Activity.javaに定義されています。

        前回から Androidアプリケーションのウィンドウ(Activity)のウィンドウオブジェクト(Window)の生成過程の解析 Activityクラスのメンバ変数mWindowはPhoneWindow型のウィンドウオブジェクトを指すので、Activityクラスのメンバ関数getWindowは呼び出し元にPhoneWindowオブジェクトを返すことがわかります。

        この後、ステップ7のActivityThreadクラスのメンバ関数handleResumeActivityに戻り、引き続き先ほど取得したPhoneWindowオブジェクトのメンバ関数getDecorViewを呼び出して、現在アクティブになっているActivityコンポーネントを取得するのですが、その際に、PhoneWindowオブジェクトのメンバ関数getDecorViewを呼び出します。

        ステップ9. PhoneWindow.getDecorView

public class PhoneWindow extends Window implements MenuBuilder.Callback {
    ......

    private DecorView mDecor;
    ......

    @Override
    public final View getDecorView() {
        if (mDecor == null) {
            installDecor();
        }
        return mDecor;
    }

    ......
}

        この関数は、frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.javaに定義されています。

        PhoneWindowクラスのメンバ関数getDecorViewは、まず、メンバ変数mDecorの値がnullであるかどうかを判定します。nullであれば、処理中のアプリケーションウィンドウに対して、まだビューオブジェクトが作成されていないことになります。そして、別のメンバ関数 installDecor が呼び出され、ビューオブジェクトが生成されます。前の呼び出しから、現在のアプリケーションウィンドウは既にビューオブジェクトを作成していることが分かっているので、ここでのメンバ変数mDecorの値はnullに等しくなく、PhoneWindowクラスのメンバ関数getDecorViewは呼び出し元に直接それを返します。

        このステップが完了したら、ステップ7のActivityThreadクラスのメンバ関数handleResumeActivityに戻り、現在アクティブなActivityコンポーネントのメンバ関数getWindowManagerを呼び出してローカルのウィンドウマネージャを取得するように処理を進めます。

        ステップ10. アクティビティ.getWindowManager

public class Activity extends ContextThemeWrapper (パブリッククラス アクティビティ エクステンション コンテキストテーマラッパー)
        は、LayoutInflaterを実装しています。
        Window.Callback、KeyEvent.Callback、KeyEvent.Callback。
        OnCreateContextMenuListener, ComponentCallbacks {...
    ......

    private WindowManager mWindowManager;
    ......

    public WindowManager getWindowManager() { (英語)
        return mWindowManager;
    

    ......
}

        この関数は、frameworks/base/core/java/android/app/Activity.javaに定義されています。

        前回から Androidアプリケーションのウィンドウ(Activity)のランタイムコンテキスト(Context)作成過程の解析 Activityクラスのメンバ変数mWindowManagerはLocalWindowManager型のローカルウィンドウ・マネージャを指し、Activityクラスのメンバ関数getWindowManagerはそれを呼び出し元に直接返すことが分かっていますね。

        このステップが完了したら、ステップ7のActivityThreadクラスのメンバ関数handleResumeActivityに戻り、先ほど取得したLocalWindowManagerオブジェクトのメンバ関数addViewを呼び出して、現在アクティブなActivityコンポーネントにアプリケーションウィンドウビューオブジェクトを関連付ける処理を行い、ViewRootオブジェクトをアプリケーションウィンドウビューオブジェクトに関連付けています。

        ステップ11. localWindowManager.addView

public abstract class Window {
    ......

    private class LocalWindowManager implements WindowManager { (ローカル ウィンドウ マネージャ)
        ......

        public final void addView(View view, ViewGroup.LayoutParams params) { {
            ......

            mWindowManager.addView(view, params)を実行します。
        

        ......

        private final WindowManager mWindowManager;
 
        ......
    }

    ......
}


        この関数は、frameworks/base/core/java/android/view/Window.javaファイルで定義されています。

        前回から Androidアプリケーションのウィンドウ(Activity)のウィンドウオブジェクト(Window)の生成過程の解析 の記事で、LocalWindowManagerクラスのメンバ変数mWindowManagerはWindowManagerImplオブジェクトを指すことが分かっているので、次にLocalWindowManagerクラスのメンバ関数addViewはWindowManagerImplクラスのメンバ関数addViewを呼び、パラメータ viewで記述したアプリケーション ウィンドウ ビュー オブジェクトにViewRootオブジェクトを関連付けることが分かります。

        ステップ12. WindowManagerImpl.addView

public class WindowManagerImpl implements WindowManager { (パブリッククラス WindowManager インプリメント ウィンドウマネージャ)
    ......

    public void addView(View view, ViewGroup.LayoutParams params)
    {
        addView(view, params, false);
    



    ......

    private void addView(View view, ViewGroup.LayoutParams params, boolean nest)
    {
        ......

        final WindowManager.LayoutParams wparams
                = (WindowManager.LayoutParams)params;

        ViewRoot ルート
        View panelParentView = null;

        synchronized (this) {
            // ここで奇妙な/疑問のあるケースがあります。
            // ビューを複数回表示する場合、単純にネスト数を増やすだけです。
            // となり、それに対応する回数だけビューを削除する必要があります。
            実際にウィンドウ マネージャから削除されるには、//回数が必要です。
            // これは特に通知マネージャのために有用です。
            // を追加・削除し続けることができます。
            // 通知が更新される。
            int index = findViewLocked(view, false);
            if (index >= 0) { {...
                if (!nest) {
                    throw new IllegalStateException("View " + view)
                            + はすでにウィンドウ マネージャに追加されています(")。
                

                root = mRoots[index];
                root.mAddNesting++;
                // レイアウトパラメータを更新します。
                view.setLayoutParams(wparams)を実行します。
                root.setLayoutParams(wparams, true);
                を返します。
            }

            // これがパネルウィンドウである場合、そのウィンドウを検索します。
            // 今後の参考のため、添付しておきます。
            If (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {。
                final int count = mViews ! = null ? mViews.length : 0;
                for (int i=0; i<count;i++){。
                    if (mRoots[i].mWindow.asBinder() == wparams.token){。
                        panelParentView = mViews[i]。
                    

                }
            }

            root = new ViewRoot(view.getContext());
            root.mAddNesting = 1;

            view.setLayoutParams(wparams)。

            if (mViews == null) {
                index = 1;
                mViews = new View[1];
                mRoots = 新しいViewRoot[1];
                mParams = new WindowManager.LayoutParams[1]。
            } else {
                index = mViews.length + 1;
                オブジェクト[] old = mViews;
                mViews = new View[index];
                System.arraycopy(old, 0, mViews, 0, index-1);
                old = mRoots;
                mRoots = 新しいViewRoot[index]。
                System.arraycopy(old, 0, mRoots, 0, index-1);
                old = mParams;
                mParams = new WindowManager.LayoutParams[index]です。
                System.arraycopy(old, 0, mParams, 0, index-1);
            

            index--;

            mViews[index] = view;
            mRoots[index] = root;
            mParams[index] = wparams;
        

        // これは最後に行う。これは、何かを始めるためのメッセージを発するためである。
        root.setView(view, wparams, panelParentView);
    }

    ......

    private View[] mViews;
    private ViewRoot[] mRoots;
    private WindowManager.LayoutParams[] mParams;

    ......









       この関数は、frameworks/base/core/java/android/view/WindowManagerImpl.javaに定義されています。

       WindowManagerImplクラスでは、2引数版のメンバ関数addViewを3引数版の同関数addViewを呼び出して実装しているので、次は3引数版のメンバ関数addViewの実装を中心に解析していきます。

       WindowManagerImplクラスの3パラメータ版addViewメンバ関数の実装を分析する前に、まずWindowManagerImplクラスがアプリケーションウィンドウのビューオブジェクト(Viewオブジェクト)とViewRootオブジェクトを関連付ける方法を理解しましょう。ViewオブジェクトはViewRootオブジェクトと関連付けられると同時に、WindowManagerとも関連付けられます。

       WindowManagerImplクラスは3つのメンバ変数mViews、mRoots、mParamsを持ち、これらはView、ViewRoot、WindowManager型の配列になっています。これら 3 つの配列のサイズは常に等しくなっています。このようにして、関連するViewオブジェクト、ViewRootオブジェクト、WindowManager.LayoutParamsオブジェクトはそれぞれ配列mViews、mRoots、mParamsの同じ場所に格納され、つまり、mViews[i], mRoots[i], mParams[i] で記述するViewオブジェクト、ViewRootオブジェクト、mParamsは常に同じサイズになるように設定されています。LayoutParams オブジェクトは、View オブジェクト、ViewRoot オブジェクト、WindowManager に関連しています。したがって、WindowManagerImplクラスのメンバ関数addViewの3パラメータ版は、配列mViews、mRoots、mParamsの同じ位置に配置することによって、Viewオブジェクト、ViewRootオブジェクト、WindowManager.LayoutParamsオブジェクトを関連づけます。

       Viewオブジェクト、ViewRootオブジェクト、WindowManager.LayoutParamsオブジェクトの関係を理解すると、WindowManagerImplクラスのメンバ関数addViewの3パラメータ版の実装は理解しやすくなります。

       パラメータ view とパラメータ params は、関連付ける View オブジェクトと WindowManager.LayoutParams オブジェクトを記述します。addViewメンバ関数は、まず、別のメンバ関数findViewLockedを呼び出し、パラメータviewで記述されたViewオブジェクトがすでにmViewsの配列に存在するかどうかをチェックします。もし既に存在していれば、そのViewオブジェクトは既にViewRootオブジェクトとWindowManager.LayoutParamsオブジェクトに関連付けされていることを意味します。この場合、パラメータ nest の値が false に等しければ、メンバ関数 addView はパラメータ view で記述された View オブジェクトの再関連付けを繰り返し行うことはできません。一方、パラメータネストの値がtrueに等しい場合、addViewメンバ関数はパラメータビューによって記述されたViewオブジェクトとそれが関連付けられているViewRootオブジェクトを変更する、つまり、WindowManager.Viewにそれを更新するだけで、ViewRootオブジェクトを変更することはできません。LayoutParamsオブジェクトに更新します。これはそれらのメンバ関数setLayoutParamsを呼び出すことによって行われます。

       パラメータ view で記述された View オブジェクトがまだ ViewRoot オブジェクトと関連付けられていない場合、メンバ関数 addView は ViewRoot オブジェクトを生成し、パラメータ view と params で記述された View オブジェクト、および WindowManager.View オブジェクトと共に配列 mViews に格納し、ViewRoot オブジェクトを生成します。LayoutParamsオブジェクトは配列mViews、mRoots、mParamsと同じ場所に格納されます。もし配列mViews、mRoots、mParamsがまだ作成されていない場合、addViewメンバ関数は最初にそれらのそれぞれに対してサイズ1の配列を作成し、関連するViewオブジェクト、ViewRootオブジェクト、WindowManager.LayoutParamsオブジェクトをそれぞれ保持するために使用できるようにすることに注意して下さい。一方、配列 mViews、mRoots、mParams が既に作成されている場合、addView メンバ関数はそれらのサイズを 1 増やして、関連する View オブジェクト、ViewRoot オブジェクト、および WindowManager.LayoutParams オブジェクトを保持するために使用できるようにする必要があります。LayoutParamsオブジェクトを保持するために使用されます。

       もう1つの注意点は、パラメータ view が子アプリケーションウィンドウのビューオブジェクトを記述している場合、つまり WindowManager.LayoutParams オブジェクト wparams のメンバ変数 type の値が WindowManager.LayoutParams.FIRST_SUB_WINDOW 以上かつ WindowManager.LayoutParams.Window_SUB_WINDOW 以下である場合、そのウィンドウは子アプリケーションウィンドウとして扱われることです。WINDOWより大きく、WindowManager.LayoutParams.LAST_SUB_WINDOW以下であれば、メンバー関数 addViewはこの子ビュー オブジェクトの親パネルParentViewを見つける必要があり、これは配列mRootsを通して反復することで見つかります。まず、WindowManager.LayoutParamsオブジェクトwparamsのメンバ変数tokenは、W型のBinderローカルオブジェクトのIBinderインタフェースを指し、これはパラメータviewで記述した子アプリケーションウィンドウビューオブジェクトが属する親アプリケーションウィンドウビューオブジェクトを記述するために使用されています。第二に、各ViewRootオブジェクトは、そのメンバ変数mWindowを通してW型のBinderローカルオブジェクトを保持しているので、配列mRootsにViewRootオブジェクトがあり、そのメンバ変数mWindowで記述されるWオブジェクトのIBinderインターフェースがWindowManager.LayoutParamsオブジェクト wparamsと等しければ、そのViewRootオブジェクトに関連するViewオブジェクトは、そのパラメータのビューの親のアプリケーションウィンドウビューオブジェクトとなる。

        メンバ関数 addView は ViewRoot オブジェクト ルートにパラメータ view で記述された View オブジェクトとパラメータ params で記述された WindowManager.LayoutParams オブジェクトを関連付けた後、最後に View をビュー画像と WindowManager.LayoutParams オブジェクトに関連付けます。LayoutParamsオブジェクトと、変数panelParentViewによって記述された親アプリケーションウィンドウのビューオブジェクトをViewRootオブジェクトルート内に保存し、これはViewRootオブジェクトルートのメンバー関数setViewを呼び出すことによって実現されます、したがって、我々は分析を続けます。 それでは、ViewRootクラスのメンバー関数、setViewの実装へと進みましょう。

        ステップ13. viewRoot.setView

public final class ViewRoot extends Handler implements ViewParent,
        View.AttachInfo.Callbacks{(ビューアタッチインフォコールバック)
    ......

    final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();
    ......

    ビュー mView;
    ......

    final View.AttachInfo mAttachInfo;
    ......

    boolean mAdded;
    ......

    public void setView(View view, WindowManager.LayoutParams attrs,
            ビューパネルペアレントビュー) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                mWindowAttributes.copyFrom(attrs)を実行します。
                ......

                mAttachInfo.mRootView = view;
                .......

                if (panelParentView ! = null) { ....
                    mAttachInfo.mPanelParentWindowToken
                            = panelParentView.getApplicationWindowToken()。
                }
                mAdded = trueです。
                ......

                requestLayout();
                ......
                トライ {
                    res = sWindowSession.add(mWindow, mWindowAttributes,
                            getHostVisibility(), mAttachInfo.mContentInsets,
                            mInputChannel)。
                } catch (リモート例外 e) {
                    mAdded = false とする。
                    mView = null。
                    ......
                    throw new RuntimeException("Adding window failed", e).をスローします。
                } 最後に {
                    if (restore) {
                        attrs.restore()を実行します。
                    }
                

                ......
            }

            ......
        }
    

    ......
}

        この関数は、frameworks/base/core/java/android/view/ViewRoot.javaファイルで定義されています。

        パラメータ view で記述された View オブジェクトが ViewRoot クラスのメンバ変数 mView に、メンバ変数 mAttachInfo で記述された AttachInfo のメンバ変数 mRootView に、それぞれ WindowManager のコンテンツが格納されます。パラメータattrsで記述されたLayoutParamsオブジェクトの内容は、ViewRootクラスのメンバ変数mWindowAttributesにコピーされます。

       パラメータ panelParentView の値が null でない場合、パラメータ view が子アプリケーションウィンドウビューオブジェクトを記述していることを意味します。この場合、パラメータpanelParentViewは、親アプリケーションウィンドウビューオブジェクトを記述しています。このとき、親アプリケーションウィンドウビューオブジェクトを記述するために使用されるW型のBinderネイティブオブジェクトのIBinderインタフェースを取得し、ViewRootクラスのメンバ変数mAttachInfo mPanelParentWindowTokenで表されるAttachInfoメンバ変数に格納できるようにする必要があります。こうすることで、子アプリケーションウィンドウビューが、ViewRootクラスのメンバ変数mViewで記述されたどの親アプリケーションウィンドウビューに属しているかを、後で知ることができます。なお、対応するWオブジェクトのIBinderインタフェースは、パラメータpanelParentViewで記述されたViewオブジェクトのgetApplicationWindowToken関数を呼び出すことで取得できます。

       上記の操作が完了すると、ViewRoot クラスのメンバ関数 setView は、メンバ変数 mAdded の値を true に設定し、ViewRoot オブジェクトが現在処理中で、それに View オブジェクトが関連付けられていることを示します。次に、ViewRoot クラスのメンバ関数 setView は、さらに 2 つの処理を行います。

       1. ViewRoot クラスの別のメンバ関数である requestLayout を呼び出し、アプリケーションウィンドウビューの UI の最初のレイアウトを要求します。

       2. ViewRoot クラスの静的メンバ変数 sWindowSession で記述される Session 型の Binder プロキシオブジェクトのメンバ関数 add を呼び出し、 WindowManagerService に WindowState オブジェクトを追加して、現在処理中のアプリケーションウィンドウに 関連付けられた ViewRoot の現在の処理を記述できるように要求します。

       ここまでで、Androidアプリのウィンドウビューオブジェクトの作成過程の解析は終了です。次回は、Android アプリ ウィンドウを WindowManagerService サービスに接続する処理、つまり、Android アプリ ウィンドウが WindowManagerService に WindowState オブジェクトの追加を要求する処理について、さらに次回は、Android アプリ ウィンドウの描画に使用する Surface の作成処理、Android アプリ ウィンドウの描画処理について分析する予定です。この3つのプロセスの分析を通じて、上記1、2で述べた2つの関数の実行プロセスが見えてきますので、ご期待ください。

<スパン ラオ・ルオの新浪微博。 http://weibo.com/shengyangluo とフォローを歓迎します