IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> WindowManagerService学习(上) -> 正文阅读

[移动开发]WindowManagerService学习(上)

WindowManagerService (WMS )和 AMS 一样,都是 Android 应用开发需要掌握的知
识点。 WMS 也很复杂并且功能繁多,需要花很多篇幅来进行讲解。如果直接讲解 WMS
很难理解,为了更好地理解 WMS ,需要先了解 WindowManager 的相关知识,这是因为从
应用开发角度来看, WindowManager 是与 WMS 关联最紧密的类。

一.Window、WindowManager、WMS

1.简介
Window:在Android视图体系中Window就是一个窗口的概念。Android中所有的视图都是依赖于Window显示的(在activity中他的实现类是PhoneWindow),可以这样理解Window是View的一个容器,而View是Window的具体表现。
WindowManager:WindowManager 个接口类,继承自接口 ViewManager ,从名称就知道它是
用来管理 Window 的,它的实现类为 WindowManager Imp对Window的管理,包括新增、更新和删除等。
WMS:窗口的最终管理者,它负责窗口的启动、添加和删除,另外窗口的大小和层级也是由WMS进行管理。
在这里插入图片描述
2.常见的Window
在这里插入图片描述
3.窗口的次序
当一个进程向 WMS 申请一个窗口时, WMS 会为窗口确定显示次序。为了方便窗口显示次序的管理,手机屏幕可以虚拟地用 轴来表示,其中 轴垂直于屏幕,从屏幕内指向屏幕外,这样确定窗口显示次序也就是确定窗口在 轴上的次序,这个次序称为Z -Oder。Type 值是 -Oder 排序的依据,我们知道应用程序窗口的 Type 值范围为0- 99,子窗口 1000-1999 ,系统窗口 2000-2999 ,在 般情况下, Type 值越大则 Z-Oder 排序越靠前,就越靠近用户。当然窗口显示次序的逻辑不会这么简单,情况会 较多,举个常见
的情况 当多个窗口的 Type 值都是 TYPE_APPLICATION ,这时 WMS 会结合各种情况给出最终的 Z-Oder。Z-Oder大的窗口会覆盖在Z-Oder比较小的窗口之上

如下图所示,第一个图片是一个选择注册的界面,第二张图片是一个注册界面,第三张图片是一个注册成功的界面,加入我通过点击同选择注册界面,到注册界面,再到注册成功的界面(假设跳转的时候没有finish),那么在注册成功的界面的窗口排列次序如最右边的图所示。

在这里插入图片描述
4.Window分类
Application Window:Activity就是一个典型的应用程序窗口。
Sub Window:子窗口,顾名思义,它不能独立存在,需要附着在其他窗口才可以,PopupWindow就属于子窗口。
System Window:输入法窗口、系统音量条窗口、系统错误窗口都属于系统窗口。
在这里插入图片描述
type = ApplicationWindow时的 Z-Oder

public static final int FIRST_APPLICATION_WINDOW = 1; // 这种类型 Z-Oder的下界

public static final int TYPE_BASE_APPLICATION = 1;

public static final int TYPE_APPLICATION = 2;

public static final int TYPE_APPLICATION_STARTING = 3; // 临时窗口(比如启动app时的过渡白屏)

public static final int TYPE_DRAWN_APPLICATION = 4;

public static final int LAST_APPLICATION_WINDOW = 99; // 这种类型 Z-Oder的上界
在这里插入图片描述

type = Sub Window时的 Z-Oder

public static final int FIRST_SUB_WINDOW = 1000; // 这种类型 Z-Oder的下界
public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;
public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1;
public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;
public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;
public static final int TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4;
public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5;
public static final int LAST_SUB_WINDOW = 1999; // 这种类型 Z-Oder的上界
在这里插入图片描述

type =System Window时的 Z-Oder

 public static final int FIRST_SYSTEM_WINDOW     = 2000; //  这种类型 Z-Oder的下界
   public static final int TYPE_STATUS_BAR         = FIRST_SYSTEM_WINDOW;
    public static final int TYPE_SEARCH_BAR         = FIRST_SYSTEM_WINDOW+1;
    public static final int TYPE_PHONE              = FIRST_SYSTEM_WINDOW+2;
    public static final int TYPE_SYSTEM_ALERT       = FIRST_SYSTEM_WINDOW+3;
    public static final int TYPE_KEYGUARD           = FIRST_SYSTEM_WINDOW+4;
    public static final int TYPE_TOAST              = FIRST_SYSTEM_WINDOW+5;
   ..
    public static final int LAST_SYSTEM_WINDOW      = 2999; // 这种类型 Z-Oder的上界

