在Android系统中,一个Activity的Window创建和添加过程

移动开发 Android
addToDisplay​方法内部调用了mService的addWindow​方法,并将Session​对象本身作为第一个参数传进去。mService​就是WMS​的实例,每一个app进程​都会对应一个Session对象用来表示app进程与WMS的通信渠道。

在Android系统中,一个Activity通常就表示一个页面,这个页面实际是由Window来管理的,每个Activity都对应着一个Window。Window是一个抽象类,具体实现类是PhoneWindow,对View进行管理。确切的来讲是,PhoneWindow包含一个DecorView类型的成员,代表这个Window的顶层View,是一个FrameLayout。DecorView的布局结构包含两部分:标题栏(title)和内容栏(content)。根据设置的主题不同,这两部分也会有不同的呈现。但内容栏是一定存在的,并且id是固定的android.R.id.content。

Window的创建过程

Window的创建过程是一个涉及多个层次和组件的复杂过程。

  • 当启动一个Activity或系统通过Intent触发一个Activity时,这个Activity的生命周期开始。
  • onCreate()方法被调用,这是Activity生命周期中的一个重要回调。
  • 在onCreate()方法中,通常会调用setContentView()来设置Activity的布局。
  • setContentView()方法会触发Window对象的创建。在Android中,每个Activity都与一个Window对象相关联。
  • Window对象通常是一个PhoneWindow的实例,用于处理与窗口相关的各种功能。

Activity启动过程在ActivityThread的performLaunchActivity方法,会调用Activity的attach方法。与Activity相关联的Window对象就是在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);
    ...
    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);
    }
    ...
    mWindow.setWindowManager(
            (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
            mToken, mComponent.flattenToString(),
            (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
     ...

    mWindow.setColorMode(info.colorMode);
}

在attach方法中创建了一个PhoneWindow对象,并设置回调接口Callback和WindowManager。由于Activity类实现了Window.Callback接口,当Window接收到相关的事件触发时就会调用Activity的相应方法。Callback接口中的方法很多,有几个是我们比较常见的,比如dispatchTouchEvent、onAttachedToWindow、onDetachedFromWindow等。

Window的添加过程

  • 与Window对象关联的是一个DecorView。DecorView是一个特殊的ViewGroup,包含了窗口的标题栏(如果有的话)和主要内容区域。
  • DecorView的创建是在Window的创建过程中自动完成的,并且与Window对象紧密相关。
  • 通过setContentView()方法加载的布局文件(通常是XML文件)会被解析并转换为相应的View对象。
  • 这些View对象被添加到DecorView的内容区域中,形成一个View树。
  • 绘制的结果被渲染到屏幕上,就能看到Activity的界面了。

ActivityThread的performLaunchActivity方法:

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ...
        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);
        ...
        if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
}

在Activity的attach方法返回后,程序会调用mInstrumentation.callActivityOnCreate方法,而这个方法最终会触发Activity的onCreate回调。而在onCreate中,会调用setContentView方法,开始Activity的Window添加过程。

public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}

Activity的setContentView方法内部调用的mWindow的setContentView方法,这个mWindow对象就是在attach方法中创建的PhoneWindow对象。

public void setContentView(int layoutResID) {
    if (mContentParent == null) {
        installDecor();
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        mContentParent.removeAllViews();
    }


    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                getContext());
        transitionTo(newScene);
    } else {
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }
    mContentParent.requestApplyInsets();
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged();
    }
    mContentParentExplicitlySet = true;
}

PhoneWindow的setContentView中,首先判断mContentParent是否存在,否则调用installDecor方法。这个mContentParent指的就是DecorView的内容栏。它的赋值就只有一个地方,就是在installDecor方法中。

private void installDecor() {
    mForceDecorInstall = false;
    if (mDecor == null) {
        mDecor = generateDecor(-1);
        mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
        mDecor.setIsRootNamespace(true);
        if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
            mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
        }
    } else {
        mDecor.setWindow(this);
    }
    if (mContentParent == null) {
        mContentParent = generateLayout(mDecor);
    ...
}

如果mDecor为null,就先调用generateDecor方法创建DecorView。

protected DecorView generateDecor(int featureId) {
    ...
    return new DecorView(context, featureId, this, getAttributes());
}

DecorView对象创建之后,再判断mContentParent对象是否存在,不存在调用generateLayout方法。

protected ViewGroup generateLayout(DecorView decor) {
    ...
    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    ...
    return contentParent;
}

generateLayout会返回一个ViewGroup对象contentParent,ID_ANDROID_CONTENT就是com.android.internal.R.id.content。

最后调用mLayoutInflater.inflate(layoutResID, mContentParent)方法,将Activity的布局视图添加到mContentParent中,回调Activity的onContentChanged方法通知Activity视图已经发生改变。

这个时候,Activity的视图布局还没有显示出来,DecorView还没有被WindowManager正式添加到窗口中。

