Window和WindowManager(一)

作者: 大虾啊啊啊 | 来源:发表于2018-09-03 15:27 被阅读13次

前言

什么是Window?其实Window是一个抽象的概念,在日常开发中,可能很少遇到,但是有时候我们需要实现一些悬浮窗的东西,这种效果就需要用到Window。我们先看下Window类:

/**
 * Abstract base class for a top-level window look and behavior policy.  An
 * instance of this class should be used as the top-level view added to the
 * window manager. It provides standard UI policies such as a background, title
 * area, default key processing, etc.
 *
 * <p>The only existing implementation of this abstract class is
 * android.view.PhoneWindow, which you should instantiate when needing a
 * Window.
 */
public abstract class Window {

看Windwo的源码,我们知道,它是一个抽下类,从注释上可以看到,Window的唯一实现类是PhonWindow。实现Window的具体实现是由PhoneWindow负责的。看下PhoneWindow类。

/**
 * Android-specific Window.
 * <p>
 * todo: need to pull the generic functionality out into a base class 
 * in android.widget.
 *
 * @hide
 */
public class PhoneWindow extends Window implements MenuBuilder.Callback {

创建一个Window通过WindowManager完成,也就是说,WindowManager是外界访问Window的入口。而Window的具体实现是在WindowManagerService中,是一个独立的进程。WindowManager和WindowManagerService的通信其实就是一个IPC过程。也就是一个跨进程通信。Android上所有的视图都是通过Window来呈现的,不管是Activity,Dialog,Toast她们的视图都是附加在Window上。也就是说,Window是所有View的直接管理者。每一个View的呈现都是附加在Window上。

一、Window和WindowManager

在前言中我们了解到了Window其实就是所有视图的直接管理者,所有视图的呈现都是附加在Window上。打个比喻就是,Window就是我们实际生活中的窗户玻璃,具体的View的呈现就是我们在玻璃上描绘的各种图案。接着我们知道通过WindowManager可以访问Window,他们俩运行在不同的进程中,通过 IPC机制进行通信。
下面我们来简单的介绍如何添加一个Window。

package com.mujin.keji.myapplication;

import android.content.Context;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.Gravity;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.ImageView;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    private ImageView ivEtxt;
    private Button button;
    private  WindowManager.LayoutParams params;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
        ivEtxt = (ImageView) findViewById(R.id.iv_etxt);
        ivEtxt.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                button = new Button(MainActivity.this);
                button.setText("这是一个按钮");
                button.setTextColor(Color.parseColor("#FF0000"));
                params  = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT,0,0, PixelFormat.TRANSPARENT);
                params.gravity = Gravity.TOP|Gravity.LEFT;
                params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
                params.x = 100;
                params.y = 300;
                windowManager.addView(button,params);
            }
        });
    }
}

添加系统权限:

    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

从上面的例子我们把一个Button添加到屏幕的坐标(100,300)的位置上。我们发现添加Window其实就是添加一个View。Window的概念是抽象的,我们把对Window的操作直接当成对View的操作即可。一个空的Window是没有意义的。在上面初始化WindowManager.LayoutParams有两个重要的参数:Flags和Type。
Flags:表示Window属性,主要用来控制Window的显示特性。接下来介绍几个常用的特性。
FLAG_NOT_FOCUSABLE:
表示Windwo没有焦点,不接受输入事件。
FLAG_NOT_TOUCH_MODAL:
表示当前Window区域的事件,自己处理。不在自己的区域的事件,交给底层Window。
FLAG_SHOW_WHEN_LOCKED:
这个属性,表示Window可以显示在锁屏的界面上。
Type:表示Window的类型,有三种类型,分别是应用Window,子Window和系统Window。应用Window对应着一个Activity,子Window不能单独存在,需要附属在特定的父Window中,比如常见的Dialog。系统Window的创建,需要权限。
在WindowManager中所提供的功能很简单,常用的方法有三个。addView,updateView,removeView。这三个方法定义在了ViewManager中,而WindowManager继承了ViewManager。我们从上面的三个方法可以看到,WindowManager对Window的操作,实际就是对Window中的View进行操作。因为一个空的Window是没有意义的,我们在这讲的Window都是说Window中的View。

二、Window的内部机制

Window是一个抽象的概念,每一个Window都对应着一个View和一个ViewRootImpl。Window和View是通过ViewRootImpl来建立连接的。之所以说Window 是抽象的,是因为,WindowManager实际上对Window的操作,其实就是对Window中的View进行操作。可以说Window是一个虚拟的不存在的东西。接下来我们分析Window的具体添加过程:

(一)Window的添加过程

Window的添加,实际就是WindowManager通过addView添加一个View。我们来看下Window的源码。

@SystemService(Context.WINDOW_SERVICE)
public interface WindowManager extends ViewManager {

我们看到WindowManager是一个接口,而它的实现类是WindowManagerImpl

* @see WindowManager
 * @see WindowManagerGlobal
 * @hide
 */
public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    private final Context mContext;
    private final Window mParentWindow;

我们来看下三个核心的方向addView removeView updateView

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

我们看到这三个方法的具体实现都是通过WindowManagerGlobal mGlobal 对象调用,mGlobal 在第一行代码中进行初始化

  private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();

所以我们知道WindowManager对Window的操作,实际上又交给了WindowManagerGlobal这个东西去处理。接着我们看下WindowManagerGlobal对Window的添加过程的实现。

 public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        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);
        } else {
            // If there's no parent, then hardware acceleration for this view is
            // set from the application's hardware acceleration setting.
            final Context context = view.getContext();
            if (context != null
                    && (context.getApplicationInfo().flags
                            & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
            }
        }

        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            // Start watching for system property changes.
            if (mSystemPropertyUpdater == null) {
                mSystemPropertyUpdater = new Runnable() {
                    @Override public void run() {
                        synchronized (mLock) {
                            for (int i = mRoots.size() - 1; i >= 0; --i) {
                                mRoots.get(i).loadSystemProperties();
                            }
                        }
                    }
                };
                SystemProperties.addChangeCallback(mSystemPropertyUpdater);
            }

            int index = findViewLocked(view, false);
            if (index >= 0) {
                if (mDyingViews.contains(view)) {
                    // Don't wait for MSG_DIE to make it's way through root's queue.
                    mRoots.get(index).doDie();
                } else {
                    throw new IllegalStateException("View " + view
                            + " has already been added to the window manager.");
                }
                // The previous removeView() had not completed executing. Now it has.
            }

            // If this is a panel window, then find the window it is being
            // attached to for future reference.
            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                final int count = mViews.size();
                for (int i = 0; i < count; i++) {
                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                        panelParentView = mViews.get(i);
                    }
                }
            }

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

1.检查参数的合法性

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);
        } else {
            // If there's no parent, then hardware acceleration for this view is
            // set from the application's hardware acceleration setting.
            final Context context = view.getContext();
            if (context != null
                    && (context.getApplicationInfo().flags
                            & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
            }
        }

如果是子Window,那么要调整一些布局参数

if (parentWindow != null) {
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        }

2.创建ViewRootImpl类,添加View

我们在上面也提到了每一个Window都会对应一个View,它们之间是通过ViewRootImpl来建立连接的。这里涉及到几个重要的列表。

 private final ArrayList<View> mViews = new ArrayList<View>();
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>();
    private final ArraySet<View> mDyingViews = new ArraySet<View>();

ArrayList<View> mViews ,用来存放所有Window对应的View。
ArrayList<ViewRootImpl> mRoots ,用来存储所有Window和View建立连接的ViewRootImpl。
ArrayList<WindowManager.LayoutParams> mParams,用来存储所有View的布局参数。
ArraySet<View> mDyingViews ,存储着那些正在被删除的View,或者说是WindowManager正在调用remoview,但是Window还没被销毁。
添加View过程。

 ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            // Start watching for system property changes.
            if (mSystemPropertyUpdater == null) {
                mSystemPropertyUpdater = new Runnable() {
                    @Override public void run() {
                        synchronized (mLock) {
                            for (int i = mRoots.size() - 1; i >= 0; --i) {
                                mRoots.get(i).loadSystemProperties();
                            }
                        }
                    }
                };
                SystemProperties.addChangeCallback(mSystemPropertyUpdater);
            }

            int index = findViewLocked(view, false);
            if (index >= 0) {
                if (mDyingViews.contains(view)) {
                    // Don't wait for MSG_DIE to make it's way through root's queue.
                    mRoots.get(index).doDie();
                } else {
                    throw new IllegalStateException("View " + view
                            + " has already been added to the window manager.");
                }
                // The previous removeView() had not completed executing. Now it has.
            }

            // If this is a panel window, then find the window it is being
            // attached to for future reference.
            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                final int count = mViews.size();
                for (int i = 0; i < count; i++) {
                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                        panelParentView = mViews.get(i);
                    }
                }
            }

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

            view.setLayoutParams(wparams);

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