在这里插入图片描述

5.窗口的次序
系统窗口位于最上面,子窗口次之,应用程序窗口位于最下面,
在这里插入图片描述
7.WindowManager
WindowManager是一个接口,在Android中他的实现类是WindowManagerImpl。我们新增窗口,更新窗口。删除窗口的能力来自于WindowManager,最终这些操作都会交给WindowManagerGloble进行处理。
在这里插入图片描述
8.添加Window
Activity#attach()方法之内PhoneWindow被创建,并同时创建一WindowManagerImpl负责维护PhoneWindow内的内容。
在Activity#onCreate()中调用setContentView()方法,这个方法内部创建一个DecorView实例作为PhoneWindow的内容。
WindowManagerImpl决定管理DecorView,并创建一个ViewRootImpl实例,将ViewRootImpl与View树进行关联,这样ViewRootImpl就可以指挥View树的具体工作。
在这里插入图片描述
在这里插入图片描述

如下代码所示,在ActivityThread的performLaunchActivity通过反射的方式创建出activity,然后在后面调用了该activity的attach方法。

ActivityThread.java '
 private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
		...
	 Activity activity = null;
        try {
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity( // 在这里通过反射创建出activity对象
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess(isProtectedComponent(r.activityInfo),
                    appContext.getAttributionSource());
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        ...
       // 在这里调用了activity的attach方法
		 activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback,
                        r.assistToken, r.shareableActivityToken);

如下代码,在activity的attach函数中,创建出了PhoneWindow的一个对象,然后在之后创建出了一个WindowManager对象,然后使得WindowManager和我们创建出的PhoneWindow产生关联。

Activity.java

   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, ActivityConfigCallback activityConfigCallback, IBinder assistToken,
            IBinder shareableActivityToken) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);

        mWindow = new PhoneWindow(this, window, activityConfigCallback); // 在这里创建出了
        // PhoneWindow
        ... 
        // 产生一个WindowManager对象,和我们创建的phoneWindow对象关联
        mWindow.setWindowManager(
           (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
            mToken, mComponent.flattenToString(),
            (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        

之后在activity的onCreate中的setContentView方法中创建了DecorView,
Window,PhoneWindow,WindowManager,WindowManagerImpl在这里运用了桥接的设计模式。

activity与 PhoneWindow与DecorView关系

在这里插入图片描述

DecorView是FrameLayout的子类,它可以被认为是Android视图树的根节点视图

在这里插入图片描述

如下代码: activity与 PhoneWindow与DecorView与WindowMnager建立了关系

ActivityThread.java 

  public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,
            boolean isForward, String reason) {
            ...
                // skip below steps for double-resume and r.mFinish = true case.
         // 这里会调用activity的onResume方法
        if (!performResumeActivity(r, finalStateRequest, reason)) {
            return;
        }
        
		...
   if (r.window == null && !a.mFinished && willBeVisible) {
            r.window = r.activity.getWindow();
            View decor = r.window.getDecorView();  // 获取decorview
            decor.setVisibility(View.INVISIBLE);
            ViewManager wm = a.getWindowManager();  // 获取activity的WindowManager
            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;     // activity和decorview产生关联
            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
            l.softInputMode |= forwardBit;
            if (r.mPreserveWindow) {
                a.mWindowAdded = true;
                r.mPreserveWindow = false;
                // Normally the ViewRoot sets up callbacks with the Activity
                // in addView->ViewRootImpl#setView. If we are instead reusing
                // the decor view we have to notify the view root that the
                // callbacks may have changed.
                ViewRootImpl impl = decor.getViewRootImpl(); // 获得decorview的ViewRootImpl 
                if (impl != null) {
                    impl.notifyChildRebuilt();
                }
            }
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);  // 把decorview以及他对应的布局参数添加给WindowManager

上面的代码可以看得出,最后把调用了WindowManager的addView,WindowManager是一个接口,他的实现类是WindowManagerImpl,如下代码:

	WindowManagerImpl.java
	
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        // mGloabal是WindowManagerGlobal,(WindowManagerGlobal是一个单例)
        mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
                mContext.getUserId());
    }

如上的代码,可以看出最后调用了WindowManagerGlobal的addView。WindowManagerGlobal可以认为是一个全局的Window管理者。作为管理者WindowManagerGlobal会管理View(DecorView),布局参数和ViewRootImpl,说道ViewRootImpl,先来简单了解一下ViewRootImpl。

ViewRootImpl

  • View树的树根并管理View树

  • 触发View的测量、布局和绘制

  • 输入响应的中转站

  • 负责与WMS进行进程间通信
    在这里插入图片描述

ViewRootImpl的类注释是这样描述的:
视图层次结构的顶部,在视图之间实现所需的协议还有WindowManager。这在很大程度上是一个内部实现细节。
ViewRootImpl非常重要,WindowManager通过ViewRootImpl与DecorView起联系。并且,View的绘制流程都是由ViewRootImpl发起的。

ViewRootImpl是View中的最高层级,属于所有View的根(但ViewRootImpl不是View,只是实现了ViewParent接口),实现了View和WindowManager之间的通信协议,实现的具体细节在WindowManagerGlobal这个类当中。
在这里插入图片描述
回到代码:如下,通过 mViews.add(view);mRoots.add(root);mParams.add(wparams);添加一个Window的流程算是走完了。

WindowManagerGlobal.java 


  public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow, int userId) {
        //  检查参数是否合法
        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;
            }
        }
		//创建ViwRootImpl
        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);
			// 将Window所对应的View、ViewRootImpl、LayoutParams顺序添加在WindowManager中
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);

            // do this last because it fires off messages to start doing things
            try {
            	//把将Window所对应的View设置给创建的ViewRootImpl
            //通过ViewRootImpl来更新界面并完成Window的添加过程
                root.setView(view, wparams, panelParentView, userId);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }

9.绘制流程
绘制流程我之前分析过,这里简单提一下,具体见View的绘制流程

当收到了一个同步信号,就开始进行测量布局和绘制。

在这里插入图片描述
10.更新Window

在这里插入图片描述
在这里插入图片描述

当窗口发生变化的时候(比如横竖屏切换,弹出一个Dialog…),这些变化会让WindowManagerImpl调用updateViewLayout。

WindowManagerImpl.java
      @Override
    public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
         // 这里调用了WindowManagerGloble的的
        mGlobal.updateViewLayout(view, params);
    }

如上的代码,WindowManagerImpl中调用了WindowManagerGloble的updateViewLayout

WindowManagerGloble.java 

    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
        if (view == null) { // 检查view(DevorView)是否为空
            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;
		// 把布局参数添加到 DecorVeiw中
        view.setLayoutParams(wparams);

        synchronized (mLock) {
            int index = findViewLocked(view, true);
            // 找出该DecorVeiw对应的ViewRootImpl 
            ViewRootImpl root = mRoots.get(index);
            mParams.remove(index);
            mParams.add(index, wparams);
            // 调用ViewRootImpl 的 setLayoutParams
            root.setLayoutParams(wparams, false);
        }
    }

如上代码,接下来会调用该Window对应的ViewRootImpl 的setLayoutParams方法。

