Android源码解析Window系列第(三)篇---WindowManager工作原理

转载请注明文章出处LooperJing

前面的两篇博客,总结了一下Window的基本知识,我们知道Window是一个抽象的概念,每一个Window都对应着一个View,Window的呈现方式是View,View要依赖Window,View和Window最终要关联在一起。Activity在android中所起的作用主要是处理一些逻辑问题,比如生命周期的管理、建立窗口等。Window的层次关系就如下面画的一样。详细参考前面两篇博客。

Window层次

Android源码解析Window系列第(一)篇---Window的基本认识和Activity的加载流程

Android源码解析Window系列第(二)篇---Dialog加载绘制流程

这篇博客写一下WindowManager,在android中,窗口的管理还是比较重要的一块,因为他直接负责把内容展示给用户,并和用户进行交互。响应用户的输入等。WindowManager可以说是外部访问Window的入口,利用WindowManager可以向Window中添加View,删除View,更新View。其实我们已经用过很多次WindowManager的addView方法了,因为ViewGroup就继承了ViewManager,WindowManager也是继承了ViewManager。再比如360手机助手有一个悬浮窗,用来显示内存的实时信息,这个也要通过WindowManager来处理。

1、WindowManager基本认识

  • WindowManager可以通过下面两种方式进行获取。
WindowManager   mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE); 
WindowManager mWindowManager=(WindowManager) getWindowManger(); 
  • 设置WindowManager.LayoutParams参数
WindowManager.LayoutParams wmParams = new WindowManager.LayoutParams(); 

// 设置window type 
wmParams.type = LayoutParams.TYPE_PHONE; 

// 设置图片格式,效果为背景透明
wmParams.format = PixelFormat.RGBA_8888; 

 // 调整悬浮窗口至右侧中间 
wmParams.gravity = Gravity.RIGHT| Gravity. CENTER_VERTICAL;

// 以屏幕左上角为原点,设置x、y初始值 
wmParams.x = 0;
wmParams.y = 0;
// 设置悬浮窗口长宽数据 
wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
wmParams.height =WindowManager.LayoutParams.WRAP_CONTENT;
  • WindowManager是一个接口,并且继承了ViewManager
public interface WindowManager extends ViewManager 
  • WindowManager.LayoutParams内部包含了Window的一些参数,比如Window的宽高,对齐方式,flag等。

  • WindowManager是外界访问Window的入口,Window的具体实现是WindowManagerService,WindowManager和WindowManagerService的交互是一个IPC过程。

  • Android中所有的视图都是通过Window呈现的,不管是Activity、Dialog还是Toast,他们的视图实际上都是附加在Window上的,因此Window实际是View的直接管理者。

2、Window中添加View

既然WindowManager是外部访问Window的入口,对于Activity的Window来说,WindowManager肯定是可以访问Activity的Window的,想往Activity中的DecorView添加View,Activity应改持有了WindowManager的引用,在第一篇博客中,有分析过,在ActivityThread中的performLaunchActivity内部用反射的方式创建了Activity对象之后,调用了Activity对象的attach方法。

 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) {
        attachBaseContext(context);

        ....

        mWindow = new PhoneWindow(this, window);
        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;
    }

attach内部创建了PhoneWindow之后,以(WindowManager)context.getSystemService(Context.WINDOW_SERVICE)的方式获取了WindowManager对象,并且设置给了Window中的mWindowManage成员变量。

  public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated
                || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        //返回的是WindowManagerImpl
        mWindowManager =((WindowManagerImpl)wm).createLocalWindowManager(this);
    }
 public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        //可以判断出WindowManagerImpl是WindowManager的实现类
        return new WindowManagerImpl(mContext, parentWindow);
    }

然后以mWindow.getWindowManager()方式给Activity的mWindowManager赋值,到此Activity持有了mWindowManager对象,mWindow对象,Window也持有了mWindowManager对象,Activity类的Window类型成员变量mWindow及WindowManager类型成员变量mWindowManager都被赋值,现在可以操作Activity的Window了。

现在分析一下WindowManager是如何操作Window中的View的,WindowManager继承了ViewManager,ViewManager定义了三个操作View的方法。addView/updateViewLayoutView/removeView,所以ViewManager像是定义了一套外部操作Activity的Window中的View的接口。

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的真正实现类是WindowManagerImpl

public final class WindowManagerImpl implements WindowManager {

    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    ......

    public WindowManagerImpl(Display display) {
        this(display, null);
    }

    private WindowManagerImpl(Display display, Window parentWindow) {
        mDisplay = display;
        mParentWindow = parentWindow;
    }

    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mDisplay, 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虽然带着Impl的后缀,却把活都交给了mGlobal,这样就要去看看WindowManagerGlobal的addView了。

 public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
        //参数校验省略
        ......

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
        if (parentWindow != null) {
            //如果是子View,需要调整布局参数
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } else {
            // If there's no parent and we're running on L or above (or in the
            // system context), assume we want hardware acceleration.
            final Context context = view.getContext();
            if (context != null
                    && context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
            }
        }

        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
             ......
            //第一步
            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.
            synchronized (mLock) {
                final int index = findViewLocked(view, false);
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
            }
            throw e;
        }
    }

上面的代码主要有五个步骤

  • 1、构建ViewRootImpl,暂时不管ViewRootImpl是什么东西
  • 2、给所添加的View设置参数
  • 3、保存View到mViews的列表中
  • 4、保存ViewRootImpl对象root到mRoots的列表中
  • 5、调用ViewRootImpl对象root的setView方法,将View显示到手机上。setView方法比较复杂,已经深入到Native层了,不分析了。

3、WindowManager获取方式的分析

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

WindowManager w2 = (WindowManager) getApplication().getSystemService(Context.WINDOW_SERVICE);

上面的w1与w2有什么区别呢?

w1获取是通过Context的getSystemService方法,这个方法的实现在Activity中,返回的是Activity的mWindowManager。

 @Override
    public Object getSystemService(@ServiceName @NonNull String name) {
        if (getBaseContext() == null) {
            throw new IllegalStateException(
                    "System services not available to Activities before onCreate()");
        }

        if (WINDOW_SERVICE.equals(name)) {
            //此处返回的是Activity的mWindowManager
            return mWindowManager;
        } else if (SEARCH_SERVICE.equals(name)) {
            ensureSearchManager();
            return mSearchManager;
        }
        return super.getSystemService(name);
    }

此处返回的Activity的mWindowManager是通过createLocalWindowManager来创建的,注意传入给WindowManagerImpl中参数parentWindow,就是当前Window对象。

public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mContext, parentWindow);
    }

因为Activity有多个,所以每个Activity的WindowManager都不一样。而w2的获取的WindowManager,最终WindowManagerImpl时传入给WindowManagerImpl中parentWindow是null值,这获取的WindowManager对每个APP来说是全局单例的。

4、 Window在事件分发中的应用

当一个点击事件产生时,它首先传递到Activity、在Activity的dispatchToucheEvent的方法中进行分发、然后传递到Window、最后才到顶级view。

    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }

查看Window子类PhoneWindow的源码。

    @Override
    public boolean superDispatchTouchEvent(MotionEvent event) {
        return mDecor.superDispatchTouchEvent(event);
    }

而这里的mDecor就是顶级View,就这样事件就从window传递到了顶级View了。

Please accept mybest wishes for your happiness and success!

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

推荐阅读更多精彩内容