深入理解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/...

相关推荐
风槐啊37 分钟前
六、Java 基础语法(下)
android·java·开发语言
皮皮虾在睡觉2 小时前
数据库基础04
android·数据库
彭于晏6894 小时前
Android高级控件
android·java·android-studio
666xiaoniuzi9 小时前
深入理解 C 语言中的内存操作函数:memcpy、memmove、memset 和 memcmp
android·c语言·数据库
沐言人生13 小时前
Android10 Framework—Init进程-8.服务端属性文件创建和mmap映射
android
沐言人生14 小时前
Android10 Framework—Init进程-9.服务端属性值初始化
android·android studio·android jetpack
沐言人生14 小时前
Android10 Framework—Init进程-7.服务端属性安全上下文序列化
android·android studio·android jetpack
追光天使14 小时前
【Mac】和【安卓手机】 通过有线方式实现投屏
android·macos·智能手机·投屏·有线
小雨cc5566ru14 小时前
uniapp+Android智慧居家养老服务平台 0fjae微信小程序
android·微信小程序·uni-app