美文网首页Android源码解析安卓资源收集Android技术知识
Android源码解析Window系列第(三)篇---Windo

Android源码解析Window系列第(三)篇---Windo

作者: LooperJing | 来源:发表于2016-12-13 18:58 被阅读547次

    转载请注明文章出处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!

    相关文章

      网友评论

      • Drummor:dialog中首先是自己创建一个PhoneWindow对象,然后将从Activity中获取的WindowManger设置给刚刚自创建的phoneWindow对象,具体设置的方法是
        public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
        ...
        ...
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
        }
        最后还是使用windowManagerImpl的createLocalWindowManager(Window mParentWindow)方法,所以无论是从Activitiy中获取WindowManager合适系统获取WindowManager最后都要使用createLocalWM方法,而且传递的是this也就是window本身,所以不存在parentwindow是空啊。。
      • 消沉沉:假如,我想让WindowManager添加一个全屏的视图,可是会显示状态栏。Flags = FLAG_HARDWARE_ACCELERATED . 请问我该设置那个Flags。

      本文标题:Android源码解析Window系列第(三)篇---Windo

      本文链接:https://www.haomeiwen.com/subject/cwjvmttx.html