各种Window的创建过程

Window的创建过程

Window是View的直接管理者

Window是View的直接管理者

图片源自https://mp.weixin.qq.com/s/IrwjQqlDoLp3xQZbthncVg

Activity的Window创建过程

Activity的启动最终会通过ActivityThread#performLaunchActivity()来完成。

ActivityThread#performLaunchActivity()

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    //...
    //通过类加载器创建Activity实例
    java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
    activity = mInstrumentation.newActivity(
            cl, component.getClassName(), r.intent);
   //...

    Application app = r.packageInfo.makeApplication(false, mInstrumentation);

    Context appContext = createBaseContextForActivity(r, activity);
    CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
    Configuration config = new Configuration(mCompatConfiguration);

    //关联运行过程中所依赖的一系列上下文环境变量
    activity.attach(appContext, this, getInstrumentation(), r.token,
            r.ident, app, r.intent, r.activityInfo, title, r.parent,
            r.embeddedID, r.lastNonConfigurationInstances, config,
            r.voiceInteractor);

    mActivities.put(r.token, r);
    return activity;
}

在attach方法里,系统会创建Activity所属的Window对象并为其设置回调接口。

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, IVoiceInteractor voiceInteractor) {

    attachBaseContext(context);
    mFragments.attachActivity(this, mContainer, null);

    //创建Window对象
    mWindow = PolicyManager.makeNewWindow(this);
    //设置回调 比如我们熟悉的onAttachedToWindow、onDetachedFromWindow、dispatchTouchEvent
    //[重要] 当Window接收到外界的状态改变时就会调用Activity实现的回调
    mWindow.setCallback(this);
    mWindow.setOnWindowDismissedCallback(this);

    //赋值
    if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
        mWindow.setSoftInputMode(info.softInputMode);
    }
    if (info.uiOptions != 0) {
        mWindow.setUiOptions(info.uiOptions);
    }
    mUiThread = Thread.currentThread();
    mMainThread = aThread;
    mInstrumentation = instr;
    mToken = token;
    mIdent = ident;
    mApplication = application;
    mIntent = intent;
    mComponent = intent.getComponent();
    mActivityInfo = info;
    mTitle = title;
    mParent = parent;
    mEmbeddedID = id;
    mLastNonConfigurationInstances = lastNonConfigurationInstances;

    //...       

    //设置WindowManager
    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;
}

再来看 PolicyManager.makeNewWindow(this),PilicyManager的真正实现类是Policy类。
位于source\frameworks\base\policy\src\com\android\internal\policy\impl\Policy.java

public Window makeNewWindow(Context context) {
    return new PhoneWindow(context);
}  

可以发现Window的实现类确实是PhoneWindow。

Window初始完毕后,再来看Activity的视图是如何依附在Window上的。Activity的视图通过setContent方法提供。

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

可以发现,Activity的setContentView将具体实现交由Window处理。

phoneWindow#setContentView()

@Override
public void setContentView(int layoutResID) {
    // 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.
    //1.创建DecorView (如果没有创建的话)
    if (mContentParent == null) {
        installDecor();
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        mContentParent.removeAllViews();
    }

    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                getContext());
        transitionTo(newScene);
        //2.将View添加到DecorView的mContentParent中
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }
    //3.回调Activity的onContentChanged()通知Activity视图已经发生改变
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged();
    }
}

经过setContentView方法,DecorView已经被创建并初始化完毕,Activity的布局文件也已经成功添加到DecorView的mContentParent中,但这个时候DecorView还没有被WindowManager添加到Window中。 (在添加之前)

