Android窗口Window和WindowManager详解

Android中所有的视图都是通过Window来呈现的,不管事Activity、Dialog还是Toast,他们的视图实际都是附加在Window上的。

Window是View的实际管理者,不管是View的事件分发机制还是setContentView,Window都是幕后主导。Activity的作用主要是

处理一些逻辑问题,比如管理生命周期、建立窗口等

Window是一个抽象类,他的具体实现是PhoneWindow,在前面已经多次提及过。

WindowManager和WindowManagerService的交互是一个IPC过程,我们在系统IPC篇还是继续介绍其他系统IPC


1 举例

//下面代码演示了通过WindowManager添加Window的过程
public class TestActivity extends Activity implements OnTouchListener {

    private static final String TAG = "TestActivity";

    private Button mCreateWindowButton;

    private Button mFloatingButton;
    private WindowManager.LayoutParams mLayoutParams;
    private WindowManager mWindowManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        initView();
    }

    private void initView() {
        mCreateWindowButton = (Button) findViewById(R.id.button1);
        mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
    }

    public void onButtonClick(View v) {
        if (v == mCreateWindowButton) {
            mFloatingButton = new Button(this);
            mFloatingButton.setText("click me");
            mLayoutParams = new WindowManager.LayoutParams(
                    LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0, 0,
                    PixelFormat.TRANSPARENT);
           //FLAG_NOT_TOUCH_MODAL在此模式下,系统会将当前Window区域外的单击事件传递给底层的Window,当前Window区域内的单击事件则自己处理
           //FLAG_NOT_FOCUSABLE表示Window不需要焦点,不需要接收输入
           //FLAG_SHOW_WHEN_LOCKED此模式下Window可以显示在锁屏的界面上
            mLayoutParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL
                    | LayoutParams.FLAG_NOT_FOCUSABLE
                    | LayoutParams.FLAG_SHOW_WHEN_LOCKED;
            //type参数表示Window的类型
            mLayoutParams.type = LayoutParams.TYPE_SYSTEM_ERROR;
            mLayoutParams.gravity = Gravity.CENTER | Gravity.TOP;
            mLayoutParams.x = 100;
            mLayoutParams.y = 300;
            mFloatingButton.setOnTouchListener(this);
	    //将mFloatingButton变成浮窗
            mWindowManager.addView(mFloatingButton, mLayoutParams);
        }
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        int rawX = (int) event.getRawX();
        int rawY = (int) event.getRawY();
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN: {
            break;
        }
        case MotionEvent.ACTION_MOVE: {
            int x = (int) event.getX();
            int y = (int) event.getY();
            mLayoutParams.x = rawX;
            mLayoutParams.y = rawY;
            mWindowManager.updateViewLayout(mFloatingButton, mLayoutParams);
            break;
        }
        case MotionEvent.ACTION_UP: {
            break;
        }
        default:
            break;
        }

        return false;
    }

    @Override
    protected void onDestroy() {
        try {
            mWindowManager.removeView(mFloatingButton);
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        }
        super.onDestroy();
    }
}
上面的例子主要涉及到了WindowManager.LayoutParams中的flags和type两个比较重要的参数,以及WindowManager常用的三个方法:

addView、updateViewLayout、removeView。


其中,flags表示Window的属性,Type表示Window的类型,Window有三种类型,分别是:

应用Window<——>Activity、子Window<——>Dialog、系统Window<——>Toast,系统状态栏


Window是分层的,对应type参数的三种类型。应用Window的层级在1~99,子Window的层级在1000~1999,系统Window的层级范围在2000~2999。

其中系统Window必须在AndroidManifest中申明相应的权限。


2 源码分析

(一)Window的创建过程

下面我们就从例子中WindowManager创建实例开始

        mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
(1)Activity

@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)) {
            return mWindowManager;
        } else if (SEARCH_SERVICE.equals(name)) {
            ensureSearchManager();
            return mSearchManager;
        }
        return super.getSystemService(name);
    }
