android Window和WindowManager

          前言:Window表示一个窗口的概念,在日常的开发过程中我们很少用到它,但是在某些特定的场景下我们会使用到。查看源码我们知道Window是一个抽象类,而它的实现类是PhoneWindow。而PhoneWindow又是在哪里实例化的呢?不明白的同学可以去我这篇文章看看https://blog.csdn.net/qq_27970997/article/details/83658203。那么Window到底是如何工作的呢?这里面就涉及到另外的两个类:WindowManager和WindowManagerService。那么它们之间的关系又是什么样子的呢?

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

 

先来看一段代码,这是ActivityThread$handleResumeActivity()方法里面的一段代码

 // 这是ActivityThread$handleResumeActivity()方法里面的一段代码
 // 我们一行一行读
 if (r.window == null && !a.mFinished && willBeVisible) {
    // r代表的是ActivityClientRecord 
    // ActivityClientRecord 用来保存Activity的信息
    // 获取ActivityClientRecord的Window
    r.window = r.activity.getWindow();
    // 获取ActivityClientRecord的DecorView
    View decor = r.window.getDecorView();
    // 设置DecorView为不可见
    decor.setVisibility(View.INVISIBLE);
    // 拿到当前Activity的WindowManager(WindowManager实现了ViewManager接口)
    ViewManager wm = a.getWindowManager();
    WindowManager.LayoutParams l = r.window.getAttributes();
    // 设置当前Activity的DecorView
    a.mDecor = decor;
    l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
    l.softInputMode |= forwardBit;
    if (r.mPreserveWindow) {
        a.mWindowAdded = true;
        r.mPreserveWindow = false;
        ViewRootImpl impl = decor.getViewRootImpl();
        if (impl != null) {
            impl.notifyChildRebuilt();
        }
    }
    
    if (a.mVisibleFromClient) {
        if (!a.mWindowAdded) {
            a.mWindowAdded = true;
	    // 把DecorView添加到Window(通过WindowManager进行添加)中
            wm.addView(decor, l);
        } else {
            a.onWindowAttributesChanged(l);
        }
    }
}

重点看13,14和32行,这里我就不啰嗦了,看我的注释应该就能看懂了。这个代码很好的解释了Window的外界访问入口WindowManager是如何添加控件的。

当然,可以给Window设置Flags属性:举几个常见的属性

1》FLAG_NOT_FOCUSABLE

     表示Window不需要获取焦点,也不需要接收各种输入事件,此标记会同时启用FLAG_NOT_TOUCH_MODAL,最终事件会直接传递给下层的具有焦点的Window

2》FLAG_NOT_TOUCH_MODAL

      在此模式下,系统会将当前Window区域以外的单击事件传递给底层的Window,当前Window区域以内的单击事件则自己处理。一般来说此标记是开启状态,否则其他Window将无法收到单击事件

3》FLAG_SHOW_WHEN_LOCKED

      开启此模式可以让Window显示在锁屏的界面上

 

 

WindowManager

       因为WindowManager是实现了ViewManager接口,所以我们先来看看ViewManager接口里面有些什么东西

public interface ViewManager
{
    /**
     * Assign the passed LayoutParams to the passed View and add the view to the window.
     * 

Throws {@link android.view.WindowManager.BadTokenException} for certain programming * errors, such as adding a second view to a window without removing the first view. *

Throws {@link android.view.WindowManager.InvalidDisplayException} if the window is on a * secondary {@link Display} and the specified display can't be found * (see {@link android.app.Presentation}). * @param view The view to be added to this window. * @param params The LayoutParams to assign to view. */ public void addView(View view, ViewGroup.LayoutParams params); public void updateViewLayout(View view, ViewGroup.LayoutParams params); public void removeView(View view); }

ViewManager接口提供了3个方法,一个添加视图,一个更新视图,一个移除视图,由此可知,WindowManager也具有这3个方法。需要注意的是,我们操作的对象是View。由此看来,WindowManager操作Window的过程其实是操作Window里面的View的过程。

 

