Framework篇 - 从 WindowManageService 的角度来分析 Activity 的窗口

本文源代码基于 Android 7.0。

  • /base/services/core/java/com/android/server/am/ActivityStack.java
  • /base/services/core/java/com/android/server/wm/WindowManagerService.java
  • /base/services/core/java/com/android/server/am/ActivityRecord.java
  • /base/services/core/java/com/android/server/policy/PhoneWindowManager.java
  • /base/core/java/android/view/WindowManagerImpl.java
  • /base/core/java/android/view/WindowManagerGlobal.java
  • /base/core/java/android/view/ViewRootImpl.java
  • /base/services/core/java/com/android/server/wm/Session.java

WindowManageService 运行于 system_server 进程,这篇文章从 WindowManageService 的角度来分析 Activity 的窗口。

 

目录:

  1. 预览窗口启动过程
  2. WindowManager 的 addView() 和 RemoveView()
  3. 总结

 

 

1. 预览窗口启动过程

Activity 启动后,窗口并不是马上就显示出来,而是先显示 starting window,作为 Activity 的预览窗口。

 

  • 1.1 ActivityStack.startActivityLocked()
final void startActivityLocked(ActivityRecord r, boolean newTask, boolean keepCurTransition,
            ActivityOptions options) {
        TaskRecord rTask = r.task;
        final int taskId = rTask.taskId;
        if (!r.mLaunchTaskBehind && (taskForIdLocked(taskId) == null || newTask)) {
            insertTaskAtTop(rTask, r);

            // 将Window相应task移至顶部,调用WindowManagerService
            mWindowManager.moveTaskToTop(taskId);
        }
        TaskRecord task = null;
        if (!newTask) {
            boolean startIt = true;
            for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
                task = mTaskHistory.get(taskNdx);
                if (task.getTopActivity() == null) {
                    continue;
                }
                if (task == r.task) {
                    if (!startIt) {
                        task.addActivityToTop(r);
                        r.putInHistory();
                        // 创建AppWindowToken对象
                        addConfigOverride(r, task);
                        if (VALIDATE_TOKENS) {
                            validateAppTokensLocked();
                        }
                        ActivityOptions.abort(options);
                        return;
                    }
                    break;
                } else if (task.numFullscreen > 0) {
                    startIt = false;
                }
            }
        }

        task = r.task;
        task.addActivityToTop(r);
        task.setFrontOfTask();

        r.putInHistory();
        if (!isHomeStack() || numActivities() > 0) {
            // ...
            // 准备window动画,进入,移除动画等
            if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
                mWindowManager.prepareAppTransition(TRANSIT_NONE, keepCurTransition);
                mNoAnimActivities.add(r);
            } else {
                mWindowManager.prepareAppTransition(newTask
                        ? r.mLaunchTaskBehind
                                ? TRANSIT_TASK_OPEN_BEHIND
                                : TRANSIT_TASK_OPEN
                        : TRANSIT_ACTIVITY_OPEN, keepCurTransition);
                mNoAnimActivities.remove(r);
            }
            addConfigOverride(r, task);
            boolean doShow = true;
            if (newTask) {
                if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
                    resetTaskIfNeededLocked(r, r);
                    doShow = topRunningNonDelayedActivityLocked(null) == r;
                }
            } else if (options != null && options.getAnimationType()
                    == ActivityOptions.ANIM_SCENE_TRANSITION) {
                doShow = false;
            }
            if (r.mLaunchTaskBehind) {
                mWindowManager.setAppVisibility(r.appToken, true);
                ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
            } else if (SHOW_APP_STARTING_PREVIEW && doShow) {
                // ...
                // 展示 startingWindow
                r.showStartingWindow(prev, showStartingIcon);
            }
        } else {
            // ...
       
    }
  • 将该 Activity 所对应的 AMS 和 WMS 的 task,分别移至栈顶;
  • 从 mTaskHistory 获取该 Activity 所属的 task;
  • 当 Activity 所属进程没有启动,则需要显示启动窗口,即 showStartingIcon=true;
  • 创建 AppWindowToken,并添加到 WMS 的 mTokenMap;
  • 设置启动窗口 (StartingWindow),r 为 ActivityRecord 类型;
  • 恢复栈顶部的 Activity。

 

  • 1.2 ActivityRecord.showStartingWindow()
    void showStartingWindow(ActivityRecord prev, boolean createIfNeeded) {
        final CompatibilityInfo compatInfo =
                service.compatibilityInfoForPackageLocked(info.applicationInfo);
        // 最终会调用 WindowManagerService 的 setAppStartingWindow()
        final boolean shown = service.mWindowManager.setAppStartingWindow(
                appToken, packageName, theme, compatInfo, nonLocalizedLabel, labelRes, icon,
                logo, windowFlags, prev != null ? prev.appToken : null, createIfNeeded);
        if (shown) {
            mStartingWindowState = STARTING_WINDOW_SHOWN;
        }
    }

 

  • 1.3 WindowManagerService.setAppStartingWindow()
    @Override
    public boolean setAppStartingWindow(IBinder token, String pkg,
            int theme, CompatibilityInfo compatInfo,
            CharSequence nonLocalizedLabel, int labelRes, int icon, int logo,
            int windowFlags, IBinder transferFrom, boolean createIfNeeded) {
        
        synchronized(mWindowMap) {
            // 从mTokenMap中查找相应的Token(appWindowToken)
            AppWindowToken wtoken = findAppWindowToken(token);
            if (wtoken == null) {
                // 当app的token为空,则直接返回
                return false;
            }
            if (!okToDisplay()) {
                // 当屏幕处于冻结状态,则直接返回
                return false;
            }

            if (wtoken.startingData != null) {
                // 当启动数据不为空,则无需再次设置
                return false;
            }
           
            if (transferStartingWindow(transferFrom, wtoken)) {
                return true;
            }
            if (!createIfNeeded) {
                return false;
            }

            // 创建启动数据,并发送消息到"android.display"线程来处理ADD_STARTING消息
            wtoken.startingData = new StartingData(pkg, theme, compatInfo, nonLocalizedLabel,
                    labelRes, icon, logo, windowFlags);
            // 加入消息队列的头部,需要立马执行
            Message m = mH.obtainMessage(H.ADD_STARTING, wtoken);
            mH.sendMessageAtFrontOfQueue(m);
        }
        return true;
    }