getSystemService返回了mWindowManager,再来看看mWindowManager是在哪里实例化的

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

        mFragments.attachActivity(this, mContainer, null);
	//Window在这里实例化,mWindow就是PhoneWindow
        mWindow = PolicyManager.makeNewWindow(this);
        mWindow.setCallback(this);
        .......
        mUiThread = Thread.currentThread();

        mMainThread = aThread;
        mInstrumentation = instr;
        mToken = token;
        mIdent = ident;
        mApplication = application;
        mIntent = intent;
        mComponent = intent.getComponent();
        mActivityInfo = info;
        mTitle = title;
        mParent = parent;
        .......
        //WindowManager在这里实例化
        mWindowManager = mWindow.getWindowManager();
        mCurrentConfig = config;
    }


在Activity启动篇我们分析过,Activity的启动会通过ActivityThread中的performLaunchActivity来完成,在performLaunchActivity中通过类加载器创建Activity实例,并调用Activity的attach方法。从Activity的attach方法可以看出,系统会创建Activity所属的Window对象并为其设置回调接口。

Activity实现了Window的Callback接口,所以Window持有Activity的引用,Window接收到外界的状态变化是会回调Activity的方法。

对于attach的出现的PolicyManager.makeNewWindow(this),mWindow.setCallback(this),mWindowManager = mWindow.getWindowManager();我们深入分析下

(2)PolicyManager

public final class PolicyManager {
    private static final String POLICY_IMPL_CLASS_NAME =
        "com.android.internal.policy.impl.Policy";
    private static final IPolicy sPolicy;
    static {
        try {
            Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
	    //sPolicy就是Policy
            sPolicy = (IPolicy)policyClass.newInstance();
        } catch (ClassNotFoundException ex) {
          ......
        }
    }

    // Cannot instantiate this class
    private PolicyManager() {}

    // 通过Policy创建PhoneWindow
    public static Window makeNewWindow(Context context) {
        return sPolicy.makeNewWindow(context);
    }
    public static LayoutInflater makeNewLayoutInflater(Context context) {
        return sPolicy.makeNewLayoutInflater(context);
    }
    public static WindowManagerPolicy makeNewWindowManager() {
        return sPolicy.makeNewWindowManager();
    }
    public static FallbackEventHandler makeNewFallbackEventHandler(Context context) {
        return sPolicy.makeNewFallbackEventHandler(context);
    }
}

PolicyManager真正实现是Policy,PolicyManager 在静态块中通过反射获得Policy对象,通过Policy创建了PhoneWindow,下面看看Policy

public static Window makeNewWindow(Context context) {
        return sPolicy.makeNewWindow(context);
    }

(3)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$DialogMenuCallback",
        "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);
            }
        }
    }

    public Window makeNewWindow(Context context) {
        return new PhoneWindow(context);
    }

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

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

    public FallbackEventHandler makeNewFallbackEventHandler(Context context) {
        return new PhoneFallbackEventHandler(context);
    }
}
Policy直接new了一个PhoneWindow,IPolicy是一个普通接口。到这里Window的创建完成了。Activity的视图是怎么附属在Window上的呢?

我们在setContentView篇已经详细讲解过,下面再复习下

(4)Activity

public void setContentView(int layoutResID) {
        getWindow().setContentView(layoutResID);
        initActionBar();
    }

getWindow()获取的就是我们上一步创建的PhoneWindow实例

(5)PhoneWindow
 @Override
    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            installDecor();
        } else {
            mContentParent.removeAllViews();
        }
        mLayoutInflater.inflate(layoutResID, mContentParent);
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
    }


下面的过程和setContent就完全一样了,详情请参考 Android布局加载之setContentView源码分析
整个过程概述:创建DecorView,此时DecorView是空白的,然后根据theme参数获取到用户指定的具体主题布局,将具体主题布局inflate成View,并添加到DecorView中,将具体主题布局中id为content的FrameLayout返回给mContentParent,然后将Activity的视图加载进入mContentParent即mLayoutInflater.inflate(layoutResID, mContentParent),很显然mContentParent是Activity视图的根布局。


