Android Window和WindowManager

一、Window简介

有时候我们需要在桌面上显示一个类似悬浮窗的东西,这种效果就需要用 Window 来实现,Window 是一个抽象类,表示一个窗口,它的具体实现类是 PhoneWindow。Window是一个抽象的概念,每一个Window 都对应着一个View 和 一个 ViewRootImpl,Window 和 View 通过 ViewRootImpl 来建立联系的,因此Window 并不是实际存在的,它是以View 的形式存在的。后面对添加Window 的源码分析过程也可以分析出来。

二、Window 分类

Window 有三种类型,分别是应用 Window、子 Window 和系统 Window。应用类 Window 对应一个 Acitivity,子 Window 不能单独存在,需要依附在特定的父 Window 中,比如常见的一些 Dialog 就是一个子 Window。系统 Window是需要声明权限才能创建的 Window,比如 Toast 和系统状态栏都是系统 Window。

Window 是分层的,每个 Window 都有对应的 z-ordered,层级大的会覆盖在层级小的 Window 上面,我们可以用一个表格来直观的表示:

这些层级范围对应着 WindowManager.LayoutParams 的 type 参数,如果想要 Window 位于所有 Window 的最顶层,那么采用较大的层级即可,很显然系统 Window 的层级是最大的,当我们采用系统层级时,需要声明权限。一般我们选用 TYPE_SYSTEM_OVERLAY 或者TYPE_SYSTEM_ERROR ,并申请相应的权限即可。

三、Window 的 添加 、删除 和 刷新

我们对 Window 的操作是通过 WindowManager 来完成的,WindowManager 是一个接口,它继承自只有三个方法的 ViewManager 接口:

public interface ViewManager
{
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}

这三个方法其实就是 WindowManager 对外提供的主要功能,即添加 View、更新 View 和删除 View。接下来来看一个通过 WindowManager 添加 Window 的例子,代码如下:

public class MainActivity extends AppCompatActivity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
 
        Button floatingButton = new Button(this);
        floatingButton.setText("button");
        WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT,
                0, 0,
                PixelFormat.TRANSPARENT
        );
        // flag 设置 Window 属性
        layoutParams.flags= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
        // type 设置 Window 类别(层级)
        layoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
        layoutParams.gravity = Gravity.CENTER;
        //通过Context.getSystemService(Context.WINDOW_SERVICE)来获取系统WindowManager
        WindowManager windowManager = (WindowManager)this.getSystemService(Context.WINDOW_SERVICE);
        windowManager.addView(floatingButton, layoutParams);
    }
}

这点从WindowManager的定义也可以看出来,它提供的三个接口方法addView 、updateViewLayout 、removeView 都是针对View 的,这就说明View 才是Window存在的实体。

四、WindowManager 的内部机制

在实际使用中无法直接访问 Window,对 Window 的访问必须通过 WindowManager。WindowManager就是Android 提供给我们的操作WindowManager 提供的三个接口方法 addView、updateViewLayout 以及 removeView 都是针对 View 的,这说明 View 才是 Window 存在的实体,上面例子实现了 Window 的添加,WindowManager 是一个接口,它的真正实现是 WindowManagerImpl 类,请看下面的分析过程:

1.Context的具体是实现是ContextImpl ,找到对应类中的getSystemService ,可以看到是通过了SystemServiceRegistry.getSystemService 来获取。

ContextImlp
@Override
public Object getSystemService(String name) {
       return SystemServiceRegistry.getSystemService(this, name);
}

2.可以看到这个对象是从 这个 SYSTEM_SERVICE_FETCHERS 私有静态对面里面获取的,那我们看看是在什么时候注册这个Context.WINDOW_SERVICE。

SystemServiceRegistry
private static final HashMap> SYSTEM_SERVICE_FETCHERS =
            new HashMap>();

public static Object getSystemService(ContextImpl ctx, String name) {
        ServiceFetcher fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
        return fetcher != null ? fetcher.getService(ctx) : null;
    }

3.在SystemServiceRegistry类中找到对应的注册方法,这个方法还是私有的,那证明注册的具体实现还是在SystemServiceRegistry这个类里面的。

private static  void registerService(String serviceName, Class serviceClass,
            ServiceFetcher serviceFetcher) {
        SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
        SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
    }

4.在这个SystemServiceRegistry中查找registerService的应用发现注册的地方都在类的静态初始化代码块里面,
找到对应的Context.WINDOW_SERVICE的注册的地方,可以看到具体类就是WindowManagerImpl

static {
registerService(Context.WINDOW_SERVICE, WindowManager.class,
                new CachedServiceFetcher() {
            @Override
            public WindowManager createService(ContextImpl ctx) {
                return new WindowManagerImpl(ctx);
            }});
}

到这里我们已经找到WindowManager 的具体实现类的,下面继续看WindowManagerImpl的具体实现。

public final class WindowManagerImpl implements WindowManager {
    
    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);
    }
    @Override
    public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.updateViewLayout(view, params);
    }
    @Override
    public void removeView(View view) {
        mGlobal.removeView(view, false);
    }
}

可以看到,WindowManagerImpl 并没有直接实现 Window 的三大操作,而是交给了 WindowManagerGlobal 来处理,下面分析以 addView 为例,分析一下 WindowManagerGlobal 中的实现过程:

1、先看一下 WindowManagerGlobal 的初始化过程

public static void initialize() {
        getWindowManagerService();
    }

    public static WindowManagerGlobal getInstance() {
        synchronized (WindowManagerGlobal.class) {
            if (sDefaultWindowManager == null) {
                sDefaultWindowManager = new WindowManagerGlobal();
            }
            return sDefaultWindowManager;
        }
    }

    public static IWindowManager getWindowManagerService() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowManagerService == null) {
                sWindowManagerService = IWindowManager.Stub.asInterface(
                        ServiceManager.getService("window"));
                try {
                    if (sWindowManagerService != null) {
                        ValueAnimator.setDurationScale(
                                sWindowManagerService.getCurrentAnimatorScale());
                    }
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowManagerService;
        }
    }

可以看到 WindowManagerGlobal 是一个单例,实例化时在getWindowManagerService()方法中获取了WindowManagerService 的 binder 对象 来调用系统的WindowManagerService。

2、addView()方法 ,检查参数合法性,如果是子 Window 做适当调整

       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);
        }

3、创建 ViewRootImpl ,并将 View 添加到集合中,通过ViewRootImpl 的 setView 方法来实现绘制。

       root = new ViewRootImpl(view.getContext(), display);

        view.setLayoutParams(wparams);

        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);

        // do this last because it fires off messages to start doing things
        try {
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
            // BadTokenException or InvalidDisplayException, clean up.
            if (index >= 0) {
                removeViewLocked(index, true);
            }
            throw e;
        }

在 WindowManagerGlobal 内部有如下几个集合比较重要:

    private final ArrayList mViews = new ArrayList();
    private final ArrayList mRoots = new ArrayList();
    private final ArrayList mParams = new ArrayList();
    private final ArraySet mDyingViews = new ArraySet();

其中 mViews 存储的是所有 Window 所对应的 View,mRoots 存储的是所有 Window 所对应的 ViewRootImpl,mParams 存储的是所有 Window 所对应的布局参数,mDyingViews 存储了那些正在被删除的 View 对象,或者说是那些已经调用了 removeView 方法但是操作删除还未完成的 Window 对象,可以通过表格直观的表示:

你可能感兴趣的:(Android Window和WindowManager)