(1) 当上一个显示 Activity 的 ttoken 不为空的情况,有两种 case:

  • 当 ttoken 的 startingWindow 和 startingView 都不为空,则将该启动窗口信息转移到新的 token,然后调用performLayoutAndPlaceSurfacesLocked() 进行布局和刷新 UI,则完成并返回;
  • 当 ttoken 的 startingData 不为空 (上一个 activity 启动窗口准备好,但没有完成),则借用该 ttoken 启动数据,并发送消息到 "android.display" 线程来处理 ADD_STARTING 消息,则完成并返回。

(2) 当不存在启动窗口,且发起者不希望创建,则直接返回。
(3) 当主题 theme 不为空,主题满足以下任一情况,则都会直接返回:

  • 该主题实体为空;
  • 半透明;
  • 浮动窗口;
  • 需要显示壁纸。

最后,创建启动数据,并发送消息到 "android.display" 线程来处理 ADD_STARTING 消息。

 

  • 1.4 WindowManagerService.H.handleMessage()
final class H extends Handler {
        @Override
        public void handleMessage(Message msg) {
            case ADD_STARTING: {
                    final AppWindowToken wtoken = (AppWindowToken)msg.obj;
                    final StartingData sd = wtoken.startingData;
                    if (sd == null) {
                        // 动画被取消则返回
                        return;
                    }
                    View view = null;
                    try {
                        final Configuration overrideConfig = wtoken != null && wtoken.mTask != null
                                ? wtoken.mTask.mOverrideConfig : null;
                        view = mPolicy.addStartingWindow(wtoken.token, sd.pkg, sd.theme,
                            sd.compatInfo, sd.nonLocalizedLabel, sd.labelRes, sd.icon, sd.logo,
                            sd.windowFlags, overrideConfig);
                    } catch (Exception e) {
                        Slog.w(TAG_WM, "Exception when adding starting window", e);
                    }

                    if (view != null) {
                        boolean abort = false;
                        synchronized(mWindowMap) {
                            // 当创建已被移除或者转移给其他activity,则需要移除它
                            if (wtoken.removed || wtoken.startingData == null) {
                                if (wtoken.startingWindow != null) {
                                    wtoken.startingWindow = null;
                                    wtoken.startingData = null;
                                    abort = true;
                                }
                            } else {
                                wtoken.startingView = view;
                            }
                        }

                        if (abort) {
                            try {
                                // 此处mPolicy为PhoneWindowManager
                                mPolicy.removeStartingWindow(wtoken.token, view);
                            } catch (Exception e) {
                            }
                        }
                    }
                } break;
        }
}