整个setContentView过程将Activity视图加入到了DecorView中,然而此时DecorView还没有和PhoneWindow关联上?他们是在哪里关联上的呢?

在ActivityThread的handleResumeActivity方法中,首先会调用Activity的onResume方法,接着调用Actiivty的makeVisible方法,正是在makeVisible()中,

DecorView完成了添加到Window和显示这两个过程

(6)Activity

void makeVisible() {
        if (!mWindowAdded) {
            ViewManager wm = getWindowManager();
            wm.addView(mDecor, getWindow().getAttributes());
            mWindowAdded = true;
        }
        mDecor.setVisibility(View.VISIBLE);
    }
到这里Activity中的Window创建过程分析完毕。

(二)Window的添加

Window的添加过程通过WindowManager的addView来完成,WindowManager真正的实现是WindowManagerImpl

在Window创建的源码分析中,我们看到Activity的attach方法中实例化了WindowManager,这里的mWindowManager = mWindow.getWindowManager()中,mWindowManager 其实就是WindowManagerImpl。所以重点看WindowManagerImpl的三大操作

(1)WindowManagerImpl

@Override
    public void addView(View view, ViewGroup.LayoutParams params) {
        mGlobal.addView(view, params, mDisplay, mParentWindow);
    }

    @Override
    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
        mGlobal.updateViewLayout(view, params);
    }

    @Override
    public void removeView(View view) {
        mGlobal.removeView(view, false);
    }
再来看看mGlobal是什么鬼?

    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
WindowManagerImpl这种工作模式是典型的桥接模式,WindowManagerImpl将所有的操作全部委托给WindowManagerGlobal来实现。所以重点落在了WindowManagerGlobal上面

(2)WindowManagerGlobal

public static WindowManagerGlobal getInstance() {
        synchronized (WindowManagerGlobal.class) {
            if (sDefaultWindowManager == null) {
                sDefaultWindowManager = new WindowManagerGlobal();
            }
            return sDefaultWindowManager;
        }
    }
重点看看WindowManagerGlobal的addView()方法

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        //检查参数是否合法
        ......

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
	//如果是子Window那么还需要调整一些布局参数
        if (parentWindow != null) {
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        }

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

            view.setLayoutParams(wparams);

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

        try {
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
            ......
        }
    }
这里涉及到几个比较重要的容器

//mViews存储的是所有Window对应的View
private final ArrayList<View> mViews = new ArrayList<View>();

//mRoots存储的是所有Window对应的ViewRootImpl
 private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();

//mParams存储的是所有Window所对应的布局参数
 private final ArrayList<WindowManager.LayoutParams> mParams =new ArrayList<WindowManager.LayoutParams>();

//mDyingView则存储着那些正在被删除的View对象,
private final ArraySet<View> mDyingViews = new ArraySet<View>();
最后通过ViewRootImpl来更新界面并完成Window的添加过程,root.setView(view, wparams, panelParentView);

(3)ViewRootImpl

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
                ......
                requestLayout();
                ......
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, 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();
                    }
                }
              ......
    }
setView内部通过requestLayout来完成一部刷新请求,接着会通过WindowSession最终完成Window的添加过程。
@Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }
scheduleTraversals是绘制View的入口,mWindowSession.addToDisplay完成Window的添加过程
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mInputChannel);
这里的mWindowSession是Session的一个代理,这里其实就是一个IPC通信过程,所以重点在Session

(4)Session

final class Session extends IWindowSession.Stub
        implements IBinder.DeathRecipient {
@Override
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outContentInsets,
            InputChannel outInputChannel) {
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                outContentInsets, outInputChannel);
    }