ViewRootImpl.java

 void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {
        synchronized (this) {
        // 省略了大量的代码
         scheduleTraversals(); // 在这里引起重绘

如上代码,最终会产生一个重绘,这个重绘会调用到WindowManagerImpl的performTraversales。窗口更新的时候会引起一个重绘。
在这里插入图片描述

9.刷新流程
*UI刷新:*以电影为例,动画至少要达到24FPS,才能保证画面的流畅性,低于这个值,肉眼会感觉到卡顿。在手机上,这个值被调整到60FPS,增加丝滑度,这也是为什么有个(1000/60)16ms的指标,一般而言目前的Android系统FPS也就是60,它是通过了一个VSYNC来保证每16ms最多绘制一帧。简而言之:UI必须至少等待16ms的间隔才会绘制下一帧,所以连续两次setTextView只会触发一次重绘。
在这里插入图片描述

接下来,将以TextView.setText()为例来说明Window刷新的流程:
在这里插入图片描述
在这里插入图片描述

等到VSYNC到来后,会移除同步栅栏,并率先开始执行当前帧的处理,调用逻辑如下

在这里插入图片描述
代码分析:

TextView.java

 private void setText(CharSequence text, BufferType type,
                         boolean notifyBefore, int oldlen) {
           ....
          checkForRelayout();
           ....
           }

在TextView的setText方法中会调用 checkForRelayout方法,这个方法的作用是检查全新文本是需要新的视图布局还是仅需要新的文本布局。

  private void checkForRelayout() {
        // If we have a fixed width, we can just swap in a new text layout
        // if the text height stays the same or if the view height is fixed.

        if ((mLayoutParams.width != LayoutParams.WRAP_CONTENT
                || (mMaxWidthMode == mMinWidthMode && mMaxWidth == mMinWidth))
                && (mHint == null || mHintLayout != null)
                && (mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight() > 0)) {
            // Static width, so try making a new text layout.

            int oldht = mLayout.getHeight();
            int want = mLayout.getWidth();
            int hintWant = mHintLayout == null ? 0 : mHintLayout.getWidth();

            /*
             * No need to bring the text into view, since the size is not
             * changing (unless we do the requestLayout(), in which case it
             * will happen at measure).
             */
            makeNewLayout(want, hintWant, UNKNOWN_BORING, UNKNOWN_BORING,
                          mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight(),
                          false);

            if (mEllipsize != TextUtils.TruncateAt.MARQUEE) {
                // In a fixed-height view, so use our new text layout.
                if (mLayoutParams.height != LayoutParams.WRAP_CONTENT
                        && mLayoutParams.height != LayoutParams.MATCH_PARENT) {
                    autoSizeText();
                    invalidate();
                    return;
                }

                // Dynamic height, but height has stayed the same,
                // so use our new text layout.
                if (mLayout.getHeight() == oldht
                        && (mHintLayout == null || mHintLayout.getHeight() == oldht)) {
                    autoSizeText();
                    invalidate();
                    return;
                }
            }

            // We lose: the height has changed and we have a dynamic height.
            // Request a new view layout using our new text layout.
           // 注意这里
            requestLayout();
            invalidate();
        } else {
            // Dynamic width, so we have no choice but to request a new
            // view layout with a new text layout.
            nullLayouts();
            requestLayout();
            invalidate();
        }
    }

在requestLayout方法中,首先先判断当前View树是否正在布局流程,接着为当前子View设置标记位,该标记位的作用就是标记了当前的View是需要进行重新布局的,接着调用mParent.requestLayout方法,这个十分重要,因为这里是向父容器请求布局,即调用父容器的requestLayout方法,为父容器添加PFLAG_FORCE_LAYOUT标记位,而父容器又会调用它的父容器的requestLayout方法,即requestLayout事件层层向上传递,直到DecorView,即根View,而根View又会传递给ViewRootImpl,也即是说子View的requestLayout事件,最终会被ViewRootImpl接收并得到处理。纵观这个向上传递的流程,其实是采用了责任链模式,即不断向上传递该事件,直到找到能处理该事件的上级,在这里,只有ViewRootImpl能够处理requestLayout事件。关于requestLayout和invalidate详细的分析见Android View 深度分析requestLayout、invalidate与postInvalidate
在这里插入图片描述

invalidate一路向上 标志脏区,然后会执行到ViewRootImpl的scheduleTraversals方法。如下

ViewRootImpl.java
	// schedule的英文含义是计划,顾名思义,这个函数就是计划绘制了
    void scheduleTraversals() {
       //设置一个成员变量,标明是否正在执行
        if (!mTraversalScheduled) {
        	//标记位设置为true,避免重复执行
            mTraversalScheduled = true;
            // 设置同步屏障
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

可以看到,上面的代码中,调用mChoreographer.postCallback()函数时候,post的是一个mTraversalRunnable,我们来看下这个mTraversalRunnable是什么。

 final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

TraversalRunnable类定义在ViewRootImpl中,如下:

final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }

该类实现了Runnable接口,在run()方法中,执行了doTraversal()函数,我们接着看下doTraversal()函数。

void doTraversal() {
        if (mTraversalScheduled) {
            //将在上面scheduleTraversals()方法中设置的标记为置为false
            mTraversalScheduled = false;
            //移除同步屏障(使用添加同步屏障的时候返回的mTraversalBarrier变量)
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }
            //真正开始绘制流程
            performTraversals();

            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }

综上分析,我们可以得出结论,在我们调用ViewRootImpl的scheduleTraversals()方法后,并不是立即就开始执行performTraversals()的,而是需要等到使用Choreographer类的postCallback()方法post出去的Runnable回调执行run()方法的时候,才开始真正的绘制流程,即调用performTraversals()方法。

再看看上面的代码:

ViewRootImpl.java
	// schedule的英文含义是计划,顾名思义,这个函数就是计划绘制了
    void scheduleTraversals() {
       //设置一个成员变量,标明是否正在执行
        if (!mTraversalScheduled) {
        	//标记位设置为true,避免重复执行
            mTraversalScheduled = true;
            // 设置同步屏障
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

Choreographer的postCallback()分析:(这里的分析借鉴了Android中老生常谈的ViewRootImpl的scheduleTraversals…

Choreographer.java

public void postCallback(int callbackType, Runnable action, Object token) {
        postCallbackDelayed(callbackType, action, token, 0);
    }

通过源码可以发现,第一个参数在Choreographer类中有五个不同的值

Choreographer.java
/**
     * Callback type: Input callback.  Runs first.
     * @hide 
     */
    public static final int CALLBACK_INPUT = 0;

    /**
     * Callback type: Animation callback.  Runs before {@link #CALLBACK_INSETS_ANIMATION}.
     * @hide
     */
    @TestApi
    public static final int CALLBACK_ANIMATION = 1;

    /**
     * Callback type: Animation callback to handle inset updates. This is separate from
     * {@link #CALLBACK_ANIMATION} as we need to "gather" all inset animation updates via
     * {@link WindowInsetsAnimationController#changeInsets} for multiple ongoing animations but then
     * update the whole view system with a single callback to {@link View#dispatchWindowInsetsAnimationProgress}
     * that contains all the combined updated insets.
     * <p>
     * Both input and animation may change insets, so we need to run this after these callbacks, but
     * before traversals.
     * <p>
     * Runs before traversals.
     * @hide
     */
    public static final int CALLBACK_INSETS_ANIMATION = 2;

    /**
     * Callback type: Traversal callback.  Handles layout and draw.  Runs
     * after all other asynchronous messages have been handled.
     * @hide
     */
    public static final int CALLBACK_TRAVERSAL = 3;

    /**
     * Callback type: Commit callback.  Handles post-draw operations for the frame.
     * Runs after traversal completes.  The {@link #getFrameTime() frame time} reported
     * during this callback may be updated to reflect delays that occurred while
     * traversals were in progress in case heavy layout operations caused some frames
     * to be skipped.  The frame time reported during this callback provides a better
     * estimate of the start time of the frame in which animations (and other updates
     * to the view hierarchy state) actually took effect.
     * @hide
     */
    public static final int CALLBACK_COMMIT = 4;

事件值 解释说明
CALLBACK_INPUT: 输入事件,第一个执行
CALLBACK_ANIMATION: 动画,在CALLBACK_INSETS_ANIMATION前执行
CALLBACK_INSETS_ANIMATION: 输入和动画之后,TRAVERSAL之前
CALLBACK_TRAVERSAL: 处理布局和绘制,运行在其他异步消息之后 CALLBACK_COMMIT 提交,最后执行

第二个参数需要的是一个Runnable

第三个参数为Object token

我们在ViewRootImpl中调用scheduleTraversals()方法中

mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);

第一个参数传入的是CALLBACK_TRAVERSAL(布局和绘制)。
第二个参数传入的是mTraversalRunnable,上面已经说过,就是一个实现了Runnable接口的TraversalRunnable类。
第三个参数传入的是null。

如下代码,在mChoreographer.postCallback中有调用了 postCallbackDelayed

Choreographer.java

    public void postCallback(int callbackType, Runnable action, Object token) {
        postCallbackDelayed(callbackType, action, token, 0);
    }

如下代码,在经过一些检查后又调用了postCallbackDelayedInternal

Choreographer.java

   public void postCallbackDelayed(int callbackType,
            Runnable action, Object token, long delayMillis) {
            // 进行一些参数的检查
        if (action == null) {
            throw new IllegalArgumentException("action must not be null");
        }
        if (callbackType < 0 || callbackType > CALLBACK_LAST) {
            throw new IllegalArgumentException("callbackType is invalid");
        }
		// 这里
        postCallbackDelayedInternal(callbackType, action, token, delayMillis);
    }

如下代码中,mCallbackQueues是Choreographer类中的一个数组,初始化是在Choreographer类的构造方法中

Choreographer.java
private void postCallbackDelayedInternal(int callbackType,
            Object action, Object token, long delayMillis) {
        if (DEBUG_FRAMES) {
            Log.d(TAG, "PostCallback: type=" + callbackType
                    + ", action=" + action + ", token=" + token
                    + ", delayMillis=" + delayMillis);
        }

        synchronized (mLock) {
            //由于传入的delayMillis是0,所以dueTime == now
            final long now = SystemClock.uptimeMillis();
            final long dueTime = now + delayMillis;
            mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

            if (dueTime <= now) {
                scheduleFrameLocked(now);
            } else {
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
                msg.arg1 = callbackType;
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, dueTime);
            }
        }
    }

如上的代码中的 mCallbackQueues[callbackType].addCallbackLocked(dueTime,
action, token);就是根据Callback的类型,把Callback添加到对应的CallbackQueue 得链表当中。
mCallbackQueues 的初始化,可以见得,mCallbackQueues 的长度为5(这里的5就对应前面的物种类型),mCallbackQueues 数组里面填充的是CallbackQueue。

Choreographer.java

mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
        for (int i = 0; i <= CALLBACK_LAST; i++) {
            mCallbackQueues[i] = new CallbackQueue();
        }

CallbackQueue是一个内部类

Choreographer.java

private final class CallbackQueue {
        private CallbackRecord mHead;

        public boolean hasDueCallbacksLocked(long now) {
            return mHead != null && mHead.dueTime <= now;
        }

        public CallbackRecord extractDueCallbacksLocked(long now) {
            ......
        }

        @UnsupportedAppUsage
        public void addCallbackLocked(long dueTime, Object action, Object token) {
            ......
        }

        public void removeCallbacksLocked(Object action, Object token) {
            ......
        }
    }

可以看到,在CallbackQueue中存储的是CallbackRecord,看下CallbackRecord类:
CallbackRecord类中有一个成员变量是next,指向的是下一个CallbackRecord,因此在CallbackQueue中存储的是一个链式结构的CallbackRecord。注意这里的run()方法,下面会用到。

Choreographer.java

private static final class CallbackRecord {
        public CallbackRecord next;
        public long dueTime;
        public Object action; // Runnable or FrameCallback
        public Object token;

        @UnsupportedAppUsage
        public void run(long frameTimeNanos) {
            if (token == FRAME_CALLBACK_TOKEN) {
                ((FrameCallback)action).doFrame(frameTimeNanos);
            } else {
                ((Runnable)action).run();
            }
        }
    }

接着上文的postCallbackDelayedInternal继续分析:

Choreographer.java

    private void scheduleFrameLocked(long now) {
        if (!mFrameScheduled) {
            mFrameScheduled = true;
            // 是否使用USE_VSYNC,在android4.1后,这个默认为ture
            if (USE_VSYNC) {
                if (DEBUG_FRAMES) {
                    Log.d(TAG, "Scheduling next frame on vsync.");
                }

                // If running on the Looper thread, then schedule the vsync immediately,
                // otherwise post a message to schedule the vsync from the UI thread
                // as soon as possible.
                // 检查是否正在 Looper thread,然后调用 scheduleVsyncLocked
                if (isRunningOnLooperThreadLocked()) {
                    scheduleVsyncLocked();
                } else {
                    Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
                    msg.setAsynchronous(true);
                    mHandler.sendMessageAtFrontOfQueue(msg);
                }
            } else {
                final long nextFrameTime = Math.max(
                        mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
                if (DEBUG_FRAMES) {
                    Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");
                }
                Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, nextFrameTime);
            }
        }
    }

如上代码 在检查了是否在Looper thread之后调用了scheduleVsyncLocked。

Choreographer.java

    private void scheduleVsyncLocked() {
        mDisplayEventReceiver.scheduleVsync();
    }

如上,在scheduleVsyncLocked中调用了mDisplayEventReceiver.scheduleVsync();,mDisplayEventReceiver类型是FrameDisplayEventReceiver。FrameDisplayEventReceiver继承自DisplayEventReceiver,并实现了Runnable接口。实现了两个方法,onVsync()和run(),其中onVsync方法来自DisplayEventReceiver,run()方法来自Runnable接口。

   private final class FrameDisplayEventReceiver extends DisplayEventReceiver
            implements Runnable {
        private boolean mHavePendingVsync;
        private long mTimestampNanos;
        private int mFrame;

        public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
            super(looper, vsyncSource, CONFIG_CHANGED_EVENT_SUPPRESS);
        }

        // TODO(b/116025192): physicalDisplayId is ignored because SF only emits VSYNC events for
        // the internal display and DisplayEventReceiver#scheduleVsync only allows requesting VSYNC
        // for the internal display implicitly.
        //这个很重要啊,下文中会分析。
        @Override
        public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
            // Post the vsync event to the Handler.
            // The idea is to prevent incoming vsync events from completely starving
            // the message queue.  If there are no messages in the queue with timestamps
            // earlier than the frame time, then the vsync event will be processed immediately.
            // Otherwise, messages that predate the vsync event will be handled first.
            long now = System.nanoTime();
            if (timestampNanos > now) {
                Log.w(TAG, "Frame time is " + ((timestampNanos - now) * 0.000001f)
                        + " ms in the future!  Check that graphics HAL is generating vsync "
                        + "timestamps using the correct timebase.");
                timestampNanos = now;
            }

            if (mHavePendingVsync) {
                Log.w(TAG, "Already have a pending vsync event.  There should only be "
                        + "one at a time.");
            } else {
                mHavePendingVsync = true;
            }

            mTimestampNanos = timestampNanos;
            mFrame = frame;
            Message msg = Message.obtain(mHandler, this);
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
        }

        @Override
        public void run() {
            mHavePendingVsync = false;
            doFrame(mTimestampNanos, mFrame);
        }
    }

如上:mDisplayEventReceiver中没有scheduleVsync(),这是因为scheduleVsync是DisplayEventReceiver父类DisplayEventReceiver的方法。如下

DisplayEventReceiver.java

    public void scheduleVsync() {
        if (mReceiverPtr == 0) {
            Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "
                    + "receiver has already been disposed.");
        } else {
            nativeScheduleVsync(mReceiverPtr);
        }
    }

因此如果mReceiverPtr不是0,会调用nativeScheduleVsync(mReceiverPtr)。该方法也是一个native方法,这里这个类指的是底层NativeDisplayEventReceiver这个类nativeScheduleVsync底层会调用到requestNextVsync()去请求下一个Vsync,具体不跟踪了,native层代码更长,还涉及到各种描述符监听以及跨进程数据传输,当我们请求后的Vsync收到后,我们只需要知道VSYNC信号的接收回调是onVsync()方法,所以下一步的分析是回到上文中FrameDisplayEventReceiver 的onVsync()


class FrameDisplayEventReceiver extends DisplayEventReceiver

  public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
            // Post the vsync event to the Handler.
            // The idea is to prevent incoming vsync events from completely starving
            // the message queue.  If there are no messages in the queue with timestamps
            // earlier than the frame time, then the vsync event will be processed immediately.
            // Otherwise, messages that predate the vsync event will be handled first.
            long now = System.nanoTime();
            if (timestampNanos > now) {
                Log.w(TAG, "Frame time is " + ((timestampNanos - now) * 0.000001f)
                        + " ms in the future!  Check that graphics HAL is generating vsync "
                        + "timestamps using the correct timebase.");
                timestampNanos = now;
            }

            if (mHavePendingVsync) {
                Log.w(TAG, "Already have a pending vsync event.  There should only be "
                        + "one at a time.");
            } else {
                mHavePendingVsync = true;
            }

            mTimestampNanos = timestampNanos;
            mFrame = frame;
            Message msg = Message.obtain(mHandler, this);
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
        }

        @Override
        public void run() {
            mHavePendingVsync = false;
            doFrame(mTimestampNanos, mFrame); // 注意这里
        }

