第八章-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隐藏和 显示工作过程是类似的。这里就不具体分析。
在这里插入图片描述

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