这里mService是WindowManagerService类型,所以在Session内部会通过WindowManagerService来实现Window的添加,这里暂时不做深入了解


(三)Window删除
Window的删除过程和添加过程一样,都是先通过WindowManagerImp后,在进一步通过WindowManagerGlobal来实现,下面是WindowManagerGlobal的删除过程

(1)WindowManagerGlobal

public void removeView(View view, boolean immediate) {
        .....
        synchronized (mLock) {
            //遍历查找要删除View的index
            int index = findViewLocked(view, true);
            View curView = mRoots.get(index).getView();
            //这里进行删除
            removeViewLocked(index, immediate);
            if (curView == view) {
                return;
            }

         ......
        }
    }
private int findViewLocked(View view, boolean required) {
        final int index = mViews.indexOf(view);
        if (required && index < 0) {
            throw new IllegalArgumentException("View=" + view + " not attached to window manager");
        }
        return index;
    }
private void removeViewLocked(int index, boolean immediate) {
        ViewRootImpl root = mRoots.get(index);
        View view = root.getView();
        ......
        boolean deferred = root.die(immediate);
        if (view != null) {
            view.assignParent(null);
            if (deferred) {
                mDyingViews.add(view);
            }
        }
    }
这里具体的删除操作在root.die,这里的root是ViewRootImpl类型,

(2)ViewRootImpl

boolean die(boolean immediate) {
        // Make sure we do execute immediately if we are in the middle of a traversal or the damage
        // done by dispatchDetachedFromWindow will cause havoc on return.
        if (immediate && !mIsInTraversal) {
            doDie();
            return false;
        }

        if (!mIsDrawing) {
            destroyHardwareRenderer();
        } else {
            Log.e(TAG, "Attempting to destroy the window while drawing!\n" +
                    "  window=" + this + ", title=" + mWindowAttributes.getTitle());
        }
        mHandler.sendEmptyMessage(MSG_DIE);
        return true;
    }
这里的removeView是异步删除,die方法只是发送了一个请求删除的消息,就立即返回了,此时View并没有完成删除操作,所以最后会将其添加到mDyingView中,

mDyingView表示待删除的View列表。

如果是同步操作,那就不发送消息直接调用doDie()

    final ViewRootHandler mHandler = new ViewRootHandler();
final class ViewRootHandler extends Handler {
        @Override
        public String getMessageName(Message message) {
            switch (message.what) {
                ......
                case MSG_DIE:
                    return "MSG_DIE";
                ......
            }
            return super.getMessageName(message);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            ......
            case MSG_DIE:
                doDie();
                break;
            ......
        }
    }
这里异步操作发送消息后最后还是调用到了doDie。

doDie内部会调用dispatchDetachedFromWindow方法,真正删除View的逻辑在dispatchDetachedFromWindow方法的内部实现,dispatchDetachedFromWindow方法通过Session的remove方法删除Window,mWindowSession.remove(mWindow),这同样也是一个IPC过程

通过WindowManagerGlobal的doRemoveView方法刷新数据,包块mRoots、mParams以及mDyingViews,将当前Window所关联的这三类对象从列表中删除。


(四)Window更新

Window的更新同样从WindowManagerGlobal的updateViewLayout方法,如下所示:

(1)WindowManagerGlobal

public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }
        if (!(params instanceof WindowManager.LayoutParams)) {
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
        }

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;

        view.setLayoutParams(wparams);

        synchronized (mLock) {
            int index = findViewLocked(view, true);
            ViewRootImpl root = mRoots.get(index);
            mParams.remove(index);
            mParams.add(index, wparams);
            root.setLayoutParams(wparams, false);
        }
    }
updateViewLayout更新View的LayoutParams并替换掉老的LayoutParams,接着再更新ViewRootImpl中的LayoutParams(root.setLayoutParams(wparams, false)),ViewRootImpl通过scheduleTraversals方法对View重新布局,还通过WindowSession来更新Window的视图,同样是一个IPC过程。


















  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值