上面的直接省略,我们看下通过mHandler发送消息,可以看到,这里创建Message消息的时候,传入的是this,因为FrameDisplayEventReceiver实现了Runnable接口,因此在handler处理消息的时候回调run方法,在run方法中,调用了doFramen方法。另外,这里调用了msg.setAsynchronous(true),通过handler发送到MessageQueue中的是一个异步消息,我们前面已经post了一个同步屏障,因此该异步消息会被调用。因为FrameDisplayEventReceiver实现了Runnable接口,同时把自己作为参数交给了handler去处理,所以回到用到这里的 run()方法,可以看到,在 run()中调用了doFrame方法。

Choreographer.java

void doFrame(long frameTimeNanos, int frame) {
        final long startNanos;
        synchronized (mLock) {
        ......
        try {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
            AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);

            mFrameInfo.markInputHandlingStart();
            doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);

            mFrameInfo.markAnimationsStart();
            doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
            doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);

            mFrameInfo.markPerformTraversalsStart();
            doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);

            doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
        } finally {
            AnimationUtils.unlockAnimationClock();
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
        ......
    }

可以看出,在doFrame中依次执行了doCallbacks方法,传入的类型是我们上面提到的Choreographer类中定义好的五种类型。看下doCallbacks方法:

Choreographer.java

 void doCallbacks(int callbackType, long frameTimeNanos) {
            CallbackRecord callbacks;
            synchronized (mLock) {
                callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
                            now / TimeUtils.NANOS_PER_MS);
                    if (callbacks == null) {
                        return;
                    }
                ......
                for (CallbackRecord c = callbacks; c != null; c = c.next) {
                    if (DEBUG_FRAMES) {
                        Log.d(TAG, "RunCallback: type=" + callbackType
                                + ", action=" + c.action + ", token=" + c.token
                                + ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime));
                    }
                    c.run(frameTimeNanos);
                }
            } finally {
                synchronized (mLock) {
                    mCallbacksRunning = false;
                    do {
                        final CallbackRecord next = callbacks.next;
                        recycleCallbackLocked(callbacks);
                        callbacks = next;
                    } while (callbacks != null);
                }
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
    }

