美文网首页
Android UI | DecorView 如何添加到Wind

Android UI | DecorView 如何添加到Wind

作者: stamSuper | 来源:发表于2021-11-12 20:50 被阅读0次

    上一篇讲了 setContentView 到所有布局的加载: 传送门

    要想了解DecorView 如何添加到Window里面的,需要先了解Activity如何运行的
    这里就要提到一个类ActivityThread,这里面的一个方法

      public Activity handleLaunchActivity(ActivityClientRecord r,
            PendingTransactionActions pendingActions, Intent customIntent) {
         // ... 
        // 这里可以看到启动Activity
        final Activity a = performLaunchActivity(r, customIntent);
        // ...
    
        return a;
    }
    
    
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ActivityInfo aInfo = r.activityInfo;
       
    
        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        try {
        // 这一步 创建了一个activity
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
            
        }
    
        try {
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
            if (activity != null) {
                // ....
                appContext.setOuterContext(activity);
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback,
                        r.assistToken);
    
                if (customIntent != null) {
                    activity.mIntent = customIntent;
                }
                r.lastNonConfigurationInstances = null;
                checkAndBlockForNetworkAccess();
                activity.mStartedActivity = false;
                int theme = r.activityInfo.getThemeResource();
                if (theme != 0) {
                    activity.setTheme(theme);
                }
    
                activity.mCalled = false;
                if (r.isPersistable()) {
                 // 这里调用 activity的OnCreate方法
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
                if (!activity.mCalled) {
                    throw new SuperNotCalledException(
                        "Activity " + r.intent.getComponent().toShortString() +
                        " did not call through to super.onCreate()");
                }
                r.activity = activity;
            }
            r.setState(ON_CREATE);
    
            // updatePendingActivityConfiguration() reads from mActivities to update
            // ActivityClientRecord which runs in a different thread. Protect modifications to
            // mActivities to avoid race.
            synchronized (mResourcesManager) {
                mActivities.put(r.token, r);
            }
    
        } catch (SuperNotCalledException e) {
            throw e;
    
        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to start activity " + component
                    + ": " + e.toString(), e);
            }
        }
    
        return activity;
    }
    

    其中比较关键的方法是调用是:
    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) {
        attachBaseContext(context);
    
        mFragments.attachHost(null /*parent*/);
        //  实例化PhoneWindow对象
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(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;
        mAssistToken = assistToken;
        mIdent = ident;
        mApplication = application;
        mIntent = intent;
        mReferrer = referrer;
        mComponent = intent.getComponent();
        mActivityInfo = info;
        mTitle = title;
        mParent = parent;
        mEmbeddedID = id;
        mLastNonConfigurationInstances = lastNonConfigurationInstances;
        if (voiceInteractor != null) {
            if (lastNonConfigurationInstances != null) {
                mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
            } else {
                mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
                        Looper.myLooper());
            }
        }
    
         // 这里比较关键, 设置了WindowManager对象,实际上就是创建了WindowManagerImpl对象
        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;
    
        mWindow.setColorMode(info.colorMode);
    
        setAutofillOptions(application.getAutofillOptions());
        setContentCaptureOptions(application.getContentCaptureOptions());
    }
    

    随后再来看看 ActivityThread类中的handleResumeActivity方法,方法内容如下:

    @Override
    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {
         // ...
        /// 这里调用了activity.performResume()--->mInstrumentation.callActivityOnResume(this)--->activity.onResume();
        final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
        if (r == null) {
            // We didn't actually resume the activity, so skipping any follow-up actions.
            return;
        }
         // ...
    
        final Activity a = r.activity;
    
        final int forwardBit = isForward
                ? WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
    
        if (r.window == null && !a.mFinished && willBeVisible) {
            // 这里就是PhoneWindow对象,可以会看attach方法赋值过程
            r.window = r.activity.getWindow();
            // 拿到decorView对象
            View decor = r.window.getDecorView();
            // 设置隐藏
            decor.setVisibility(View.INVISIBLE);
            // 拿到WindowManagerImpl对象
            ViewManager wm = a.getWindowManager();
            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;
            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
            l.softInputMode |= forwardBit;
            if (r.mPreserveWindow) {
                a.mWindowAdded = true;
                r.mPreserveWindow = false;
                ViewRootImpl impl = decor.getViewRootImpl();
                if (impl != null) {
                    impl.notifyChildRebuilt();
                }
            }
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    // 这里就把decorView添加到WindowManagerImpl里面了
                    wm.addView(decor, l);
                } else {
                    a.onWindowAttributesChanged(l);
                }
            }
    
        } else if (!willBeVisible) {
            if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");
            r.hideForNow = true;
        }
    
        if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {
           
            if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward=" + isForward);
            WindowManager.LayoutParams l = r.window.getAttributes();
            if ((l.softInputMode
                    & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)
                    != forwardBit) {
                l.softInputMode = (l.softInputMode
                        & (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))
                        | forwardBit;
                if (r.activity.mVisibleFromClient) {
                    ViewManager wm = a.getWindowManager();
                    View decor = r.window.getDecorView();
                    // 这里是更新decor的view布局
                    wm.updateViewLayout(decor, l);
                }
            }
    
            r.activity.mVisibleFromServer = true;
            mNumVisibleActivities++;
            if (r.activity.mVisibleFromClient) {
                r.activity.makeVisible();
            }
        }
    
        r.nextIdle = mNewActivities;
        mNewActivities = r;
        if (localLOGV) Slog.v(TAG, "Scheduling idle handler for " + r);
        Looper.myQueue().addIdleHandler(new Idler());
    }
    

    继续查看 WindowManagerImpl里面的addView方法

    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }
    
    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        //...
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        if (parentWindow != null) {
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } else {
            final Context context = view.getContext();
            if (context != null
                    && (context.getApplicationInfo().flags
                            & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
            }
        }
        
        ViewRootImpl root;
        View panelParentView = null;
    
        synchronized (mLock) {
            // ...
            // 这里实例化了一个非常重要的类,所有的View 真正绘制都是在这个类里面执行的
            root = new ViewRootImpl(view.getContext(), display);
    
            view.setLayoutParams(wparams);
            // 这里通过三个三个集合分别把 decorView 、root 、 params 保存起来
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
    
            // do this last because it fires off messages to start doing things
            try {
                // 给ViewRootImpl 设置一个View,即decorView
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }
    

    继续查看 ViewRootImpl类里面的setView方法,如下:

    /**
     * We have one child
     *  root 只有一个子View
     */
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
        // 这里只有一个view
            if (mView == null) {
                mView = view;
    
                // .... 
    
                // Schedule the first layout -before- adding to the window
                // manager, to make sure we do the relayout before receiving
                // any other events from the system.
                // 这个方法很眼熟,这里就是调用具体绘制的方法
                requestLayout();
    
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    mInputChannel = new InputChannel();
                }
                mForceDecorViewVisibility = (mWindowAttributes.privateFlags
                        & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    // 这里通过方法名称可以看出是 添加显示到设备的方法
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
                            mTempInsets);
                    setFrame(mTmpFrame);
                } catch (RemoteException e) {
                    mAdded = false;
                    mView = null;
                    mAttachInfo.mRootView = null;
                    mInputChannel = null;
                    mFallbackEventHandler.setView(null);
                    unscheduleTraversals();
                    setAccessibilityFocus(null, null);
                    throw new RuntimeException("Adding window failed", e);
                } finally {
                    if (restore) {
                        attrs.restore();
                    }
                }
                // ...
                // 这个就是给decorView设置父级为 ViewRootImpl
                view.assignParent(this);
                // ...
            }
        }
    }
    

    到这里,decorView 添加到PhoneWindow 基本结束了。下面我们继续查看,view 是如何在RootViewImpl类中绘制出来的。

    查看ViewRootImpl类里面的 requestLayout()方法

    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
    
            // 这个就是检测当前线程是否在主线程
            checkThread();
            mLayoutRequested = true;
            // 重点是这个方法
            scheduleTraversals();
        }
    }
    
    @UnsupportedAppUsage
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            // 这里执行了一个任务,mTraversalRunnable
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                // 计划使用批处理输入
                scheduleConsumeBatchedInput();
            }
            // 通知渲染器帧挂起
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }
    

    继续查看 mTraversalRunnable

    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }
    
    void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
            // 重点是这个方法
            performTraversals();
        }
    }
    
     private void performTraversals() {
        // 这个就是我们的DecorView
        final View host = mView;
        //... 
        //顶层视图DecorView所需要窗口的宽度和高度,因为我们必须要知道父容器的宽高才能测量子View的宽高。 
        int desiredWindowWidth;
        int desiredWindowHeight;
        mDisplay.getRealSize(size);
        desiredWindowWidth = size.x;
        desiredWindowHeight = size.y;
        // ... 
        else {
        // 这这里的宽高是是手机屏幕的宽高,见下面代码
                DisplayMetrics packageMetrics =
                    mView.getContext().getResources().getDisplayMetrics();
                desiredWindowWidth = packageMetrics.widthPixels;
                desiredWindowHeight = packageMetrics.heightPixels;
            }
         // .. 省略,大部分为初始化操作
         // Ask host how big it wants to be
         // 重点方法,通过注释可以猜测,这里是测量布局所占用空间。
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
        //  这个就是比较常见的 View 布局方法
        performLayout(lp, desiredWindowWidth, desiredWindowHeight);
        // 开始绘制方法
        performDraw();
    
    }
    

    继续查看 performMeasure

     private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
        try {
            // 这里的mView 就是decorView (参看ViewRootImpl的setView() 方法 )
            mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }
    

    decorView 实际上继承了 ViewGroup 、 View ,所以实际上也是调用的View的measure方法。所以这些 measure 、 layout 、 draw都是再ViewRootImpl发起的。

    到这里,Activity 启动过程中生成PhoneWindow , 并加载DecorView ,之后调起绘制流程。

    总结:ActivityThread.handleResumeActivity中,wm.addView(decor, l);
    把decorView 添加到windowManager , 然后在WindowManagerImpl 里面初始化了 ViewRootImpl ,然后调用 ViewRootImpl.setView(),在setview方法里调用了view.assignParent(this);,将Decorview的mParent设置成ViewRootImpl

    image.png

    相关文章

      网友评论

          本文标题:Android UI | DecorView 如何添加到Wind

          本文链接:https://www.haomeiwen.com/subject/rnqizltx.html