Dialog 関連のソース コードを開始するためにアクティビティ コンテキストを使用する必要があるのはなぜですか? Qianlima は、ハイテクの観点から問題を考察します。

背景質問のエクスポート

ファン、友人の皆さん、
こんにちは!最近、別の学生が質問をしました。この質問はアプリケーションの学生が遭遇したはずです。質問は次のとおりです: ダイアログを
作成するときに、グローバル アプリケーションのコンテキストではなく、アクティビティのコンテキストの使用を強調する必要があるのはなぜですか? これを詳しく分析してもらえますか?
実際、これは本当に良い質問で、ほとんどの学生は上記の結論だけを知っていて丸暗記していると思いますが、チョリマの WMS/AMS 特別コースを受講した後は、この質問は難しくないはずです(笑)。
Chollima の wms/ams 特別コースはここをクリックしてください。
生徒が必要な場合は、+ Wei: androidframework007
ここに画像の説明を挿入します

このダイアログの分析では、次のプロセスに従って分析します。

ここに画像の説明を挿入します

wms レベルでの Dumpsys 分析ダイアログ

すでに WMS コースに精通している学生の場合、このタイプのウィンドウ調査では、まず dumpsys 関連コマンドを使用して、このダイアログの状況を確認する必要があります。以下では、分析の例としてギャラリー インターフェイスを使用します。
ここに画像の説明を挿入します

ここにはダイアログがあるので、dumpsys アクティビティ コンテナーを通じて出力された階層構造を確認してください。

 #3 Task=462 type=standard mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
         #0 ActivityRecord{265c566 u0 com.android.gallery3d/.app.GalleryActivity} t462} type=standard mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
          #1 372dff com.android.gallery3d/com.android.gallery3d.app.GalleryActivity type=standard mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
          #0 d924826 com.android.gallery3d/com.android.gallery3d.app.GalleryActivity type=standard mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]

ギャラリーの ActivityRecord の下に 2 つの WindowState がマウントされていることは明らかなので、
ここでは基本的に、Dialog は実際には独自の独立した WindowState を持っているが、ActivityRecord の下のノードに属しており、Dialog の WidnowState が Activity 自体の WindowState をカバーしていると判断できます。 。
上記の情報では十分ではありません。引き続き dumpsys ウィンドウを実行して、ウィンドウの状況を確認します。

//Dialog的WindowState
Window #10 Window{372dff u0 com.android.gallery3d/com.android.gallery3d.app.GalleryActivity}:
    mDisplayId=0 rootTaskId=462 mSession=Session{
    
    812e805 1834:u0a10083} mClient=android.os.BinderProxy@8bce11e
    mOwnerUid=10083 showForAllUsers=false package=com.android.gallery3d appop=NONE
    mAttrs={
    
    (0,0)(wrapxwrap) gr=CENTER sim={
    
    adjust=pan forwardNavigation} ty=APPLICATION fmt=TRANSPARENT wanim=0x10302fb
      fl=DIM_BEHIND ALT_FOCUSABLE_IM SPLIT_TOUCH HARDWARE_ACCELERATED
      pfl=USE_BLAST INSET_PARENT_FRAME_BY_IME
      bhv=DEFAULT
      fitTypes=STATUS_BARS NAVIGATION_BARS CAPTION_BAR}
    Requested w=1366 h=443 mLayoutSeq=260
    mBaseLayer=21000 mSubLayer=0    mToken=ActivityRecord{
    
    265c566 u0 com.android.gallery3d/.app.GalleryActivity} t462}
    mActivityRecord=ActivityRecord{
    
    265c566 u0 com.android.gallery3d/.app.GalleryActivity} t462}
    mAppDied=false    drawnStateEvaluated=true    mightAffectAllDrawn=true
    mViewVisibility=0x0 mHaveFrame=true mObscured=false
    mGivenContentInsets=[0,0][0,0] mGivenVisibleInsets=[0,0][0,0]
    mFullConfiguration={
    
    1.0 310mcc260mnc [en_US] ldltr sw411dp w411dp h797dp 560dpi nrml long port finger qwerty/v/v dpad/v winConfig={
    
     mBounds=Rect(0, 0 - 1440, 2960) mAppBounds=Rect(0, 0 - 1440, 2876) mMaxBounds=Rect(0, 0 - 1440, 2960) mDisplayRotation=ROTATION_0 mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_0} s.1 fontWeightAdjustment=0}


