[基础] 4. 理解Window和WindowManager

Window和WindowManager

Window(abstract class)是一个抽象的概念。每个Window都对应一个View和一个ViewRootImpl,ViewRootImpl是联系桥梁。因此Window并不是实际存在的,是以View的形式存在。
我们通过WindowManager (interface extends ViewManager)访问Window。 ViewManager接口的三个方法如下。都是针对View,再次说明Window是以View的形式存在

    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);

WindowManager通过下面的方式获取

    mwindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);

通过WindowManager添加View 的过程

        mwindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
        Button button = new Button(this);
        button.setText("糖宝");
        mlayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT,
                LayoutParams.WRAP_CONTENT, 0, 0, PixelFormat.TRANSPARENT);
        mlayoutParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL
                | LayoutParams.FLAG_NOT_FOCUSABLE
                | LayoutParams.FLAG_SHOW_WHEN_LOCKED;
        mlayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
        mlayoutParams.x = 100;
        mlayoutParams.y = 300;
        mwindowManager.addView(button, mlayoutParams);

WindowManager真正实现是WindowManagerImpl,WindowManagerImpl又交给了WindowManagerGlobal工厂(WindowManagerGlobal.getInstance)来提供实例。WindowManagerImpl这种工作模式是典型的桥接模式

Windows分层

window是分层的,每个window都对应着z-ordered,层级大的会覆盖在校的上面

  • 应用window的层级范围是1~99
  • 子window的层级范围是1000~1999
  • 系统window的层级范围是2000~2999

Window的创建过程

Window的具体实现是在WindowManagerService中,它的具体是现实PhoneWindow。WindowManager和WindowManagerService的交互是一个IPC的过程。
View是Android中的视图的呈现方式,但是View不能单独存在,它必须附着在Window这个抽象的概念上面,因此有视图的地方就有Window,我们平常使用的Activity、Dialog、Toast还有PopUpWindow和菜单都对应着一个Window。

Activity的window创建过程

核心流程:Activity.attach创建PhoneWindow, PhoneWindow创建DecorView,最后Activity的makeVisible显示DecorView

这里写图片描述

  1. ActivityThread中的performLaunchActivity完成Activity整个的启动过程,内部会创建Activity的实例,调用Activity的attach关联上下文环境变量
  2. attach方法会创建Window对象(PhoneWindow), 并设置回调接口(Activity implements Window.Callback), 当Window接收到外界状态变化时会回调Activity的方法。例如onAttachedToWindow等

    mWindow = PolicyManager.makeNewWindow(this)
    mWindow.setCallback(this)
    ......

    到这里Window已经创建成功了

  3. Activity的调用setContentView方法,Activy将具体的实现交给了PhoneWindow处理

        public void setContentView(@LayoutRes int layoutResID) {
            getWindow().setContentView(layoutResID);
            initWindowDecorActionBar();
        }
  4. PhoneWindow的setContentView,会创建DecorView(FrameLayout,Activity的顶级View),DecorView一般包含标题栏和内容栏(android.R.id.content)。PhoneWindow会加载的布局文件,并将view添加到DecorView的mContentParent。回调Activity的onContentChanged通知Activity视图已经发生了变化。

    到这里DecorView已经创建并初始化完毕。但是这时候Window, DectorView并没有被WindowManager识别,无法接收外界的输入信息

  5. Activity的makeVisibale这里DecorView才真正完成了添加和显示,这里Activity的视图才能被用户看到

    ActivityThread的handleResumeActivity会先调用Activity的OnResume方法,接着会调用Activity的makeVisible

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

Dialog的Window创建过程

过程与Activity的Window创建过程类似。

  1. 构造函数创建Window实例
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
......

        mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        final Window w = new PhoneWindow(mContext);
        mWindow = w;
        w.setCallback(this);
        w.setOnWindowDismissedCallback(this);
        w.setWindowManager(mWindowManager, null, null);
        w.setGravity(Gravity.CENTER);

        mListenersHandler = new ListenersHandler(this);
    }
  1. Dialog的setContentView将Dialog视图添加到DecorView中

        public void setContentView(@LayoutRes int layoutResID) {
            mWindow.setContentView(layoutResID);
        }
  2. Dialog的show方法,显示DecorView

    public void show() {
    ......
            mDecor = mWindow.getDecorView();
            mWindowManager.addView(mDecor, l);
            mShowing = true;
            sendShowMessage();
        }

Dialogue的Window创建过程和Activity的Window创建过程非常类似,几乎没什么差别。

需要注意普通的Dialog必须采用Activity的Context,如果采用Application的Context会报错。
原因是Application没有应用token,应用token一般是只有Activity才拥有。
Service也没有token,所以也不能在service中弹出普通dialog。不过可以使用系统的Window指定对话框,因为系统对话框不需要使用token

Toast的Window创建过程

Toast和Dialog不同,过程稍显复杂。Toast也是基于Window,但是Toast具有定时取消功能,所以系统采用了Handler。

Toast内部有两类IPC过程:
1. Toast访问NotificationManagerService
2. NotificationManagerService回调Toast里的TN接口(extends ITransientNotification.Stub),这明显是Binder类

Toast属于系统Window,内部的视图由两种方式指定:一种是系统默认的样式;另一种通过setView方法来指定一个自定义的View。
Toast的show和cancel都是IPC过程。
show和cancel方法,NMS处理Toast请求时会跨进程回调TN实例的方法,方法运行在请求应用的Binder线程池中,需要Handler切换到当前线程,因此Toast必须在有Looper的线程中弹出。

    ```
        public void show() {
            if (mNextView == null) {
                throw new RuntimeException("setView must have been called");
            }

            INotificationManager service = getService();
            String pkg = mContext.getOpPackageName();
            TN tn = mTN; // 远程回调
            tn.mNextView = mNextView;

            try {
                service.enqueueToast(pkg, tn, mDuration); // IPC
            } catch (RemoteException e) {
                // Empty
            }
        }

        public void cancel() {
            mTN.hide();

            try {
                getService().cancelToast(mContext.getPackageName(), mTN); // IPC
            } catch (RemoteException e) {
                // Empty
            }
        }
    ```

Toast的显示和隐藏是IPC过程,都是通过NotificationManagerService来实现。
enqueueToast会封装Toast请求为ToastRecord对象,加入mToastQueue队列(ArrayList)
对于非系统应用来说,mToastQueue最多能同时存在50个ToastRecord,这样做是为了防止DOS(Denial of Service,拒绝服务)。
因为如果某个应用弹出太多的Toast会导致其他应用没有机会弹出Toast。

小结

  • Toast,有消息循环的线程才能调用
  • 普通Dialog需要token,因此需要Activity的Context调用;系统Dialog不需要token
  • Activity Window和Dialog创建过程类似,ActivityThread和WMS的IPC通信
  • Toast创建跟NMS的IPC通信。所有非系统应用最多存在50个

关联小知识

  • 系统应用:process是”android” 或者在系统进程中(sharedUserId设置+platform key)的应用
  • linux下的UID是系统用户名的意思,Android系统修改了linux 的UID的含义
  • 每个APP对应一个UID。通过设置sharedUserId,拥有同一个sharedUserId并且签名相同的两个APP可以跑在同一个进程中
  • PID是进程id的意思,一个UID可以对应多个PID
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值