Android UI架构(二)--Activity的显示(1).md

参考资料

  1. 深入理解Andorid(卷一)

  2. 理清Activity、View及Window之间关系

  3. Android窗口系統第三篇—WindowManagerService中窗口的組織方式

在前面我们稍微了解了底层UI绘制的大致原理. 现在我们从Android最经典的Activity入手, 分析下Activity(布局)是如何与SurfaceFlinger交互的.

一. 类图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

二. Activity的UI创建流程

Activity的生命周期都很熟悉, 但Activity是如何被创建的呢. 通过学习Activity的启动流程,我们知道最终是调用了ActivityThread.handleLaunchActivity

2.1 ActivityThread.handleLaunchActivity


@Override
    public Activity handleLaunchActivity(ActivityClientRecord r,
            PendingTransactionActions pendingActions, Intent customIntent) {
        ......

        // 这里其实就是在WindowManagerGlobal里获取了WindowManager服务(window)
        WindowManagerGlobal.initialize();

        // 创建Activity
        final Activity a = performLaunchActivity(r, customIntent);
        if (a != null) {
            r.createdConfig = new Configuration(mConfiguration);
            reportSizeConfigurations(r);
            if (!r.activity.mFinished && pendingActions != null) {
                pendingActions.setOldState(r.state);
                pendingActions.setRestoreInstanceState(true);
                pendingActions.setCallOnPostCreate(true);
            }
        } 

        ......
        return a;
    }

2.2 ActivityThread.performLaunchActivity


    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ActivityInfo aInfo = r.activityInfo;
        if (r.packageInfo == null) {
            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }

        ......

        // 通过反射创建的

        Activity activity = null;
        try {
            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);
            }

        ......

              Window window = null;
                if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
                    window = r.mPendingRemoveWindow;
                    r.mPendingRemoveWindow = null;
                    r.mPendingRemoveWindowManager = 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);

        ......

           activity.mCalled = false;
                if (r.isPersistable()) {

                    // 3.1 进入Activity生命周期, 调用onCreate
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }

         ......

    }

稍微复习了下Activity的启动流程, 现在我们有了Activity的实例.

2.3 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) {
        attachBaseContext(context);
        mFragments.attachHost(null /*parent*/);

        // 注意这里的Window是PhoneWindow
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        ......

        // 创建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());
        }

        // 保存该WindowManager对象
        mWindowManager = mWindow.getWindowManager();
        mCurrentConfig = config;
        mWindow.setColorMode(info.colorMode);
        setAutofillCompatibilityEnabled(application.isAutofillCompatibilityEnabled());
        enableAutofillCompatibilityIfNeeded();
    }

2.4 PhoneWindow.setWindowManager – 创建WindowManager对象

PhoneWindow是继承了Window的,这个方式是在Window.java里定义的


    public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated
                || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }

        // 这里创建的是WindowManagerImpl的对象, WindowManagerImpl继承了WindowManager
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

大致类图如下:
在这里插入图片描述
从这里可以看到

  1. Activity中的mWindow其实是PhoneWindow,继承了Window类

  2. Activity中的mWindowManager就是WindowManagerImpl, 其实现了WindowManager接口的方法

  3. WindowManagerImpl 其实只是代理模式,最终干活的是WindowManagerGlobal, 这是个单例,一个进程只有一个.负责管理View以及和WMS通信

再继续分析之前,我们有必要先了解Activity,View和Window的联系与区别.

这部分其实这篇博客理清Activity、View及Window之间关系写的非常到位. 强烈建议阅读.

  1. View

View主要是用于绘制我们想要的结果,是一个最基本的, 最终呈现给用户的一个个UI组件.

  1. Window

Window是一个容器,里面装着很多树形结构的View. 对系统来讲, 因为有了Window的存在,就很方便管理各个View了

  1. Activity

Activity的出现方便了开发者对各个窗口的管理(任务栈,状态等)

三. Activity的UI绘制

到这里还只是皮毛,我们仅仅知道了Activity的创建,关于UI部分还没接触到. 其实是隐藏在流程之中的.
在Activity的生命周期onCreate方法中,我们一般会调用setContentView方法.这里会设置该Activity的布局也就是UI部分-View.
注意, 这部分是发生在App进程中的.

3.1 Activity.setContentView


    public void setContentView(View view) {

        // 设置View
        getWindow().setContentView(view);
        initWindowDecorActionBar();
    }

    public Window getWindow() {

        // 上面我们分析了,这里的mWindow就是PhoneWindow
        return mWindow;
    }