在Activity执行onResume方法之后视图才能完全显示,并和用户正常交互,onResume方法是在ActivityThread的handleLaunchActivity方法中回调。

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
    ...
    Activity a = performLaunchActivity(r, customIntent);
    if (a != null) {
    r.createdConfig = new Configuration(mConfiguration);
    reportSizeConfigurations(r);
    Bundle oldState = r.state;
    handleResumeActivity(r.token, false, r.isForward,
            !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
    ...
}

执行完performLaunchActivity方法返回一个Activity的实例,接下来判断如果创建的Activity实例不为null,就会执行handleResumeActivity方法。

final void handleResumeActivity(IBinder token,
        boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
    ...
    r = performResumeActivity(token, clearHide, reason);
    if (r != null) {
        final Activity a = r.activity;
        ...
        if (r.activity.mVisibleFromClient) {
            r.activity.makeVisible();
        }
    ...
}

在handleResumeActivity方法中,会调用performResumeActivity方法,经过层层调用最终会回调Activity的onResume方法。

handleResumeActivity中,在performResumeActivity方法执行之后,会调用Activity的makeVisible方法。

void makeVisible() {
    if (!mWindowAdded) {
        ViewManager wm = getWindowManager();
        wm.addView(mDecor, getWindow().getAttributes());
        mWindowAdded = true;
    }
    mDecor.setVisibility(View.VISIBLE);
}

在makeVisible方法中,会调用WindowManager的addView方法,将DecorView正式添加到窗口中,同时DecorView设置为可见。

@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}

在WindowManagerImpl的addView方法内部,调用的是WindowManagerGlobal的addView方法。WindowManagerImpl通过桥接模式,将功能实现委托给了WindowManagerGlobal。WindowManagerGlobal是一个单例,说明一个进程中只有一个WindowManagerGlobal实例。每一个Window都会有一个相关联的WindowManagerImpl实例。

public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow) {
    if (view == null) {
        throw new IllegalArgumentException("view must not be null");
    }
    if (display == null) {
        throw new IllegalArgumentException("display must not be null");
    }
    if (!(params instanceof WindowManager.LayoutParams)) {
        throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
    }
    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;
    ...
        int index = findViewLocked(view, false);
        if (index >= 0) {
            if (mDyingViews.contains(view)) {
                // Don't wait for MSG_DIE to make it's way through root's queue.
                mRoots.get(index).doDie();
            } else {
                throw new IllegalStateException("View " + view
                        + " has already been added to the window manager.");
            }
            // The previous removeView() had not completed executing. Now it has.
        }
        if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
            final int count = mViews.size();
            for (int i = 0; i < count; i++) {
                if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                    panelParentView = mViews.get(i);
                }
            }
        }
        root = new ViewRootImpl(view.getContext(), display);
        view.setLayoutParams(wparams);
        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);
        try {
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
            // BadTokenException or InvalidDisplayException, clean up.
            if (index >= 0) {
                removeViewLocked(index, true);
            }
            throw e;
        }
    }
}

WindowManagerGlobal的addView方法主要完成三个步骤:

  • 检查参数是否合法,如果是子Window还需要调整一些布局参数
  • 创建ViewRootImpl,并将传进来的View添加到mViews列表里
  • 通过ViewRootImpl来更新界面并完成Window的添加过程。

最终调用了root.setView(view, wparams, panelParentView)方法。

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    ...
    requestLayout();
    ...
    try {
    mOrigWindowType = mWindowAttributes.type;
    mAttachInfo.mRecomputeGlobalAttributes = true;
    collectViewAttributes();
    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
            getHostVisibility(), mDisplay.getDisplayId(),
            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
            mAttachInfo.mOutsets, mInputChannel);
    ...
}

View树被构建完成,系统会遍历这个树,对每个View进行测量和布局,调用requestLayout方法会触发View层级的绘制遍历,requestLayout方法内部会调用scheduleTraversals方法。scheduleTraversals方法实际就是View绘制过程的入口。

然后会调用mWindowSession对象的addToDisplay方法,mWindowSession的类型是IWindowSession,是一个Binder对象,用于进程间通信,IWindowSession是Client端的代理。Server端实现是Session。代码都是运行在Activity所在的app进程,Session的addToDisplay方法则是运行在WMS所在的SystemServer进程中。

图片图片

@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
        int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
        Rect outOutsets, InputChannel outInputChannel) {
    return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
            outContentInsets, outStableInsets, outOutsets, outInputChannel);
}

addToDisplay方法内部调用了mService的addWindow方法,并将Session对象本身作为第一个参数传进去。mService就是WMS的实例,每一个app进程都会对应一个Session对象用来表示app进程与WMS的通信渠道。WMS会用ArrayList来存放这些Session对象。WMS会为这个要添加的窗口分配Surface,并确定窗口的显示次序,真正负责显示界面视图的是画布Surface而不是窗口本身。WMS会将所管理的Surface交由SurfaceFlinger处理,SurfaceFlinger会将这些Surface混合并绘制并最终呈现到屏幕上。

责任编辑:武晓燕 来源: 沐雨花飞蝶
相关推荐

2023-03-15 09:00:43

SwiftUISlider

2015-08-06 13:44:21

swiftcocoapods

2011-07-19 17:05:22

Xcode Libary

2024-01-29 10:20:39

Obsidian链接

2010-01-11 17:21:26

Linux Fedor

2020-09-29 07:24:14

Python字典数据

2010-04-15 17:45:26

Oracle存储过程

2011-03-10 10:45:47

Azure“Hello Worl

2009-09-22 11:54:42

ibmdwPHP

2018-09-08 09:05:00

UbuntuLinuxIP地址

2012-11-21 11:48:23

i-NVMM加密密码

2022-03-30 08:19:12

JavaGroovy

2022-02-10 22:34:51

对象JVM收集器

2011-04-08 10:29:04

AccessMIS管理系统

2020-12-16 14:29:40

终端开发shell

2011-07-20 09:16:02

MongoDB索引稀疏索引

2011-07-29 13:21:31

CTE递归存储过程真分页

2018-11-05 15:14:42

MySQL编程语言技术

2017-03-13 14:30:38

Android开发库指南

2011-09-08 10:46:12

Widget
点赞
收藏

51CTO技术栈公众号