Activity 和 Window 的关系

背景

 在对 Activity 的启动分析中,我们看到了 Activity 是如何和 Window 产生关联的

//ActivityThread 中的方法
final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
      ...
       r.window = r.activity.getWindow();
       View decor = r.window.getDecorView();
       decor.setVisibility(View.INVISIBLE);
       //这里的 a 就是 Activity,即 r.activity
       ViewManager wm = a.getWindowManager();
       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;
                    // Normally the ViewRoot sets up callbacks with the Activity
                    // in addView->ViewRootImpl#setView. If we are instead reusing
                    // the decor view we have to notify the view root that the
                    // callbacks may have changed.
                    ViewRootImpl impl = decor.getViewRootImpl();
                    if (impl != null) {
                        impl.notifyChildRebuilt();
                    }
       }
       if (a.mVisibleFromClient && !a.mWindowAdded) {
                    a.mWindowAdded = true;
                    //和UI部分挂钩了,非常重要的一个步骤,涉及到和wms、sf之间的通信。
                    wm.addView(decor, l);
       }
      ...
}

 如上所示,本质上 Activity 中布局,是通过 WindowManger 的 addView 设置进去的。下面我们就来具体分析一下这个过程。在此之前,我们先用一张图看一下它们之间的整体包含关系与通信,有一个大体上的了解。

Activity、Window以及WMS之间的关系

Window 的创建及作用

 Window 的创建,是在 Activity 中的 attach 进行的。

//Activity 中的方法
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) {
//注意,这就是为什么 attach 之后才可以调用 context 的方法
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);
//创建 Window
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
       ......
        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());
        }
        mWindowManager = mWindow.getWindowManager();
        mCurrentConfig = config;

        mWindow.setColorMode(info.colorMode);
    }

 由上可知,Activity 中创建了 Window,并且每个 Activity 都有一个与之对应的Window ,具体的实现类,其实是一个 PhoneWindow。Android之setContentView 这篇文章中,我们分析了 PhoneWindow 负责对整个布局的管理,它提供了一个模板,并把我们的布局 set 进这个模板中id 为 content 的控件中。

布局层次关系

 上图中的 DecorView 是在 PhoneWindow 中生成的,也就是最终显示给用户的 UI。回到背景中介绍的,系统最终将它从 Window 中取出,并通过 WindowManager 进行了 add 操作。那么,接下来,先不急着看 addView 方法,我们先来认识一下这里的 WindowManager。

 我们看到,Window 会为自己 set 一个 WindowManager,而这个 WindowManager,实际上,是
一个 WindowManagerImpl,从上面代码块中,我们可以看到它的构建过程

 mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);

//Window 中的方法
 public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        ......
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }
  //WindowMangerImpl 中的方法
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mContext, parentWindow);
    }

 即,通过getSystemService(Context.WINDOW_SERVICE) 方法得到的,WindowManagerImpl中的方法,是交给其内部的单例 WindowManagerGlobal 实现的。每个应用进程只有一个WindowManagerGlobal,而每个 Activity 或者说每个 Window
都会有一个 WindowManagerImpl。
现在,我们将焦点聚焦在了 WinodowManagerGlobal 的 addView 方法上。

addView 方法

 Ok, 现在我们可以来看看 addView 方法了。

//WindowManagerGlobal.java文件中
//保存了每个 Window 所对应的 view,也就是 decorVIew
private final ArrayList<View> mViews = new ArrayList<View>();
//保存了每个 Window 所对应的 ViewRootImpl
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
//保存了每个 Window 所对应的布局参数
private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>();
private final ArraySet<View> mDyingViews = new ArraySet<View>();

//这里的 view 实际上是 decorView
public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        ......
        ViewRootImpl root;
        View panelParentView = null;
        ......
        root = new ViewRootImpl(view.getContext(), display);
        view.setLayoutParams(wparams);
        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);
        ......
        root.setView(view, wparams, panelParentView);
}

WindowManagerGlobal 中维护这一些数据结构。其中,每个 Window 对应一个 ViewRootImpl。而addView 实际上将调用 ViewRootImpl 的 setView 方法

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
......

   res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
......   
}
调用过程

包含关系是:WindowManagerImpl 存在成员变量 WindowManagerGlobal; WindownManagerGlobal 包含成员变量 ViewRootImpl;ViewRootImpl 包含成员变量 IWindowSession。
 IWindowSession 虽然是 ViewRootImpl 的成员变量,是实际上是在 ViewManagerGlobal 的方法中实例化的。

//ViewManagerGlobal 中的方法
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;
        }
    }

 由上可知,每个进程的 Session 也是唯一的。IWindowSession 是一个接口, 实际上是 WindowManager 调用 openSession 之后返回的是一个实现类 Session。这个返回过程实际上是一个跨进程调用的过程。每次 getWindowSession 返回的都是服务端同一个 Session。

//Session  中的方法
 @Override
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
            Rect outOutsets, InputChannel outInputChannel) {
//这个 mService 是 WindowManagerService
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                outContentInsets, outStableInsets, outOutsets, outInputChannel);
    }

 追究其本质,现在又回到了 WindowManagerService 中。它的 addWindow 又是什么呢?

  public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            InputChannel outInputChannel) {
        ......
//Window 不能重复添加
        if (mWindowMap.containsKey(client.asBinder())) {
                Slog.w(TAG_WM, "Window " + client + " is already added");
                return WindowManagerGlobal.ADD_DUPLICATE_ADD;
            }
//如果是子窗口,取值在 FIRST_SUB_WINDOW 和 LAST_SUB_WINDOW 之间
        if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
                attachedWindow = windowForClientLocked(null, attrs.token, false);
                //子窗口必须附着在一个 Window 上
                if (attachedWindow == null) {
                    Slog.w(TAG_WM, "Attempted to add window with token that is not a window: "
                          + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
                }
                //子窗口所附着的窗口不能是一个子窗口
                if (attachedWindow.mAttrs.type >= FIRST_SUB_WINDOW
                        && attachedWindow.mAttrs.type <= LAST_SUB_WINDOW) {
                    Slog.w(TAG_WM, "Attempted to add window with token that is a sub-window: "
                            + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
                }
            }
        ......
        WindowState win = new WindowState(this, session, client, token,
                    attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
        ......
//接下来,就是 WMS 向 SurfaceFlinger 申请 Surface,然后传回给 Activity 使用的 
        win.attach();
        ......

小结

 本节到此为止,也算是对以前所写的 activity 启动分析、setContentView 分析等相关内容的一个串联。WMS 和 SurfaceFlinger 之间,是如何申请 Surface、又是如何回传和 App 的内容,目前还没有展开;还有就是这其中事件的传递机制是如何展开的,这两部分可以作为后续章节。

参考链接:
http://gityuan.com/2017/04/16/activity-with-window/
https://www.jianshu.com/p/40776c123adb
http://blog.csdn.net/yhaolpz/article/details/68936932

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容