Window

      Window的添加过程需要通过WindowManager的addView来实现,而WindowManager是一个接口,它的真正实现是WindowManagerImpl类

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

   在WindowManagerImpl类中我们找到对应的addView(),updateViewLayout(),removeView()方法,但是我们发现WindowManagerImpl并没有直接实现Window的三大操作,而是全部交给了WindowManagerGlobal来处理,那么我们接着往下走,看看WindowManagerGlobal里面是怎么进行处理的(以addView()为例)

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        // 如果添加进来的View为空,则直接抛出异常 	
        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");
        }

	// 如果Window不为空,调整一些布局参数
        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:实现View的绘制
        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            
	    // 省略部分代码  
            // 实例化ViewRootImpl 
            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);
            // mViews存储的是所有Window所对应的View
            mViews.add(view);
	    // mRoots存储的是所有Window所对应的ViewRootImpl
            mRoots.add(root);
	    // mParams存储的是所有Window所对应的布局参数
            mParams.add(wparams);

            // do this last because it fires off messages to start doing things
            try {
	        // 开始进行View的绘制
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }
 public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                // 省略代码
                requestLayout();
                // 省略代码
            }
    }
}

我们知道 requestLayout();其实就是刷新整个页面布局的方法,那么我们来看看 requestLayout();方法里面做了些什么

    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }
   void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }

第一步:checkThread()即检查线程。有没有发现这个错误很熟悉,在开发过程中我们都知道不能在子线程中更新UI,现在知道

为什么了吧!

 

第二步:执行scheduleTraversals()方法

    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

我们关注最后一行

void pokeDrawLockIfNeeded() {
        final int displayState = mAttachInfo.mDisplayState;
        if (mView != null && mAdded && mTraversalScheduled
                && (displayState == Display.STATE_DOZE
                        || displayState == Display.STATE_DOZE_SUSPEND)) {
            try {
                mWindowSession.pokeDrawLock(mWindow);
            } catch (RemoteException ex) {
                // System server died, oh well.
            }
        }
    }

最终会执行 mWindowSession.pokeDrawLock(mWindow),其中mWindowSession是IWindowSession,它是一个Binder对象,真正的实现类是Session,具体的IPC的调用过程有兴趣的同学可以自己去研究研究。最终会执行到WindowManagerService里面的pokeDrawLock去执行

    @Override
    public void pokeDrawLock(IBinder window) {
        final long identity = Binder.clearCallingIdentity();
        try {
            mService.pokeDrawLock(this, window);
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
    }
  public void pokeDrawLock(Session session, IBinder token) {
        synchronized (mWindowMap) {
            WindowState window = windowForClientLocked(session, token, false);
            if (window != null) {
                window.pokeDrawLockLw(mDrawLockTimeoutMillis);
            }
        }
    }

那么Activity是如何创建Window的呢,这就涉及到Activity的启动流程了,具体的我就不再细讲了,不明白的同学可以去我的另外一篇博客去看看,里面有详解https://blog.csdn.net/qq_27970997/article/details/83658203

这里我稍微总结一下:

1.Activity的启动过程很复杂,但是最终会执行到handleLaunchActivity()方法,这个方法里面首先是执行

performLaunchActivity()方法,在performLaunchActivity()方法里面首先通过反射生成Activity的实例

,然后调用生命周期里面的attach()方法,create(),start()方法

2.在attach()方法里面会做以下几个事情:

   1》实例化PhoneWindow

   2》获取WindowManager并进行设置

3.在onCreate()方法里面调用setContentView()方法,实例化DecorView并将我们设置的布局文件添加到ID为com.android.internal.R.id.content的容器中

4.回到handleLaunchActivity()方法,执行handleResumeActivity()方法,里面有这几行代码

 if (!a.mWindowAdded) {
      a.mWindowAdded = true;
      wm.addView(decor, l);
} 

DecorView被添加到Window当中,这样我们的整个流程就走完了,界面就显示出来了。

你可能感兴趣的:(源码分析)