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

相关推荐
帅得不敢出门3 小时前
Android Framework预装traceroute执行文件到system/bin下
android
xzkyd outpaper3 小时前
从面试角度回答Android中ContentProvider启动原理
android·面试·计算机八股
编程乐学3 小时前
基于Android 开发完成的购物商城App--前后端分离项目
android·android studio·springboot·前后端分离·大作业·购物商城
这个家伙很笨7 小时前
了解Android studio 初学者零基础推荐(4)
android·ide·android studio
alexhilton9 小时前
在Android应用中实战Repository模式
android·kotlin·android jetpack
二流小码农14 小时前
鸿蒙开发:DevEcoTesting中的稳定性测试
android·ios·harmonyos
一起搞IT吧14 小时前
相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
android·图像处理·数码相机
xzkyd outpaper14 小时前
Android中ContentProvider细节
android·计算机八股
恋猫de小郭14 小时前
Flutter 多版本管理工具 Puro ,它和 FVM 有什么区别?
android·前端·flutter