深入理解Activity的显示原理(2)

前面讲了onCreat中具体做了什么 深入理解Activity的显示原理(1)

接下来看看onResume生命周期中做了什么

Activity.onResume

ActivityThread.java

Java 复制代码
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
        String reason) {
    ...
    final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
    ...
    if (r.window == null && !a.mFinished && willBeVisible) {
        r.window = r.activity.getWindow();
        View decor = r.window.getDecorView();
        decor.setVisibility(View.INVISIBLE);
        ViewManager wm = a.getWindowManager();
        ...
        //1.拿到Activity持有的mWindowManager后调用addView
        wm.addView(decor, l);
    }
    if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {
        ...
        if (r.activity.mVisibleFromClient) {
            //2.
            r.activity.makeVisible();
        }
    }
    ...
}

    ActivityThread 会根据请求创建管理着四大组件的生命周期方法的调用,onResume生命周期是在ActivityThread的handleResumeActivity方法中去调用的。在这个方法中先是通过performResumeActivity的方式触发了activity的回调,然后判断了activity未关闭而且要显示时,会取出activity的DecorView,把DecorView添加到ViewManager。这个ViewManager其实是Activity持有的一个WindowManager。

    最后调用activity的makeVisible方法,把activity设置为可见的。其实makeVisiblel方法所做的仅仅是把activity的DecorView设置为可见状态,这并不足以支撑完成全部的绘制工作,只是触发了一次重绘。所以重点还是要分析addView具体是怎么操作的。

ViewManager.addView

源码
Java 复制代码
//Activity.java
private WindowManager mWindowManager;

final void attach() {

    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    //获取WindowManager
    mWindowManager = mWindow.getWindowManager();
    ...
}

--------------------
//Windows.java
public WindowManager getWindowManager() {
    return mWindowManager;
}

public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
        boolean hardwareAccelerated) {
    mAppToken = appToken;
    mAppName = appName;
    mHardwareAccelerated = hardwareAccelerated;
    if (wm == null) {
        wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
    }
    mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}

---------------------
//WindowManagerImpl.java
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();

@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {

    ...
    applyDefaultToken(params);
    mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}

    每个activity在初始化的时候都会持有一个WindowManager。而且是通过每个activity自己的phonewindow的getWindowManager方法来获取的。用于管理Window上的View,包括addViewremove

    在Windows.java可以看到用来addView的WindowManager是通过createLocalWindowManager方法自己创建的一个WindowManagerImpl实例。所以接着看WindowManagerImpl类的addView方法,发现最后是调用了WindowManagerGlobal的addView方法。

Java 复制代码
//WindowManagerGlobal.java
public static WindowManagerGlobal getInstance() {
    synchronized (WindowManagerGlobal.class) {
        if (sDefaultWindowManager == null) {
            sDefaultWindowManager = new WindowManagerGlobal();
        }
        return sDefaultWindowManager;
    }
}

public void addView(View view, ViewGroup.LayoutParams params,
    Display display, Window parentWindow) {
    ...
    root = new ViewRootImpl(view.getContext(), display);

    view.setLayoutParams(wparams);

    mViews.add(view);
    mRoots.add(root);
    mParams.add(wparams);
    
    ...
    root.setView(view, wparams, panelParentView);
}

    WindowManagerGlobal是个单例。在调用addView方法之后,如果是首次添加的情况,会创建一个ViewRootImpl实列root,并且把view和root缓存到单例的属性中,最后再调用setView方法。

ViewRootImpl.setView

Java 复制代码
final W mWindow;

View mView;

final IWindowSession mWindowSession;

static class W extends IWindow.Stub {}

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    if (mView == null) {
        mView = view;
        ...
        requestLayout();//触发布局和绘制
        ...
        res = mWindowSession.addToDisplay(mWindow, ...);//通知WMS添加窗口
        ...
        view.assignParent(this);
    }
}

    mView为空的时候,调用requestLayout方法来触发布局和绘制。

    addToDisplay这个方法的作用就是通知WMS添加窗口。调用WMS肯定是跨进程通信,参数中的mWindow是一个W类型的静态内部类,这个类继承了IWindow.Stub用来和WMS进行双向通信。这里用到的mWindowSession,同样是一个跨进程通信的句柄,是一个Binder服务代理,是App端向WMS发送消息的通道。

    在方法的最后,调用view的assignParent方法,这个View就是我们上文的DecorView,把decorView的parent设置为了ViewRootImpl。这样做的目的就是让ViewRootImpl能够管理整个viewTree。

    总结一下到目前为止的流程。首先在Activity的attach过程中,除了创建了phoneWindow,还为activity创建了一个WindowManager,以便管理整个phoneWindow。在onResume生命周期中,会调用WindowManager的addView添加decorView。那么当WindowManager管理viewTree的时候,会给viewTree分配一个ViewRootImpl。ViewRootImpl的职责就是管理viewTree的绘制工作,包括viewTree的显示、测量、同步刷新以及事件分发等等,和负责与其他的服务进行通信。

WMS通信

    在多个 Activity 同时存在的情况下,每个 Activity 都有自己独立的窗口相关对象,包括 PhoneWindow、DecorView 和 WindowManagerImpl。这样可以确保每个 Activity 的窗口操作相互独立,互不干扰。WindowManagerGlobal作为一个全局的单例,持有每个activity的rootView ,mWindowSession和mWindow分别是和WMS进行双向通信的句柄。

总结

    Activity的attach方法中会初始化PhoneWindow以及WindowManager,setContentView方法中创建了DecorView,并且把我们自己实现的布局转化成了viewTree挂载在了DecorView的contentParent下面,形成了一个完整的ViewTree。

    最后activity的onResume生命周期里,通过addView的方式创建了ViewRootImpl,并且用它来管理viewTree的绘制工作。ViewRootImpl可以理解为"View树的管理者"------它有一个mView成员变量,其指向Window和Activity中共同拥有的mDecor对象,即View树的根DecorView。windowSession以及mWindow是用来和WMS进行双向通信的。最后再通过requestLayout显示activity中的内容。

juejin.cn/post/684490...

mp.weixin.qq.com/s/iMQ5xF7K3...

blog.51cto.com/u_16113862/...

相关推荐
千里马学框架10 小时前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台10 小时前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone10 小时前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc11 小时前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo13 小时前
Kotlin 协程中的 Job 结构化并发与取消
android
sun00770013 小时前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼14 小时前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone15 小时前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen15 小时前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone15 小时前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui