WindowManager杂谈

悬浮窗口相信开发android的猩猩们都遇到过或者实现过,简单的说明原理就是获取WindowManager对象,通过该对象的addView和removeView来向一个页面添加一个悬浮框和删除该悬浮框,其实用WindowManger这个可以实现好多小功能:
1)比如在TV端开发的过程中如果某一页面有分页显示数据的话,当用户按遥控器数字键翻页的时候在页面中动态添加一个View来显示用户输入的数字。
2)可以在MediaPlayer全屏播放的时候在某个时机动态添加一个View来展示相关内容。
3)一些手机管理软件也用悬浮框实现来部署一些快捷功能
实现上述功能有两种思路或者方法来实现:
1) 通过(WindowManager)getSystemService(Context.WINDOW_SERVICE);来获取WindowManger对象,调用addView来添加悬浮框,在用addView添加View的时候需要设置该LayoutParams来控制View的大小或者显示位置。
2)在获取WindowManger的同时,在通过反射机制调用makeNewWindow来生成一个Window里面。我们知道Activity都封装了一个Window对象(PhoneWindow),在Activity里面调用setContentView的时候实际上是调用Window对象的setContentView方法。那么我们完全也可以用此种方法来实现:
通过反射机制来获取Window对象的方法如下:


        try { 
            @SuppressWarnings("rawtypes")
            Class policyManagerClass = Class.forName("com.android.internal.policy.PolicyManager"); 
            @SuppressWarnings("rawtypes")
            Class[] parameterTypes = {Context.class};
            @SuppressWarnings("unchecked")
            Method method = policyManagerClass.getMethod("makeNewWindow", parameterTypes);
            mWindow = (Window)method.invoke(null, this);
            mWindow.setFormat(PixelFormat.TRANSLUCENT);
            mWindow.requestFeature(Window.FEATURE_NO_TITLE);
            mWindow.setWindowManager(mWindowManager, null, null);
        mWindow.setBackgroundDrawable(colorDrawable);

        } catch (Exception e1) {

            e1.printStackTrace();
        }

此时添加View的时候可以用下面两行代码来实现:

    mWindow.setContentView(view, params)
            mWindowManager.addView(mWindow.getDecorView(), params);

删除添加的View的时候可以下面两段代码来实现:

//注意Window对象是没有提供remove等实现删除View的方法的,所以要结合
//mWIndowManger来完成
mWindowManager.removeView(mWindow.getDecorView());

写到这儿可以思考如下问题来理一下思路:
1)Activity中Window具体的说是PhoneWindow在Activity中是怎么初始化的?
2)WindowManger对象又是怎么初始化的?
由于水平有限阅读Activity源码对我来说是个间距的任务,但结合网上大牛们写的技术博客和相关书籍资料(前人栽树后人乘凉就是方便),可以知道在Activity的attach方法里面完成了Window和WindowManager的初始化(简化如下):

 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) {
        attachBaseContext(context); 
        mWindow = PolicyManager.makeNewWindow(this);
        mWindow.setWindowManager(null, mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        mWindowManager = mWindow.getWindowManager();
        mCurrentConfig = config;
    }

mWindow通过PolicyManger的makeNewWindow来初始化,PolicyManger的源代码也很简单(为了减少篇幅,删除了一些本篇博客不需要的部分代码):


public final class PolicyManager {
    private static final String POLICY_IMPL_CLASS_NAME =
        "com.android.internal.policy.impl.Policy";

    private static final IPolicy sPolicy;

    static {
            Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
            //通过反射来对IPolicy进行初始化
           sPolicy = (IPolicy)policyClass.newInstance();

    }

    //私有构造器,不能在外部初始化
    private PolicyManager() {}

    // The static methods to spawn new policy-specific objects
    //在attach方法中正式通过调用这个方法来初始化Window对象
    public static Window makeNewWindow(Context context) {
        return sPolicy.makeNewWindow(context);
    }

}

正如上面代码展现的那样,先通过反射来初始化IPolicy 对象实际上是IPolicy接口的实现类Policy,通过该对象来调用makeNewWindow来实例化一个Window对象,所以接下来很简单就看看这个Policy是个什么鬼:

public class Policy implements IPolicy {
    private static final String TAG = "PhonePolicy";

    private static final String[] preload_classes = {
        "com.android.internal.policy.impl.PhoneLayoutInflater",
        "com.android.internal.policy.impl.PhoneWindow",
        "com.android.internal.policy.impl.PhoneWindow$1",
        "com.android.internal.policy.impl.PhoneWindow$ContextMenuCallback",
        "com.android.internal.policy.impl.PhoneWindow$DecorView",
        "com.android.internal.policy.impl.PhoneWindow$PanelFeatureState",
        "com.android.internal.policy.impl.PhoneWindow$PanelFeatureState$SavedState",
    };

    static {
        // For performance reasons, preload some policy specific classes when
        // the policy gets loaded.
        for (String s : preload_classes) {
            try {
                Class.forName(s);
            } catch (ClassNotFoundException ex) {
                Log.e(TAG, "Could not preload class for phone policy: " + s);
            }
        }
    }
    //此正式初始化Activity中mWindow对象的地方
    public PhoneWindow makeNewWindow(Context context) {
        return new PhoneWindow(context);
    }

    public PhoneLayoutInflater makeNewLayoutInflater(Context context) {
        return new PhoneLayoutInflater(context);
    }

    public PhoneWindowManager makeNewWindowManager() {
        return new PhoneWindowManager();
    }
}

上面的代码简单的说明了一下mWindow的初始化流程,获得的是PhoneWindow。下面就简单的分析下WindowManger又是怎么初始化的:
用初始化后的mWindow调用setWindowManager来完成了Window的初始化操作:

 public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        .....
        //在attach中传入的是null,所以if条件成立
        if (wm == null) {
            //此处vm为WindowManagerImpl
            wm = WindowManagerImpl.getDefault();
        }
        //初始化Winow类中封装的mWindowManger变量
        //该变量也是一个WindowManger
        mWindowManager = new LocalWindowManager(wm, hardwareAccelerated);
    }

WindowMangerImpl.getDefault方法直接返回的是一个WindowMangerImpl对象,该对象是接口WindowManger的实现类。紧接着初始化了Window类中的WindowManger类型的变量mWindowManger!而最终在Activity的attach方法中对Activity的mWindowManger这个WindowManger类型的赋值就是通过mWindow.getWindowManager();这个WindowManger就是LocalWindowManager。
到此为止简单的梳理了一下Window和WindowManger的初始化过程,其实我们在Activity中使用WindowManager的时候是调用getSystemService(Context.WINDOW_SERVICE);来获取的,那就在看看该方法都做了些什么事儿:

  public Object getSystemService(String name) {
         //返回的上面所说的LocalWindowManager
        if (WINDOW_SERVICE.equals(name)) {
            return mWindowManager;
        } else if (SEARCH_SERVICE.equals(name)) {
            ensureSearchManager();
            return mSearchManager;
        }
        return super.getSystemService(name);
    }

可见通常在Activity中获取的WindowManager中获取到的就是LocalWindowManager.
最后在简单梳理一下WindowManager的类继承关系:
WindowManager杂谈_第1张图片
其实虽然说LocalWindowManager继承了WindowManager,但是真正工作的还是该类里面持有的WindowManagerImpl类型的mWindowManager这个变量指向的对象WindowManagerImpl.LocalWindowManager主要起到了一种代理的作用。

上面简单的说了一下WindowManager和Window的关系,但是我们如果有好奇心的话应该会有这么个疑问:在Activity中通过setContentView方法把将View添加到DecorView中《详细说明点击此处》,但是这个DecorView又添加到哪儿了呢?在对WindowManager的说明中我们知道我们可以通过WindowManager的addView方法可以添加View.所以我们可以设想一下这个DecorView是不是也添加到WindowManager里面了呢?结合网上的资料查阅ActivityThread类中的handleResumeActivity方法有这么一段代码:

 final Activity a = r.activity; final Activity a = r.activity;
   if (r.window == null && !a.mFinished && willBeVisible) {
                //获取Activity的window对象为PhoneWindow
                r.window = r.activity.getWindow();
                //获取DecorView
                View decor = r.window.getDecorView();
                decor.setVisibility(View.INVISIBLE);
                //获取Activity的WindowManager
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                if (a.mVisibleFromClient) {
                    a.mWindowAdded = true;
                    //把DecorView添加到windowManager中
                    wm.addView(decor, l);
                }

            // If the window has already been added, but during resume
            // we started another activity, then don't yet make the
            // window visible.
            } else if (!willBeVisible) {
                if (localLOGV) Slog.v(
                    TAG, "Launch " + r + " mStartedActivity set");
                r.hideForNow = true;
            }

虽然写到此处还是不能给出Activity中WindowManager,Window,View三者之间的关系下结论,但是基本的简单的关系还是可以很清晰的展现:
1)调用activity中的setContentView。
2)调用Window也就是phoneWindow的setContentView将View添加到DecorView中,确切地说是添加到DecorView的子view :mContentParent中,该view是个ViewGroup.这也是为什么Activity中叫setContentView而不是叫setView的原因。
3)在ActivityThread的handleResumeActivity方法中将DecorView添加到WindowManager中。
至于深处的关联以及ActivityThread这涉及到Activity的启动过程,目前水平很菜还不知道,慢慢研究吧。

这篇博客写到这就结束,没写什么实质性有价值的东东,写这篇博客的时候深切体会到了知识之不足,往往想往深处写点什么但是不知道从何下手的感觉,自己对android的某些知识点理解的还是太肤浅,可以说是革命尚未成功,自己仍需努力。虽然自己的博客没别人博客写的有深度,但是自己还是不会放弃写博客,写博客的好处只有自己动手写了才知道收获是多么巨大。
进步,总是一步步走过来的,编程和学习编程切忌急于求成

你可能感兴趣的:(android)