第八章-Window的創建過程

在這裏插入圖片描述

1、Activity的Window創建過程

要分析Activity的Window創建過程就必須瞭解activity的啓動過程,詳細的過程在後面分析,大概瞭解即可。Activity的啓動過程很複雜,最終會由ActivityThread中的perfromLaunchActivity()來完成整個啓動過程,這個方法內部會通過類加載器創建Activity的實例對象,並且調用其attach方法爲其關聯運行過程中的所依賴的一系列上下文的變量。代碼如下所示:

 if (activity != null) {
	Context appContext = createBaseContextForActivity(r, activity);
	CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
	Configuration config = new Configuration(mCompatConfiguration);
	if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
			+ r.activityInfo.name + " with config " + config);
	activity.attach(appContext, this, getInstrumentation(), r.token,
			r.ident, app, r.intent, r.activityInfo, title, r.parent,
			r.embeddedID, r.lastNonConfigurationInstances, config,
			r.voiceInteractor);

這裏是引用

以上在android9.0上並沒有找到相關的PolicyManager相關的代碼。(書中可能是之前的版本),我在Android4.0上搜索到了相關的代碼。

在這裏插入圖片描述
在這裏插入圖片描述

到這裏Window已經創建完成了(不同版本代碼最終都是創建了PhoneWindow對象),下面分析Activity的視圖是怎麼附屬在Window上面的。由於Activity的視圖是由setContentView方法提供的,我們只需要看setContentView的實現就可以:

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

在這裏插入圖片描述
看下PhoneWindow中的setContentView方法的代碼:
在這裏插入圖片描述
a.如果沒有DecorView就去創建它

這裏是引用
在這裏插入圖片描述

b.將View添加到DecorView的mContentParent中

在這裏插入圖片描述

c.回調Activity的onCreateChanged方法來通知Activity視圖已經發生改變

這個過程就更簡單了,由於Activity實現了Window的Callback接口,這裏表示Activity的佈局文件已經被添加到DecorView的mContentParent中了,於是需要通知Activity,使其可以做相應的處理。Activity的onCreateChanged是個空實現,我們可以在子Activity處理這個回調,這個過程代碼如下:

final callback cb = getCallback();
if(cb != null && !isDestroyed()){
	cb.onContentChanged();
}

在這裏插入圖片描述
在這裏插入圖片描述

可以看到真正的DecorView的添加時在Activity的makeViewVisible中的。

2、Dialog的Window創建過程
Dialog的Window創建過程和Activity類似,有如下幾個步驟:
a.創建Window
在android9.0上的代碼和書上的不一樣。以我們的爲準。代碼片段如下:
在這裏插入圖片描述

b.初始化DecorView並將Dialog的視圖添加到DecorView中
這個過程也和Activity的類似,都是通過Window去添加指定的佈局文件。
在這裏插入圖片描述

c.將DecorView添加到Window並且顯示
在這裏插入圖片描述

創建一個Dialog的代碼:(MainActivity的onCreate方法中)

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Dialog dialog = new Dialog(this);//使用activity的context是沒問題的
        TextView textView = new TextView(this);
        textView.setText("this is a toast");
        dialog.setContentView(textView);
        dialog.show();
    }

效果如下:
在這裏插入圖片描述
如果使用 Dialog dialog = new Dialog(getApplicationContext()); 就會報如下異常:

2020-04-04 23:18:06.428 22385-22385/com.example.testgesturedetector E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.testgesturedetector, PID: 22385
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.testgesturedetector/com.example.testgesturedetector.MainActivity}: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running?
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3194)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3302)
        at android.app.ActivityThread.-wrap12(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1891)
        at android.os.Handler.dispatchMessage(Handler.java:108)
        at android.os.Looper.loop(Looper.java:166)
        at android.app.ActivityThread.main(ActivityThread.java:7425)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:245)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:921)
     Caused by: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running?
        at android.view.ViewRootImpl.setView(ViewRootImpl.java:884)
        at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:372)
        at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:128)
        at android.app.Dialog.show(Dialog.java:454)
        at com.example.testgesturedetector.MainActivity.onCreate(MainActivity.java:24)
        at android.app.Activity.performCreate(Activity.java:7372)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1218)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3147)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3302) 
        at android.app.ActivityThread.-wrap12(Unknown Source:0) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1891) 
        at android.os.Handler.dispatchMessage(Handler.java:108) 
        at android.os.Looper.loop(Looper.java:166) 
        at android.app.ActivityThread.main(ActivityThread.java:7425) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:245) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:921) 

在這裏插入圖片描述
在android9.0上實際測試是能正常彈出來的。
在這裏插入圖片描述

3、Toast的Window創建過程

在這裏插入圖片描述

    /**
     * Show the view for the specified duration.
     */
    public void show() {
        if (mNextView == null) {
            throw new RuntimeException("setView must have been called");
        }

        INotificationManager service = getService();
        String pkg = mContext.getOpPackageName();
        TN tn = mTN;
        tn.mNextView = mNextView;
        final int displayId = mContext.getDisplayId();

        try {
            service.enqueueToast(pkg, tn, mDuration, displayId);
        } catch (RemoteException e) {
            // Empty
        }
    }

    /**
     * Close the view if it's showing, or don't show it if it isn't showing yet.
     * You do not normally have to call this.  Normally view will disappear on its own
     * after the appropriate duration.
     */
    public void cancel() {
        mTN.cancel();
    }

在這裏插入圖片描述

首先來看下Toast的顯示過程,它調用了NMS中的enqueueToast方法:

INotificationManager service = getService();
String pkg = mContext.getOpPackageName();
TN tn = mTN;
tn.mNextView = mNextView;

try {
    service.enqueueToast(pkg, tn, mDuration);
} catch (RemoteException e) {
    // Empty
}

在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述

    @GuardedBy("mToastQueue")
    void showNextToastLocked() {
        ToastRecord record = mToastQueue.get(0);
        while (record != null) {
            if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
            try {
                record.callback.show(record.token);
                scheduleDurationReachedLocked(record);//這個和書上的代碼不一致,需要自己分析
                return;
            } catch (RemoteException e) {
                Slog.w(TAG, "Object died trying to show notification " + record.callback
                        + " in package " + record.pkg);
                // remove it from the list and let the process die
                int index = mToastQueue.indexOf(record);
                if (index >= 0) {
                    mToastQueue.remove(index);
                }
                keepProcessAliveIfNeededLocked(record.pid);
                if (mToastQueue.size() > 0) {
                    record = mToastQueue.get(0);
                } else {
                    record = null;
                }
            }
        }
    }

在這裏插入圖片描述

Toast隱藏和 顯示工作過程是類似的。這裏就不具體分析。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章