3.2 PhoneWindow.setContentView


    @Override
    public void setContentView(View view, ViewGroup.LayoutParams params) {
        if (mContentParent == null) {

            // mContentParent是ViewGroup类型的对象, 初始值为null

            // 3.2.1 会在intallDecor中创建该对象
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

        // 启用Transitions,我们看普通场景即未启用该feature的情况

        // note: 关于Transition参考博文[Activity和Fragment Transition介绍](http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0113/2310.html)

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            view.setLayoutParams(params);
            final Scene newScene = new Scene(mContentParent, view);
            transitionTo(newScene);
        } else {

            // 3.3 添加Activity的布局
            mContentParent.addView(view, params);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }

3.2.1 PhoneWindow.installDecor


    private void installDecor() {
        mForceDecorInstall = false;
        if (mDecor == null) {

            // 3.2.2 初始值为null,创建DecorView
            mDecor = generateDecor(-1);
            ......
        }

        if (mContentParent == null) {

            // 3.2.3 根据DecorView生成mContentParent
            mContentParent = generateLayout(mDecor);

            ......

    }

这里主要是创建DecorView和mContentParent.

3.2.2 PhoneWindow.generateDecor


protected DecorView generateDecor(int featureId) {
        Context context;
        if (mUseDecorContext) {
            Context applicationContext = getContext().getApplicationContext();
            if (applicationContext == null) {
                context = getContext();
            } else {
                context = new DecorContext(applicationContext, getContext().getResources());
                if (mTheme != -1) {
                    context.setTheme(mTheme);
                }
            }
        } else {
            context = getContext();
        }
        return new DecorView(context, featureId, this, getAttributes());
    }

DecorView的家族图谱如下,其实也就是一个继承了FrameLayout的View.
在这里插入图片描述

3.2.3 PhoneWindow.generateLayout

    protected ViewGroup generateLayout(DecorView decor) {
        // Apply data from current theme.
        TypedArray a = getWindowStyle();
        ......
        mDecor.startChanging();
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
        // ID_ANDROID_CONTENT = com.android.internal.R.id.content;
        // 3.2.4 这里的contentParent其实是DecorView的一部分
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        ...... // 设置了窗口属性
        mDecor.finishChanging();
        return contentParent;
    }

3.2.4 Window.findViewById

    @Nullable
    public <T extends View> T findViewById(@IdRes int id) {
        return getDecorView().findViewById(id);
    }

从这里知道DecorView和Window的关系了. 下图中的ContentParent(ViewGroup)其实就是Activity的View
在这里插入图片描述
回到 3.2,继续看Activity的View是如何设置的.

3.3 ViewGroup.addView

    public void addView(View child) {
        addView(child, -1);
    }
    public void addView(View child, int index) {
        if (child == null) {
            throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
        }
        LayoutParams params = child.getLayoutParams();
        if (params == null) {
            params = generateDefaultLayoutParams();
            if (params == null) {
                throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
            }
        }
        addView(child, index, params);
    }
    public void addView(View child, int index, LayoutParams params) {
        if (child == null) {
            throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
        }
        // 啊哈,这里就开始了愉快的 measure, layout, draw的过程了
        requestLayout();
        invalidate(true);
        addViewInner(child, index, params, false);
    }

addView最后会调用requestLayout,最终会走View的绘制三部曲:measure,layout,draw(此流程后续详细分析). 大致流程图如下(图片来源):
在这里插入图片描述

四. Activity与WMS的交互

前面Activity创建过程中就会绘制对应的View布局,但是这个时候还没有真正显示到屏幕上. 从Activity的生命周期中我们知道,Activity的显示是在执行onResume方法后的.
所以我们看ActivityThread.handleResumeActivity流程

4.1 ActivityThread.handleResumeActivity

    @Override
    public void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward,
            String reason) {
        unscheduleGcIdler();
        mSomeActivitiesChanged = true;
        // 这里调用了Activity.onResume 方法
        final ActivityClientRecord r = performResumeActivity(token, clearHide, reason);
        if (r != null) {
           final Activity a = r.activity;
           ......
           if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow(); // 这里的Window就是PhoneWindow
                View decor = r.window.getDecorView();
                decor.setVisibility(View.INVISIBLE); // 这里标记DecorView的flag为INVISIBLE
                ViewManager wm = a.getWindowManager(); // WindowManagerImpl
                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;
                    // 通常,ViewRoot使用addView-> ViewRootImpl#setView中的Activity设置回调。
                    // 如果重新使用DecorView,我们必须通知视图根回调可能已经改变。
                    ViewRootImpl impl = decor.getViewRootImpl();
                    if (impl != null) {
                        impl.notifyChildRebuilt();
                    }
                }
                if (a.mVisibleFromClient) {
                    if (!a.mWindowAdded) {
                        a.mWindowAdded = true;
                        // 4.2 重点在这里, WindowManager中添加了DecorView
                        // Activity的View和WindowManager交互
                        wm.addView(decor, l);
                    } else {
                        a.onWindowAttributesChanged(l);
                    }
                }
        ......
    }

在这个方法里,先调用了Activity的onResume方法,然后将DecorView添加至WindowManager中.

4.2 WindowManagerImpl.addView

    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        // 实际调用了WindowManagerGlobal.addView
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

这里实际调用了WindowManagerGlobal的addView方法, WindowManagerGlobal是一个单例,每个进程只有一个.
这个类保存当前进程所有的View, 当然也承担了和WindowManagerService通信交互的功能.

4.3 WindowManagerGlobal.addView

    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        ......
        ViewRootImpl root;
        View panelParentView = null;
        synchronized (mLock) {
            ......
            // 4.3.1 ViewRootImpl应用了装饰模式
            // 最重要的是建立了与WMS通信的通道
            root = new ViewRootImpl(view.getContext(), display);
            view.setLayoutParams(wparams);
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
            try {
                // 4.4 看样子最终的View给放到了ViewRootImpl里
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }

4.3.1 ViewRootImpl实例化

    public ViewRootImpl(Context context, Display display) {
        mContext = context;
        // 4.3.2 如果是进程第一次实例化ViewRootIml, 那么这里建立了此进程与WMS的通信通道
        mWindowSession = WindowManagerGlobal.getWindowSession();
        mDisplay = display;
        mBasePackageName = context.getBasePackageName();
        mThread = Thread.currentThread();
        mLocation = new WindowLeaked(null);
        mLocation.fillInStackTrace();
        mWidth = -1;
        mHeight = -1;
        mDirty = new Rect();
        mTempRect = new Rect();
        mVisRect = new Rect();
        mWinFrame = new Rect();
        mWindow = new W(this);
        mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
        mViewVisibility = View.GONE;
        mTransparentRegion = new Region();
        mPreviousTransparentRegion = new Region();
        mFirst = true; // true for the first time the view is added
        mAdded = false;
        mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,
                context);
        mAccessibilityManager = AccessibilityManager.getInstance(context);
        mAccessibilityManager.addAccessibilityStateChangeListener(
                mAccessibilityInteractionConnectionManager, mHandler);
        mHighContrastTextManager = new HighContrastTextManager();
        mAccessibilityManager.addHighTextContrastStateChangeListener(
                mHighContrastTextManager, mHandler);
        mViewConfiguration = ViewConfiguration.get(context);
        mDensity = context.getResources().getDisplayMetrics().densityDpi;
        mNoncompatDensity = context.getResources().getDisplayMetrics().noncompatDensityDpi;
        mFallbackEventHandler = new PhoneFallbackEventHandler(context);
        // Choreographer就不用多说了吧!
        mChoreographer = Choreographer.getInstance();
        mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
        if (!sCompatibilityDone) {
            sAlwaysAssignFocus = mTargetSdkVersion < Build.VERSION_CODES.P;
            sCompatibilityDone = true;
        }
        loadSystemProperties();
    }

ViewRootImpl的实例化主要是:

  1. 第一次实例化ViewRootIml时建立进程与WMS的通信通道4.3.2
  2. 初始化W
  3. 初始化Choreographer

再继续分析之前, 有必要先梳理下类之间的关系:
在这里插入图片描述
这里要重点区分紫色的这三个接口以及类W的作用. 这里就简单的概述下,方便接下来的分析.

  1. IWindowManager
    对应WindowManagerService, 应用进程端通过这个接口即可向WMS传递消息.
  2. IWindowSession
    通过这个Session和WMS中的Session交互.暂时还不理解这个作用
  3. W extends IWindow
    对应应用进程端的窗口, WMS通过这个向应用进程的Window发送消息.

一图以辅之:
在这里插入图片描述

4.3.2 WindowManagerGlobal.getWindowSession

    public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {
                    InputMethodManager imm = InputMethodManager.getInstance();
                    IWindowManager windowManager = getWindowManagerService();
                    sWindowSession = windowManager.openSession(
                            new IWindowSessionCallback.Stub() {
                                @Override
                                public void onAnimatorScaleChanged(float scale) {
                                    ValueAnimator.setDurationScale(scale);
                                }
                            },
                            imm.getClient(), imm.getInputContext());
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowSession;
        }
    }

    public static IWindowManager getWindowManagerService() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowManagerService == null) {
                sWindowManagerService = IWindowManager.Stub.asInterface(
                        ServiceManager.getService("window"));
                try {
                    if (sWindowManagerService != null) {
                        ValueAnimator.setDurationScale(
                                sWindowManagerService.getCurrentAnimatorScale());
                    }
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowManagerService;
        }
    }