//Activity的WindowState
  Window #12 Window{d924826 u0 com.android.gallery3d/com.android.gallery3d.app.GalleryActivity}:
    mDisplayId=0 rootTaskId=462 mSession=Session{
    
    812e805 1834:u0a10083} mClient=android.os.BinderProxy@6deff81
    mOwnerUid=10083 showForAllUsers=false package=com.android.gallery3d appop=NONE
    mAttrs={
    
    (0,0)(fillxfill) sim={
    
    adjust=pan forwardNavigation} ty=BASE_APPLICATION fmt=TRANSLUCENT wanim=0x10302fa
      fl=LAYOUT_IN_SCREEN LAYOUT_INSET_DECOR SPLIT_TOUCH HARDWARE_ACCELERATED
      pfl=NO_MOVE_ANIMATION FORCE_DRAW_STATUS_BAR_BACKGROUND USE_BLAST FIT_INSETS_CONTROLLED
      bhv=DEFAULT
      fitSides=}
    Requested w=1440 h=2960 mLayoutSeq=260
    mBaseLayer=21000 mSubLayer=0    mToken=ActivityRecord{
    
    265c566 u0 com.android.gallery3d/.app.GalleryActivity} t462}
    mActivityRecord=ActivityRecord{
    
    265c566 u0 com.android.gallery3d/.app.GalleryActivity} t462}
    mAppDied=false    drawnStateEvaluated=true    mightAffectAllDrawn=true
    mViewVisibility=0x0 mHaveFrame=true mObscured=false
    mGivenContentInsets=[0,0][0,0] mGivenVisibleInsets=[0,0][0,0]
    mFullConfiguration={
    
    1.0 310mcc260mnc [en_US] ldltr sw411dp w411dp h797dp 560dpi nrml long port finger qwerty/v/v dpad/v winConfig={
    
     mBounds=Rect(0, 0 - 1440, 2960) mAppBounds=Rect(0, 0 - 1440, 2876) mMaxBounds=Rect(0, 0 - 1440, 2960) mDisplayRotation=ROTATION_0 mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_0} s.1 fontWeightAdjustment=0}
  

上記の dumpsys 情報を基に、次の結論を導き出すことができます。

1 ダイアログの WindowState はアクティビティの WindowState の上にあるため、ダイアログが一番上になります。
2 ダイアログのウィンドウ タイプは APPLICATION ですが、アクティビティのウィンドウ タイプは BASE_APPLICATION です。
3 共通の mToken=ActivityRecord{265c566 u0 com.android.gallery3d/.app.GalleryActivity } t462} があります。

したがって、ここでの上記の情報から、次の結論を導き出すことができます。

1. Dialog は単一の Window に属し、Activity から独立しています。どちらも wms 側で独立した WindowState を持っています。 2.
Dilaog は、Activity と共通のトークンを持ちます。このトークンと APPLICATION 型で決定できます。両方とも必要です。 ActivityRecord の下にマウントされる

上記はダイアログの状況を wms レベルから見たものです。上記の結論に達したら、ダイアログ コードの具体的な実装部分を見てみましょう。その後、wms からダイアログの状況を分析したので、明確な目標が見えてきます。ソース コードを見るとき、プロセス全体を理解するには、ほとんどの場合、この結論に頼る必要があります。
次に、ダイアログの場合は、トークン設定とタイプ設定に焦点を当てることができます。

WMS コースを学習すると、一般的にグローバル ウィンドウを作成するには次の手順が必要であることがわかります。

1. RootView を作成します。この RootView が実際に表示されるコンテンツです。
2. WindowManager の addView メソッドが呼び出されます。addView メソッドの最初のパラメータは上記の RootView、2 番目のパラメータは LayoutParams です
。 wms との通信はこの LayoutParams パラメータであり、対応するタイプとトークンを設定できます。これは、前述の wms の結論に関係します。

アプリケーションフレームワークレベルでのDialogのソースコード分析

まず、ダイアログを表示する最も簡単なコードを見てみましょう。

void showDialog() {
    
    
         Dialog d = new Dialog(this);
         d.setTitle("Dialog Title");
         d.setContentView(R.layout.window);
         d.show();
     }

Dialog を新規にする必要があり、Context パラメーターを渡す必要があることがわかります。Dialog の構築ソース コードに注目してみましょう: Dialog にはいくつかの構築方法があり、最終的に次のコードが呼び出されます

フレームワーク/base/core/java/android/app/Dialog.java


Dialog(@UiContext @NonNull Context context, @StyleRes int themeResId,
            boolean createContextThemeWrapper) {
    
    
        i     //省略部分
		//通过context获取WinowManager,注意这里非常关键哈
        mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
		//直接new PhoneWindow
        final Window w = new PhoneWindow(mContext);
        mWindow = w;
        //省略部分
        //调用PhoneWindow的setWindowManager来给自己设置合适的WindowManager
        w.setWindowManager(mWindowManager, null, null);
        w.setGravity(Gravity.CENTER);
    }


ここで見てみましょう. context.getSystemService(Context.WINDOW_SERVICE) は実際に最後にActivity の getSystemService を呼び出します. ここでは、キャッシュされた mWindowManager が直接使用されます。

@Override
public Object getSystemService(@ServiceName @NonNull String name) {
    
    
    if (getBaseContext() == null) {
    
    
        throw new IllegalStateException(
                "System services not available to Activities before onCreate()");
    }

    if (WINDOW_SERVICE.equals(name)) {
    
    
        return mWindowManager;
    } else if (SEARCH_SERVICE.equals(name)) {
    
    
        ensureSearchManager();
        return mSearchManager;
    }
    return super.getSystemService(name);
}