#
  • Activity的attach()中,Window被创建并初始化
  • 在Activity的setContentView中 (PhoneWindow#setContentView),DecorView被创建 (如果没被创建的话)
  • 而在ActivityThread#handleResumeActivity首先会调用Activity的onResume方法中,接着会先将DecorView设为不可见(INVISIBLE),然后会调用Activity的makeVisible(),在makeVisible()中,将DecorView添加到Window并置为Visible。
#
void makeVisible() {
    //将DecorView添加到Window
    if (!mWindowAdded) {
        ViewManager wm = getWindowManager();
        wm.addView(mDecor, getWindow().getAttributes());
        mWindowAdded = true;
    }
    //将Activity的显示置为Visible
    mDecor.setVisibility(View.VISIBLE);
}  

Dialog的window创建过程

Dialog的Window创建过程和Activity类似

Dialog#构造方法

Dialog(Context context, int theme, boolean createContextThemeWrapper) {
    //...        
    mContext = context;

    mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
    //创建Window对象
    Window w = PolicyManager.makeNewWindow(mContext);
    mWindow = w;
    w.setCallback(this);
    w.setOnWindowDismissedCallback(this);
    w.setWindowManager(mWindowManager, null, null);
    w.setGravity(Gravity.CENTER);
    mListenersHandler = new ListenersHandler(this);
}

再来看Dialog#setContentView()

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

同样是交由Window处理。

当Dialog dismiss时,会通过WindowManager来移除DecorView

@Override
public void dismiss() {
    if (Looper.myLooper() == mHandler.getLooper()) {
        dismissDialog();
    } else {
        mHandler.post(mDismissAction);
    }
}

void dismissDialog() {
    //...
    //移除DecorView
    mWindowManager.removeViewImmediate(mDecor);
   //...
}  

注意

普通Dialog需要使用Activity的Content
因为Window需要应用token,而应用token一般只有Activity才拥有。
系统Window比较特殊,它可以不需要token。

Toast的Window创建过程

Toast属于系统Window
Toast内部有两类IPC过程

  1. Toast访问NotifationManagerService
  2. NotificationManagerService回调Toast的TN接口

Toast提供了show和cancel分别用于显示和隐藏Toast [都是IPC过程]

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;

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

public void cancel() {
    mTN.hide();

    try {
        getService().cancelToast(mContext.getPackageName(), mTN);
    } catch (RemoteException e) {
        // Empty
    }
}

主要来看enqueueToast()

//参数一:当前应用的包名
//参数二:远程回调
//参数三:Toast的时长
//enqueueToast首先将Toast封装为ToastRecord对象并将其添加到一个名为mToastQueue的队列中
service.enqueueToast(pkg, tn, mDuration);  

当ToastRecord被添加到mToastQueue中后,Inotifacationmanager就会通过showNextToastLacked方法来显示当前的Toast。

NotificationManagerService#showNextToastLocked()

void showNextToastLocked() {
    //获取下一个ToastRecord
    ToastRecord record = mToastQueue.get(0);
    while (record != null) {
        if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
        try {
            //record.callback就是Toast.java类中的TN (Binder) 对象
            record.callback.show();
            //发送延迟消息来移除toast
            //scheduleTimeoutLocked -> mHandler.sendMessageDelayed -> cancelToastLocked -> record.callback.hide();
            scheduleTimeoutLocked(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);
            }
            keepProcessAliveLocked(record.pid);
            if (mToastQueue.size() > 0) {
                record = mToastQueue.get(0);
            } else {
                record = null;
            }
        }
    }
}  

可以发现,Toast的显示和移除都是通过Toast的TN类(Binder对象)来完成的。

Toast内部类TN

private static class TN extends ITransientNotification.Stub {
    final Runnable mShow = new Runnable() {
        @Override
        public void run() {
            handleShow();
        }
    };

    final Runnable mHide = new Runnable() {
        @Override
        public void run() {
            handleHide();
            // Don't do this in handleHide() because it is also invoked by handleShow()
            mNextView = null;
        }
    };

    @Override
    public void show() {
        if (localLOGV) Log.v(TAG, "SHOW: " + this);
        mHandler.post(mShow);
    }

    @Override
    public void hide() {
        if (localLOGV) Log.v(TAG, "HIDE: " + this);
        mHandler.post(mHide);
    }

    //...
}  

真正的显示

public void handleShow() {
        //...
        mWM = (WindowManager)context.getSystemService
        //...
        if (mView.getParent() != null) {
            mWM.removeView(mView);
        }
        mWM.addView(mView, mParams);
        //...
    }
}  

真正的隐藏

public void handleHide() {
    if (mView != null) {
        if (mView.getParent() != null) {
            mWM.removeView(mView);
        }
        mView = null;
    }
}

相关

PhoneWindow伪代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

氦客

你的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值