关键一句在于 mPolicy.addStartingWindow(),mPolicy 在这边的实现为 PhoneWindowManager。

 

  • 1.5 PhoneWindowManager.addStartingWindow()
 @Override
    public View addStartingWindow(IBinder appToken, String packageName, int theme,
            CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
            int icon, int logo, int windowFlags, Configuration overrideConfig) {
        if (!SHOW_STARTING_ANIMATIONS) {
            return null;
        }
        if (packageName == null) {
            return null;
        }

        WindowManager wm = null;
        View view = null;

        try {
            Context context = mContext;
            // 设置主题
            if (theme != context.getThemeResId() || labelRes != 0) {
                try {
                    context = context.createPackageContext(packageName, 0);
                    context.setTheme(theme);
                } catch (PackageManager.NameNotFoundException e) {
                    // Ignore
                }
            }
            //...
            // 创建 PhoneWindow
            final PhoneWindow win = new PhoneWindow(context);
            win.setIsStartingWindow(true);
            CharSequence label = context.getResources().getText(labelRes, null);
            // 设置窗体标题
            if (label != null) {
                win.setTitle(label, true);
            } else {
                win.setTitle(nonLocalizedLabel, false);
            }

            // 设置窗口类型为启动窗口类型
            win.setType(
                WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);

            synchronized (mWindowManagerFuncs.getWindowManagerLock()) {
                if (mKeyguardHidden) {
                    windowFlags |= FLAG_SHOW_WHEN_LOCKED;
                }
            }
            // 设置不可触摸和聚焦
            win.setFlags(
                windowFlags|
                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE|
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
                WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
                windowFlags|
                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE|
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
                WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);

            win.setDefaultIcon(icon);
            win.setDefaultLogo(logo);

            win.setLayout(WindowManager.LayoutParams.MATCH_PARENT,
                    WindowManager.LayoutParams.MATCH_PARENT);

            final WindowManager.LayoutParams params = win.getAttributes();
            params.token = appToken;
            params.packageName = packageName;
            params.windowAnimations = win.getWindowStyle().getResourceId(
                    com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
            params.privateFlags |=
                    WindowManager.LayoutParams.PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED;
            params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;

            if (!compatInfo.supportsScreen()) {
                params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
            }

            params.setTitle("Starting " + packageName);

            // 获取WindowManager对象
            wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
            // 获取decorView
            view = win.getDecorView();
            // WindowManager添加View,在PhoneWindowManager中
            wm.addView(view, params);
            // 当窗口成功添加到WMS,则返回该对象
            return view.getParent() != null ? view : null;
        } catch (WindowManager.BadTokenException e) {
        } 
        //...
        return null;
    }

这边就添加了 startingWindow了,具体逻辑看注释,最终调用 WindowManager.addView()。

 

  • 1.6 结束 startingWindow

在 WindowManagerService 中:

    void scheduleRemoveStartingWindowLocked(AppWindowToken wtoken) {
        if (wtoken == null) {
            return;
        }
        if (mH.hasMessages(H.REMOVE_STARTING, wtoken)) {
            return;
        }

        if (wtoken.startingWindow == null) {
            if (wtoken.startingData != null) {
                wtoken.startingData = null;
            }
            return;
        }
        Message m = mH.obtainMessage(H.REMOVE_STARTING, wtoken);
        mH.sendMessage(m);
    }

然后 H 类收到消息:

                case REMOVE_STARTING: {
                    final AppWindowToken wtoken = (AppWindowToken)msg.obj;
                    IBinder token = null;
                    View view = null;
                    synchronized (mWindowMap) {
                        if (wtoken.startingWindow != null) {
                            view = wtoken.startingView;
                            token = wtoken.token;
                            wtoken.startingData = null;
                            wtoken.startingView = null;
                            wtoken.startingWindow = null;
                            wtoken.startingDisplayed = false;
                        }
                    }
                    if (view != null) {
                        try {
                            mPolicy.removeStartingWindow(token, view);
                        } catch (Exception e) {
                        }
                    }
                } break;

会调用 PhoneWindowManager.removeStartingWindow()。

   @Override
    public void removeStartingWindow(IBinder appToken, View window) {
        if (window != null) {
            WindowManager wm =(WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
            wm.removeView(window);
        }
    }

最后调用 WindowManager.removeView()。

 

  • 1.7 总结

startingWindow 的整个启动和结束过程,主要发送在 "android.display" 这个 Looper 线程,其中启动和结束最终分别调用了
WindowManagerGlobal 对象的 addView() 和 removeView() 操作,期间还有动画调度操作。

 


 

2. WindowManager 的 addView() 和 RemoveView()

具体的流程可以参考 Framework篇 - 一文搞懂 Activity、View、Window、ViewRootImpl。这边来介绍下 WindowManager 的 addView() 和 removeView() 的过程,实现在 WindowManagerImpl 中。

 

  • 2.1 WindowManager.addView()
    // WindowManager添加View
    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        // 调用WindowManagerGlobal.addView
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }
  • 2.2 WindowManagerGlobal.addView()
    // 添加View
    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        // view不能为空
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }
        // display不能为空
        if (display == null) {
            throw new IllegalArgumentException("display must not be null");
        }
        // LayoutParams必须为WindowManager.LayoutParams类型
        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;

        synchronized (mLock) {
            // ...
            // 每次调用global.addView()都会创建一个ViewRootImpl,它是decorView与WMS沟通的桥梁
            root = new ViewRootImpl(view.getContext(), display);

            // 设置LayoutParams
            view.setLayoutParams(wparams);

            // 加到mViews, mRoots, mParams集合中
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
        }

        try {
            // ViewRootImpl设置View
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
            synchronized (mLock) {
                final int index = findViewLocked(view, false);
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
            }
            throw e;
        }
    }