可以看到,首先根据指定的任务类型,从mCallbackQueues中查找需要执行的CallbackRecord,如果是null,直接return了。紧接着,遍历执行CallbackRecord中的所有任务(链表结构),回调CallbackRecord的run 方法。

CallbackRecord.java

public void run(long frameTimeNanos) {
            if (token == FRAME_CALLBACK_TOKEN) {
                ((FrameCallback)action).doFrame(frameTimeNanos);
            } else {
                ((Runnable)action).run();
            }
        }

因为我们在上面往CallbackRecord中add任务的时候,传入的token是null,所以这里直接走到了action.run()方法,而action是前面提到的TraversalRunnable,所以直接调用了TraversalRunnable类的run方法,然后调用了doTraversal(),在doTraversal()方法中,开始真正的绘制流程。看下doTraversal方法:

void doTraversal() {
        if (mTraversalScheduled) {
            //标记位设为false
            mTraversalScheduled = false;
            //从MessageQueue中移除同步屏障消息
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }
            //真正开始绘制流程
            performTraversals();

            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }

在调用performTraversals之前,调用了mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier),在这里就把之前我们添加的同步屏障从MessageQueue中移除,从而使MesssageQueue中next()方法可以正常取到同步消息。

使用一张图来梳理一下在整个Choreographer中的流程:
在这里插入图片描述
总结一下:
android一般60FPS,是VSYNC及决定的,每16ms最多一帧