4.4 ViewRootImpl.setView

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            ......
                if (view instanceof RootViewSurfaceTaker) {
                    // 这里传进来的是DecorView, 而DecorView是实现了RootViewSurfaceTaker接口的
                    // 这里的主要目的是提供给应用接口,使得应用可以自己实现渲染操作
                    mSurfaceHolderCallback =
                            ((RootViewSurfaceTaker)view).willYouTakeTheSurface();
                    if (mSurfaceHolderCallback != null) {
                        mSurfaceHolder = new TakenSurfaceHolder();
                        mSurfaceHolder.setFormat(PixelFormat.UNKNOWN);
                        mSurfaceHolder.addCallback(mSurfaceHolderCallback);
                    }
                }
            ......
                    // 4.5 通过WindowSession告诉WMS添加该Activity的Window
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
            ......
    }

这里最主要的功能是告诉WMS添加该Activity的窗口.

4.5 Session.addToDisplay

    @Override
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
            Rect outOutsets, DisplayCutout.ParcelableWrapper outDisplayCutout,
            InputChannel outInputChannel) {
        // mService也就是WindowManagerService
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel);
    }

4.6 WMS.addWindow

    public int addWindow(Session session, IWindow client, int seq,
            LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
        ......
        synchronized(mWindowMap) {
            ......
            AppWindowToken atoken = null;
            final boolean hasParent = parentWindow != null;
            WindowToken token = displayContent.getWindowToken(
                    hasParent ? parentWindow.mAttrs.token : attrs.token);
            final int rootType = hasParent ? parentWindow.mAttrs.type : type;
            boolean addToastWindowRequiresToken = false;
            if (token == null) {
                if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
                    Slog.w(TAG_WM, "Attempted to add application window with unknown token "
                          + attrs.token + ". Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                ...... // 区分各种窗口类型, 比如Toast
                if (type == TYPE_TOAST) {
                    if (doesAddToastWindowRequireToken(attrs.packageName, callingUid,
                            parentWindow)) {
                        Slog.w(TAG_WM, "Attempted to add a toast window with unknown token "
                                + attrs.token + ". Aborting.");
                        return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                    }
                }
                final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
                token = new WindowToken(this, binder, type, false, displayContent,
                        session.mCanAddInternalSystemWindow);
            }
            // 初始化WindowState
            final WindowState win = new WindowState(this, session, client, token, parentWindow,
                    appOp[0], seq, attrs, viewVisibility, session.mUid,
                    session.mCanAddInternalSystemWindow);
            ......
            // 4.7 关键点
            win.attach();
            // 保存WindowState
            mWindowMap.put(client.asBinder(), win);
        ......
    }

等等, WindowState又是啥? WindowToken,AppWindowToken有什么作用? 先看下相关的类图.
在这里插入图片描述

  1. WindowToken: 是在WindowManagerService 中定义的一个基类,顾名思义,它是用来标识某一个窗口。可以把WindowToken看成是一个显示令牌,无论是系统窗口还是应用窗口,添加新的窗口时需要使用这个令牌向WMS表明自己的身份,添加窗口(addWindow)时会创建WindowToken,销毁窗口的时候移除WindowToken(removeWindowToken方法)。
  2. AppWindowToken: 顾名思义,它是用来标识app, 跟准确的说法,是用来标识某个具体的Activity. App每个的Activity对应一個AppWindowToken。其中的appToken為IApplicationToken类型,连接对应AMS中的ActivityRecord::Token对象,有了它就可以顺者AppWindowToken找到AMS中相应的ActivityRecord。

但是多个WindowState可以公用同一个WindowToken. WMS使用WindowToken将同一个应用的组件(Activity,InputMethod,Wallpaper,Dream)的窗口组织在一起,换句话说,每一个窗口都会对应一个WindowToken,并且这个窗口中的所有子窗口将会对应同一个WindowToken. 如下图:
在这里插入图片描述

4.7 WindowState.attach

    void attach() {
        if (localLOGV) Slog.v(TAG, "Attaching " + this + " token=" + mToken);
        // mSession就是Session对象
        mSession.windowAddedLocked(mAttrs.packageName);
    }

Session是继承了IWindowSession.Stub的,是WMS和其他进程交互的通道.

4.8 Session.windowAddedLocked

    void windowAddedLocked(String packageName) {
        mPackageName = packageName;
        mRelayoutTag = "relayoutWindow: " + mPackageName;
        if (mSurfaceSession == null) {
            // 最主要的就是创建SurfaceSession, 这个用来和SurfaceFlinger通信
            mSurfaceSession = new SurfaceSession();
            mService.mSessions.add(this);
            if (mLastReportedAnimatorScale != mService.getCurrentAnimatorScale()) {
                mService.dispatchNewAnimatorScaleLocked(this);
            }
        }
        mNumWindow++;
    }

最终, handleResumeActivity的流程会建立:

  1. WMS与App进程通信的双向通道, W和Session
  2. ViewRootIml通过addView, 向WMS发送addWindow的消息, 最终进入WMS的Session建立与SurfaceFlinger通信的通道–SurfaceSession
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值