把要添加的View,ViewRootImpl,布局参数mParams 添加到集合列表中。

3.添加到列表之后,更新界面显示

通过ViewRootImpl调用setView进行界面的绘制。

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

我们知道,把View添加到Window之后,需要绘制,才能呈现到界面上。在setView方法中通过调用requestLayout()方法实现View的绘制,具体的绘制流程,先不介绍,后面的章节会介绍如何绘制View。

       /* = WindowManagerImpl.ADD_OKAY; */

                // Schedule the first layout -before- adding to the window
                // manager, to make sure we do the relayout before receiving
                // any other events from the system.
                requestLayout();

接着把View绘制之后,继续往下面看代码。

try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
                } catch (RemoteException e) {
                    mAdded = false;
                    mView = null;
                    mAttachInfo.mRootView = null;
                    mInputChannel = null;
                    mFallbackEventHandler.setView(null);
                    unscheduleTraversals();
                    setAccessibilityFocus(null, null);
                    throw new RuntimeException("Adding window failed", e);
                } finally {
                    if (restore) {
                        attrs.restore();
                    }

我们看到核心代码。

  res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);

window的添加过程交给了mWindowSession对象调用addToDisplay方法进行添加。我们看下mWindowSeesion初始化的地方

public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {
                    InputMethodManager imm = InputMethodManager.getInstance();
                    IWindowManager windowManager = getWindowManagerService();
                    sWindowSession = windowManager.openSession(
                            new IWindowSessionCallback.Stub() {
                                @Override
                                public void onAnimatorScaleChanged(float scale) {
                                    ValueAnimator.setDurationScale(scale);
                                }
                            },
                            imm.getClient(), imm.getInputContext());
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowSession;
        }
    }
  IWindowManager windowManager = getWindowManagerService();
                    sWindowSession = windowManager.openSession(
                            new IWindowSessionCallback.Stub() {
                                @Override
                                public void onAnimatorScaleChanged(float scale) {
                                    ValueAnimator.setDurationScale(scale);
                                }
                            },
                            imm.getClient(), imm.getInputContext());

而mWindowSeesion通过windowManager.openSession()而来。是一个Binder对象。所以在这里涉及到了IPC机制,也就是把添加window的任务交给了WindowManagerService来完成。而WindowManagerService是一个独立的进程。
小结:
我们不必深入代码的具体实现,在这里我们大概知道添加window的流程其实就是
1.首先通过ViewRootImpl把view添加到window中,
2.然后通过ViewRootImpl类实现对View的绘制
3.最后通过IWindowSession mWindowSession;对象调用addToDisplay方法实现对window的添加。而mWindowSession是一个Binder对象,这里涉及到IIPC机制,跨进程通信。最终Window的添加其实交给了WindowManagerService完成。

相关文章

  • Android进阶(8)| Window和WindowManag

    一.Window和WindowManager 1.通过WindowManager添加Window的过程 代码如下:...

  • Window和WindowManager(一)

    前言 什么是Window?其实Window是一个抽象的概念,在日常开发中,可能很少遇到,但是有时候我们需要实现一些...

  • Window和WindowManager

    1.Window是抽象的概念,每个Window对应一个View和ViewRootImpl,实际上是以View的形式...

  • Window和WindowManager

    参考资料 凯子哥带你学Framework· Activity界面显示全解析-上凯子哥带你学Framework Ac...

  • Window和WindowManager

    参考《Android开发艺术探索》第八章

  • Window 和 WindowManager

    这篇文章是本着抛砖引玉的目的,如果有不严谨,甚至错误地方,劳驾各位指正。 有一群很(hua)好(qian)学(ma...

  • Window和WindowManager

    在WinodwManagerGlobal中,ViewRootImpl(在老版本中为ViewRoot类)起到了绘制V...

  • Window 和 WindowManager

    概述 window window 是一个抽象类,具体实现是PhoneWindow window 也是一个抽象的概念...

  • Window和WindowManager

    在屏幕上创建一个浮动试图 首先实现一个功能,在屏幕上创建一个浮窗,创建一个Window,这一步只需要使用Windo...

  • window和windowManager

    window是一个抽象类,它的具体实现时PhoneWindow,创建window是由windowManager来完...

网友评论

    本文标题:Window和WindowManager(一)

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