里面会创建 ViewRootImpl,接下来看看 ViewRootImpl。

 

  • 2.3 ViewRootImpl
public ViewRootImpl(Context context, Display display) {
    mContext = context;
    // 获取IWindowSession的代理类
    mWindowSession = WindowManagerGlobal.getWindowSession();
    mDisplay = display;
    mThread = Thread.currentThread(); //主线程
    mWindow = new W(this);
    mChoreographer = Choreographer.getInstance();
    ...
}

初始化变量:

  • mWindowSession:WMS 端的 Session 的代理对象;
  • mWindow: 继承于 IWindow.Stub 的 W 对象;
  • mChoreographer:绘制相关的对象。

 

  • 2.4 WindowManagerGlobal.getWindowSession()
  // ViewRootImpl构造器中调用 -> 获取IWindowSession的代理类
    public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {
                    // 获取IMS的代理类
                    InputMethodManager imm = InputMethodManager.getInstance();
                    // 获取WMS的代理类
                    IWindowManager windowManager = getWindowManagerService();
                    // 经过Binder调用,最终调用WMS
                    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;
        }
    }

通过 Binder 调用进入 system_server 进程,执行 openSession() 的操作。

 

  • 2.5 WindowManagerService.openSession()
    // 打开Session
    @Override
    public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
            IInputContext inputContext) {
        if (client == null) throw new IllegalArgumentException("null client");
        if (inputContext == null) throw new IllegalArgumentException("null inputContext");
        // 构建Session
        Session session = new Session(this, callback, client, inputContext);
        return session;
    }