VSYNC要客户端主动申请,才会有有VSYNC到来才会刷新

UI没更改,不会请求VSYNC也就不会刷新

UI局部重绘其实只会去重绘带刷新的View

11.SurfaceFlinger

SF是整个Android系统渲染的核心进程。所有应用的渲染逻辑最终都会来到SF中进行处理,最终会把处理后的图像数据交给CPU或者GPU进行绘制。
SurfaceFlinger服务是在System进程中启动的,并且负责统一管理设备的帧缓冲区。SurfaceFlinger服务在启动的过程中,会创建两个线程,其中一个线程用来监控控制台事件,而另外一个线程用来渲染系统的UI。在本文中,我们就将详细分析SurfaceFlinger服务的启动过程。
在这里插入图片描述
在这里插入图片描述

如上图所描述的DecorView surface layer 图层 图像数据
之间的关系:一个DecorVeiw对应一个surface,一个surface
又对应一个layer图层,在SurfaceFlinger会将这些图层合成成一层 然后交给CPU/GPU进行处理

在每一个应用中都以Surface作为一个图元传递单元,向SF这个服务端传递图元数据

在这里插入图片描述

SF是以生产者以及消费者为核心设计思想,把每一个应用进程作为生产者生产图元保存到SF的图元队列中,SF则作为消费者依照一定的规则把生产者存放到SF中的队列一一处理。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-01-17 11:36:04  更:2022-01-17 11:38:11 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 11:38:01-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码