各种Window的创建过程

    xiaoxiao2021-03-26  36

    Window的创建过程

    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过程

    Toast访问NotifationManagerServiceNotificationManagerService回调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伪代码

    转载请注明原文地址: https://ju.6miu.com/read-662394.html

    最新回复(0)