再次经过 Binder 将数据写回 app 进程,则获取的便是 Session 的代理对象,接着来看看上面的 root.setView() 的实现。

 

  • 2.6 ViewRootImpl.setView()
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                // ...
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();

                    // 通过Binder调用,进入system进程的Session
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
                } catch (RemoteException e) {
                    mAdded = false;
                    mView = null;
                    mAttachInfo.mRootView = null;
                    mInputChannel = null;
                    mFallbackEventHandler.setView(null);
                    unscheduleTraversals();
                    setAccessibilityFocus(null, null);
                    throw new RuntimeException("Adding window failed", e);
                } finally {
                    if (restore) {
                        attrs.restore();
                    }
                }
                // ...
            }
        }
    }

通过 Binder 调用,进入 system_server 进程的 Session 对象。

 

  • 2.7 Session.addToDisplay()
final class Session extends IWindowSession.Stub
        implements IBinder.DeathRecipient {
    
    @Override
    public int add(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, Rect outContentInsets, Rect outStableInsets,
            InputChannel outInputChannel) {
        return addToDisplay(window, seq, attrs, viewVisibility, Display.DEFAULT_DISPLAY,
                outContentInsets, outStableInsets, null /* outOutsets */, outInputChannel);
    }

    @Override
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
            Rect outOutsets, InputChannel outInputChannel) {
        // 这边会进入WindowManagerService,进行addWindow操作
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                outContentInsets, outStableInsets, outOutsets, outInputChannel);
    }
}

最终在 Session 里面调用 WindowManagerService 的 addWindow() 方法。这边就不往下看了,感兴趣的同学可以去看看源码。WindowManager.removeView() 逻辑和 addView() 相似,就不再赘述。

 

 

3. 总结

Activity 的启动过程同时贯穿着 AMS/WMS 相关信息的创建,本文从 Window 角度来看待整个过程:

第一阶段运行在 system_server 进程:

将 Window 相应 task 移至顶部,并创建 AppWindowToken 对象,添加到 WMS.mTokenMap 记录该信息;
发送消息到 "android.display" 线程来处理 ADD_STARTING 启动窗口的消息。

第二阶段运行在目标进程的主线程:(当然在该阶段有些方法会 Binder call 到 system_server 进程)

创建获取 AMS 的 Binder 代理;

执行 performLaunchActivity() 过程,创建如下对象:

(1) 创建 LoadedApk 对象;

(2) 创建 ComponentName 对象;

(3) 创建目标 Activity 对象;

(4) 创建 Application 对象;

(5) 创建完 Activity,执行 attach 操作,初始化成员变量:

  • mWindow:数据类型为 PhoneWindow,继承于 Window 对象;
  • mWindowManager:数据类型为 WindowManagerImpl,实现 WindowManager 接口;
  • mToken:远程 ActivityRecord 的 appToken 的代理端。


执行performResumeActivity() 过程:

(1) 回调 onResume() 方法;

(2) 执行 addView 过程;

(3) 创建 ViewRootImpl 对象;

(4) 创建 WMS 端的 Session 的代理对象;

(5) 创建继承于 IWindow.Stub 的 ViewRootImpl.W 对象;

(6) 执行 setView() 添加视图到 WMS;

(7) 在 WMS 中创建 WindowState 对象;

(8) updateFocusedWindowLocked() 来更新聚焦窗口情况。

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值