では、Activity の mWindowManager はどこから来たのでしょうか? ここで、Dialog が実際に Activity の mWindowManager を使用していることがわかっていることに注意してください。では、このアクティビティに値が割り当てられるのはどこでしょうか? 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,
            Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken,
            IBinder shareableActivityToken) {
    
    
       

        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
                
                }

次のステップは PhoneWindow の setWindowManager です

 public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
    
    
        mAppToken = appToken;//设置token到了windowmanager
        mAppName = appName;
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

ここでは別の createLocalWindowManager が呼び出されていますが、実際には、Activity の WindowManager がローカル ウィンドウマネージャーを作成する必要があると考えることができます。

public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
    
    
        return new WindowManagerImpl(mContext, parentWindow, mWindowContextToken);
    }
   private WindowManagerImpl(Context context, Window parentWindow,
            @Nullable IBinder windowContextToken) {
    
    
        mContext = context;
        mParentWindow = parentWindow;
        mWindowContextToken = windowContextToken;
    }

WindowManagerImpl は、ここで、別の変数、parentWindow 変数を構築します。これは非常に重要です。一般に、parentWindow は、それ自身のウィンドウの PhoneWindow です。つまり、
ここで結論があります: WindowManager のparentWindow は、それ自体の PhoneWindow に対応します。

PhoneWindow は実際には Window を継承しており、主に Frameworks/base/core/java/android/view/Window.java に注意する必要がある LayoutParams パラメータがあります

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

対応する LayoutParamas を見てみましょう。

  public LayoutParams() {
    
    
            super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
            type = TYPE_APPLICATION;//这里初始化就是TYPE_APPLICATION
            format = PixelFormat.OPAQUE;
        }

上記は堂々巡りになりましたが、要約すると次のとおりです。
1. Dialog の mWindowManager は、コンテキストが Activity であるため、実際には Activet からのものです
。 2. Dialog には新しい PhoneWindow もあります。これは、Dialog 内で独立したウィンドウになることを意味します。現時点では、デフォルトのタイプは TYPE_APPLICATION です。

次に注目すべきは WidnowManager の addView であり、LayoutParams に関連する変更があるかどうかを確認します。

AddView は、Dialog が最初に show メソッドを呼び出したときにのみ実行されます。

    public void show() {
    
    
     //省略

        mWindowManager.addView(mDecor, l);//注意这里mWindowManager是Activity的哈
     
  //省略
    }


ここでは、addView がFrameworks/base/core/java/android/view/WindowManagerGlobal.javaまで呼び出されます。

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow, int userId) {
    
    
       
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        if (parentWindow != null) {
    
    //因为是Activity的WindowManager这里的parentWindow就是activity的PhoneWindow,故不等于null
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } else {
    
    
           
        }
//省略

            if (windowlessSession == null) {
    
    
                root = new ViewRootImpl(view.getContext(), display);
            } else {
    
    
                root = new ViewRootImpl(view.getContext(), display,
                        windowlessSession);
            }

            view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);

            // do this last because it fires off messages to start doing things
            try {
    
    
                root.setView(view, wparams, panelParentView, userId);
            } catch (RuntimeException e) {
    
    
      //省略
        }
    }


では、adjustLayoutParamsForSubWindow(wparams) の分析に焦点を当ててみましょう。

void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) {
    
    
        CharSequence curTitle = wp.getTitle();
        if (wp.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
    
    
                //省略
        } else if (wp.type >= WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW &&
                wp.type <= WindowManager.LayoutParams.LAST_SYSTEM_WINDOW) {
    
    
              //省略
        } else {
    
    //TYPE_APPLICATION会进入这里哈
            if (wp.token == null) {
    
    //前面Dialog没有对token又过赋值为null,进入这里
                wp.token = mContainer == null ? mAppToken : mContainer.mAppToken;//这里会是的mAppToken,它和Activity的一样,所以就是这样把Dialog的token和Activity的token搞成一样的
            }
            if ((curTitle == null || curTitle.length() == 0)
                    && mAppName != null) {
    
    
                wp.setTitle(mAppName);
            }
        }
        //省略
    }


上記のメソッドはコア キーであり、それに依存してトークンに値を割り当てるため、WMS 側でダイアログとアクティビティを統合するための重要な基盤となります。

要約:

これはアプリケーション レベルでの Dialog の一時的な分析です。私たちの分析アイデアは、WMS をすでに深く理解しているという事実に基づいています。その後、問題をより高い視点から見て、目的を持ってコードを分析できます。常にコードが多すぎて、目的なしに正確に分析することは不可能です。長い間コードを分析し、多くのブログを読んできたとしても、重要なポイントを把握できず、依然として理解できない可能性が非常に高いです。知識ポイントをつなぎ合わせます。

おすすめ

転載: blog.csdn.net/learnframework/article/details/133001825