前语

上班第一天摸摸鱼,由于前几天录制了一个关于Android WMS中心原了解析的视频,刚好整理了一些关于这个视频的文档。

重视大众号:Android苦做舟
解锁 《Android十大板块文档》,让学习更靠近未来实战。已构成PDF版

内容如下

1.2022最新Android11位大厂面试专题,128道附答案
2.音视频大合集,从初中高到面试包罗万象
3.Android车载运用大合集,从零开端一同学
4.功能优化大合集,离别优化烦恼
5.Framework大合集,从里到外分析的明明白白
6.Flutter大合集,进阶Flutter高级工程师
7.compose大合集,拥抱新技术
8.Jetpack大合集,全家桶一次吃个够
9.架构大合集,轻松应对作业需求
10.Android根底篇大合集,根基安定楼房平地起

开端进入正题,ღ( ・ᴗ・` )

一丶WMS根底:WMS与activity发动流程

首要经过Activity的发动进程来看ActivityPhoneWindow,View,DecoView,ViewRootImpl这几者之间的联系

1.1.view的制作调用示意图

Android WMS基础与原理解析

1.2.View的制作时序图

整个调用进程

Android WMS基础与原理解析

1.3.从Activity发动进程来看PhoneWindow,DecoView,ViewRootImpl,View的联系

Android WMS基础与原理解析

  1. Activity的发动进程可知,Activity 中创立了一个PhoneWindow(PhoneWindow是Android中仅有完结笼统类Window的类,除了Activity创立了PhoneWindow, 还有Dialog,和PhoneWindowManage) 故一个Activtity至少拥有一个Window
  2. setWindowManager设置的windowManager是哪个类呢?
//Activity.java中的的attch办法中
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
//注册的当地frameworks/base/core/java/android/app/SystemServiceRegistry.java
        registerService(Context.WINDOW_SERVICE, WindowManager.class,
                new CachedServiceFetcher<WindowManager>() {
            @Override
            public WindowManager createService(ContextImpl ctx) {
                return new WindowManagerImpl(ctx);
            }});
//看下WindowManagerImpl类
//SystemSerever.java
            wm = WindowManagerService.main(context, inputManager,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                    !mFirstBoot, mOnlyCore, new PhoneWindowManager());
            ServiceManager.addService(Context.WINDOW_SERVICE, wm);
//-------------------------
//IWindowManager.aidl
//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public class WindowManagerService extends IWindowManager.Stub

可知WindowManagerService.java是完结部分

二丶WMS根底:WMS制作原理

了解 ViewRootImpl

简介

ViewRootImpl 是 View 的最高层级,是一切 View 的根。ViewRootImpl 完结了 View 和 WindowManager 之间所需求的协议。ViewRootImpl 的创立进程是从 WindowManagerImpl 中开端的。View 的丈量,布局,制作以及上屏,都是从 ViewRootImpl 中开端的。

咱们经过一张图来认识一下它:

Android WMS基础与原理解析
Window

咱们知道界面中一切的元素都是由 View 构成的,View 是依附于 Window 上面的。Window 只是一个笼统概念,把界面笼统成一个 窗口,也能够笼统成一个 View。

ViewManange

一个接口,内部界说了三个办法,用来对 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

也是一个接口,继承自 ViewManager,在运用程序中,经过 WindowManager 来办理 Window,将 View 附加到 Window 上。他有一个完结类 WindowManagerImpl

WindowManagerImpl

WindowManager 的完结类,WindowManagerImpl 中的内部办法完结都是经过署理类 WindowManagerGlobal 来完结。

WindowManagerGlobal

WindowManagerGlobal 是一个单例,也便是说一个进程中只有一个 WindowManagerGlobal目标,他服务与一切页面的 View。

ViewParent

一个接口,界说了将成为 View 父级类的责任。

ViewRootImpl

视图层次结构的顶部。一个 Window 对应着一个 ViewRootImpl 和 一个 VIew。这个 View 便是被 ViewRootImpl 操作的。

一个小栗子,咱们都只 Actiivty 中 会创立一个 Window 目标。 setContentView 办法中的 View 终究也会被增加到 Window 目标中的 DecorView 中,也便是说一个 Window 中对应着一个 View。这个 View 是被 RootViewImpl 操作的。

​WindowManager 便是进口。经过 WindowManager的 addView 增加一个 Window(也能够了解为 View),然后就会创立一个 ViewRootImpl,来对 view 进行操作,最后将 View 渲染到屏幕的窗口上。

例如 Activity 中,在 onresume 履行完结后,就会获取 Window 中的 DecorView,然后经过 WindowManagerDecorView 增加到窗口上,这个进程中是由 RootViewImpl 来完结丈量,制作,等操作的。

2.1 RootViewImpl 的初始化

View 的三大流程都是经过 RootViewImpl 来完结的,在 ActivityThread 中,当 Activity 目标被创立完毕后,在 onResume 后,就会经过 WindowManagerDecorView 增加到窗口上,在这个进程中会创立 ViewRootImpl

ActivityThread.handleResumeActivity

public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
        String reason) {
    // .....
    final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
    final Activity a = r.activity;
    if (r.window == null && !a.mFinished && willBeVisible) {
        r.window = r.activity.getWindow();
        View decor = r.window.getDecorView();
        decor.setVisibility(View.INVISIBLE);
        ViewManager wm = a.getWindowManager();
        WindowManager.LayoutParams l = r.window.getAttributes();
        a.mDecor = decor;
        //设置窗口类型为运用类型
        l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
        if (a.mVisibleFromClient) {
            if (!a.mWindowAdded) {
                a.mWindowAdded = true;
                //增加 decor 到 window 中
                wm.addView(decor, l);
            } else {
                a.onWindowAttributesChanged(l);
            }
        }
    } 
    //....
}

2.2 WindowManagerImpl.addView增加View

public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}

2.3 WindowManagerGlobal.addView

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;
  	//.....
    ViewRootImpl root;
    View panelParentView = null;
    synchronized (mLock) {
		//创立 ViewRootImpl
        root = new ViewRootImpl(view.getContext(), display);
        view.setLayoutParams(wparams);
        //将 Window 所对应的 View,ViewRootImp,params 次序增加到列表中,这一步是为了方便更新和删去 View
        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);
        } catch (RuntimeException e) {
            // BadTokenException or InvalidDisplayException, clean up.
            if (index >= 0) {
                removeViewLocked(index, true);
            }
            throw e;
        }
    }
}

2.4 ViewRootImpl 对 View 进行操作

ViewRootImpl.setView

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
        if (mView == null) {
            mView = view;
            attrs = mWindowAttributes;
      		//恳求布局,履行 View 的制作办法
            requestLayout();
            if ((mWindowAttributes.inputFeatures
                    & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                mInputChannel = new InputChannel();
            }
            mForceDecorViewVisibility = (mWindowAttributes.privateFlags
                    & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
            try {
                mOrigWindowType = mWindowAttributes.type;
                mAttachInfo.mRecomputeGlobalAttributes = true;
                collectViewAttributes();
                //将 Window 增加到屏幕上,mWindowSession 完结了 IWindowSession接口,是 Session 的 Binder 目标。
                // addToDisplay 是一次 AIDL 的进程,告诉 WindowManagerServer 增加 Window
                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                        getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                        mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
                        mTempInsets);
                setFrame(mTmpFrame);
            }
			//设置当时 View 的 Parent
            view.assignParent(this);
        }
    }
}

2.5 ViewRootImpl.requestLayout

//恳求布局
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.");
    }
}
final class TraversalRunnable implements Runnable {
    @Override
    public void run() {
        doTraversal();
    }
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        mChoreographer.postCallback(
            Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        if (!mUnbufferedInputDispatch) {
            scheduleConsumeBatchedInput();
        }
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}
void doTraversal() {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
        if (mProfile) {
            Debug.startMethodTracing("ViewAncestor");
        }
        performTraversals();
        if (mProfile) {
            Debug.stopMethodTracing();
            mProfile = false;
        }
    }
}

在上面代码中,调用 requestLayout 恳求布局,终究会履行到 performTraversals 办法中。在这个办法中会调用 Viewmeasure()layout()draw() 办法。

2.6 ViewRootImpl. performTraversals

View 的丈量 ViewRootImpl 调用 performMeasure 履行 Window 对应的 View 丈量。

private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
    if (mView == null) {
        return;
    }
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
    try {
        mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
}

performMeasure 办法中就会履行 View 的 measure 办法,在其中会核算一下束缚信息,然后就会调用 onMeasure 办法.

2.7 View 的布局

ViewRootImpl 调用 performLayout 履行 Window 对应 View 的布局

private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
        int desiredWindowHeight) {
    mScrollMayChange = true;
    mInLayout = true;
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
    try {
        // 调用 layout 办法进行布局
        host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
        mInLayout = false;
        int numViewsRequestingLayout = mLayoutRequesters.size();
        if (numViewsRequestingLayout > 0) {
            ArrayList<View> validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters,
                    false);
            if (validLayoutRequesters != null) {
                mHandlingLayoutInLayoutRequest = true;
                // Process fresh layout requests, then measure and layout
                int numValidRequests = validLayoutRequesters.size();
                for (int i = 0; i < numValidRequests; ++i) {
                    final View view = validLayoutRequesters.get(i);
                    Log.w("View", "requestLayout() improperly called by " + view +
                            " during layout: running second layout pass");
                    // 恳求对该 View 进行布局,该操作会导致此 View 被从头丈量,布局,制作
                    view.requestLayout();
                }
                measureHierarchy(host, lp, mView.getContext().getResources(),
                        desiredWindowWidth, desiredWindowHeight);
                mInLayout = true;
                host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
                mHandlingLayoutInLayoutRequest = false;
                // Check the valid requests again, this time without checking/clearing the
                // layout flags, since requests happening during the second pass get noop'd
                validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters, true);
                if (validLayoutRequesters != null) {
                    final ArrayList<View> finalRequesters = validLayoutRequesters;
                    // Post second-pass requests to the next frame
                    getRunQueue().post(new Runnable() {
                        @Override
                        public void run() {
                            int numValidRequests = finalRequesters.size();
                            for (int i = 0; i < numValidRequests; ++i) {
                                final View view = finalRequesters.get(i);
                                // 恳求对该 View 进行布局,该操作会导致此 View 被从头丈量,布局,制作
                                view.requestLayout();
                            }
                        }
                    });
                }
            }
        }
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
    mInLayout = false;
}

经过上面代码能够看出,在 layout 布局期间,有或许会对 View 进行 requestLayout 从头进行丈量,布局,制作。

2.8 View 的制作

ViewRootImpl 经过调用 performDraw 履行 Window 对应 View 的制作。

private void performDraw() {
    if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
        return;
    } else if (mView == null) {
        return;
    }
    final boolean fullRedrawNeeded =
            mFullRedrawNeeded || mReportNextDraw || mNextDrawUseBlastSync;
    mFullRedrawNeeded = false;
    mIsDrawing = true;
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
    boolean usingAsyncReport = addFrameCompleteCallbackIfNeeded();
    addFrameCallbackIfNeeded();
    try {
        //制作
        boolean canUseAsync = draw(fullRedrawNeeded);
        if (usingAsyncReport && !canUseAsync) {
            mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(null);
            usingAsyncReport = false;
        }
    } finally {
        mIsDrawing = false;
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
	//...
}

2.9.Draw办法

private boolean draw(boolean fullRedrawNeeded) {
    //........
	//告诉 View 上注册的制作事情
    mAttachInfo.mTreeObserver.dispatchOnDraw();
    boolean useAsyncReport = false;
    if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty || mNextDrawUseBlastSync) {
        if (isHardwareEnabled()) {
          	//调用 Window 对应 View 的 invalidate 办法
            mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
        } else {
          	// 制作 View
            if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
                    scalingRequired, dirty, surfaceInsets)) {
                return false;
            }
        }
    }
    if (animating) {
        mFullRedrawNeeded = true;
        scheduleTraversals();
    }
    return useAsyncReport;
}
void invalidate() {
    mDirty.set(0, 0, mWidth, mHeight);
    if (!mWillDrawSoon) {
        scheduleTraversals();
    }
}

2.10.drawSoftware办法

private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
        boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
    // Draw with software renderer.
    final Canvas canvas;
    try {
        canvas = mSurface.lockCanvas(dirty);
        // TODO: Do this in native
        canvas.setDensity(mDensity);
    } 
    try {
        if (DEBUG_ORIENTATION || DEBUG_DRAW) {
            Log.v(mTag, "Surface " + surface + " drawing to bitmap w="
                    + canvas.getWidth() + ", h=" + canvas.getHeight());
            //canvas.drawARGB(255, 255, 0, 0);
        }
		// View 制作
        mView.draw(canvas);
        drawAccessibilityFocusedDrawableIfNeeded(canvas);
    } finally {
    }
    return true;
}

2.11.WMS制作流程

主角:ViewRootImpl、Choreographer、Surfaceflinfer

WMS扮演了什么人物?

作为和谐者,和谐view布局,制作;

  1. ActivityThread中创立Actiivty后,调用activity.attach()时,创立一个窗体目标PhoneWindow

  2. PhoneWindow创立了一个WMS的署理桥接类WindowManagerImpl目标,作为WMS在app中的代表;

  3. WindowManagerImpl目标中的(mGlobal)WindowManagerGlobal专门和WMS通讯,在mGlobal里面获取了到了WMS的Binder:getWindowSession()->WMS::openSession();

setContentView()

  1. 调用PhoneWindow.setContentView(resouseID)

  2. PhoneWindow中:创立mDector:窗体上的整个View:里面有官方的主题布局+用户自己的布局;

  3. PhoneWindow中:创立mContentParent:官方主题布局中供给给用户装载布局的容器:id为content

  4. 调用mLayoutInflater.inflater(resouseID,mContentParent):

  5. 解析用户的布局xml

  6. 递归调用:解析根布局,经过反射创立根布局;解析子view,经过反射创立view;

  7. 最后PhoneWindow中的mContentParent加载用户的根布局;

  8. 提交view数据

ps:这里递归调用,若嵌套层级太多,会导致栈溢出;由于递归调用不会释放栈;

ViewRootImpl 单例,办理一切View的制作策略;

注意onCreate.setContentView后view数据已解析并实例化了;

  1. 在状态机为Resume时:

  2. 调用WindowManagerImpl中的mGlobal.addView(view)

  3. addView中创立ViewRootImpl root=new ViewRootImpl()

  4. root.setView(view);

  5. 在setView总调用requestLayout()

  6. requestLayout()恳求制作,编舞者出场

帧速率: CPU/GPU出图速率;

改写率: 屏幕改写速率;

  1. 帧速率>改写率时,呈现丢帧(出图好多张了,可是只显现了最初和结束两张,中心的丢了)

  2. 帧速率<改写率,呈现卡顿(屏幕改写好多次了,可是还是显现的第一帧)

Vsync: 笔直同步制作信号; 因或许硬件帧速率和改写率不共同,用来同步改写的问题;

Choreographer编舞者: 担任办理帧率节奏;

  1. 在内部维护了个Handler和Looper,保证制作发生在UI主线程:Looper.myLooper==mLooper判断是否是主线程,是的话去调同步制作信号,不是的话发送消息,走主线程去调同步制作信号

  2. 走native层恳求笔直同步信号,实际是找底层驱动要前次制作的时刻

  3. 恳求到笔直同步信号后回调onVsync

  4. doFrame去逻辑管控, 判断当时时刻离前次制作的时刻大于了1帧的时刻(16.66毫秒) 就跳帧(卡顿优化有用到),若小于16.66毫秒就再次恳求笔直同步信号,避免堆叠

  5. 履行callback,让ViewRootImpl去真实制作,调用ViewRootImpl.performTraversals()

Android WMS基础与原理解析

真实的制作: ViewRootImpl.performTraversals()

  1. 调用relayoutWindow()

  2. 创立用户java层的surface:只有用户供给的画面数据;

  3. 创立native层的surface:包括用户供给的画面数据(java层的surface)+体系的画面数据(状态栏,电池、wifi等等);

  4. 创立完surface后:顺次调用:performMeasure(对应view的onMeasure)、performLayout(onLayout)、performDraw(onDraw);

performDraw()中:

  1. 将view的数据传至native层的surface

  2. surface中的canvas记录数据

  3. 生成bitmap图画数据(此时数据是在surface中)

  4. surface放入行列中;生产者顾客模式;

  5. 告诉surfaceflinfer进程去行列中取surface数据

  6. surfaceflinfer拿到不同的surface,进行融合,生成bitmap数据

  7. bitmap数据放入framebuffer中,进行展现

Android WMS基础与原理解析

简单版总结: Activity.setContentView(R.layout.resId):

解析xml并实例化;

  1. 调用phoneWindow.setContentView(resId)

  2. setContentView中调用installDector():依据不同的主题,找到体系默许的xml,初始化出mDectormContentParent(反射实例化出对应的ViewGroup

  3. 初始化完结后,调用mLayoutInflater.inflate(resId,mContentParent):

  4. 解析resId的xml文件,将解析的view反射实例化;递归增加到各节点的viewgroup中;最后将自己界说的xml根布局view增加到mContentParent;

制作发生时刻: 在AMS回调ActivityThread中的handleResumeActivity时,也便是Resume时,而不是onCreate()

  1. 获取PhoneWindow

  2. 获取PhoneWindow中的mDector布局视图view

  3. mDector布局视图view传给ViewRootImpl

  4. ViewRootImpl中调用requestLayout()

  5. requestLayout()中顺次调用:performMeasure()、performLayout()、performDraw()

三丶WMS原理:WMS人物与实例化进程

  1. window:它是一个笼统类,详细完结类为 PhoneWindow ,它对 View 进行办理。Window是View的容器,View是Window的详细表现内容;
  2. windowManager:是一个接口类,继承自接口 ViewManager ,从它的称号就知道它是用来办理 Window 的,它的完结类为 WindowManagerImpl;
  3. WMS:是窗口的办理者,它担任窗口的发动、增加和删去。别的窗口的大小和层级也是由它进行办理的;

三者之间的联系:

Android WMS基础与原理解析

3.1 Window

Window分类

1.**Application Window(运用窗口):**例如Activity,Dialog;【取值规模为1~99】

2.**Sub Window(子窗口):不能独立存在,**需求依附在其他窗口,例如PopupWindow【取值规模为1000~1999】;

3.**System Window(体系窗口):**例如Toast、输入法窗口、体系音量条窗口、体系过错窗口等【2000~2999】;

3.2.Window显现层级

Android WMS基础与原理解析

窗口相关标志

1.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON(当Window可见时允许锁屏)

2.FLAG_NOT_FOCUSABLE(Window 不能获得输入焦点,即不接受任何按键或按钮事情,例如该 Window 上 有 EditView,点击 EditView 是 不会弹出软键盘的,Window 规模外的事情依旧为原窗口处理;例如点击该窗口外的view,仍然会有呼应。别的只要设置了此Flag,都将会启用FLAG_NOT_TOUCH_MODAL

3.FLAG_NOT_TOUCH_MODAL(设置了该 Flag,将 Window 之外的按键事情发送给后边的 Window 处理, 而自己只会处理 Window 区域内的接触事情;Window 之外的 view 也是能够呼应 touch 事情。

4.FLAG_NOT_TOUCHABLE(设置了该Flag,表明该 Window 将不会接受任何 touch 事情,例如点击该 Window 不会有呼应,只会传给下面有聚焦的窗口)

5.FLAG_KEEP_SCREEN_ON(只要 Window 可见时屏幕就会一向亮着,视频播放、游戏)

6.FLAG_LAYOUT_NO_LIMITS(允许 Window 超越屏幕之外)

7.FLAG_IGNORE_CHEEK_PRESSES(当用户的脸靠近屏幕时(比方打电话),不会去呼应此事情 )

  1. FLAG_SHOW_WHEN_LOCKED(当用户的脸靠近屏幕时(比方打电话),不会去呼应此事情 9.FLAG_IGNORE_CHEEK_PRESSES ; 窗口能够在锁屏的 Window 之上显现, 运用Activity#setShowWhenLocked(boolean) 办法替代)

3.3. WindowManager

在了解WindowManager办理View完结之前,先了解下WindowManager相关类图以及Activity界面各层级显现联系;

Android WMS基础与原理解析

Android WMS基础与原理解析

3.4.addView完结流程

Android WMS基础与原理解析

3.5.removeView流程

Android WMS基础与原理解析

3.6.重要类简介:

  1. WindowManagerImplWindowManager的仅有完结类;
  2. WindowManagerGlobal:窗口办理操作,一切Activity都是经过这个进程内仅有的WindowManagerGlobal目标和WMS通讯;
  3. ViewRootImpl:
  • View树的树根并办理View树;
  • 触发View的丈量、布局以及制作;
  • 输入相应的中转站;
  • 担任与WMS进行进程间通讯;

3.7.TextView.setText改写流程

ViewRootImpl会调用scheduleTraversals预备重绘,可是,重绘一般不会立即履行,而是往ChoreographerChoreographer.CALLBACK_TRAVERSAL行列中增加了一个mTraversalRunnable,同时请求VSYNC,这个mTraversalRunnable要一向等到请求的VSYNC到来后才会被履行;

3.8.小结

android一般60fps,是VSYNC信号决定的,【每16ms改写一帧(连续调用setText只会触发一次重绘)】,VSYNC信号要客户端自动请求,才会有VSYNC改写,UI没更改,不会恳求VSYNC信号进行改写;

四丶WMS原理:WMS作业原理

4.1.WMS的责任

Android WMS基础与原理解析

4.2.WMS中重要的一些特点释义

Android WMS基础与原理解析

从WMS类中包括的特点也能够看出WMS的首要责任,窗口办理、窗口动画以及监听输入进行事情派发。

WMS中addWindow源码分析

在分析addWindow之前,先了解几个类;

WindowTokenWindowToken具有令牌的作用,是对运用组件的行为进行标准办理的一个手段。WindowToken由运用组件或其办理者担任向WMS声明并持有。运用组件在需求新的窗口时,有必要供给WindowToken以表明自己的身份,而且窗口的类型有必要与所持有的WindowToken的类型共同,同时它将归于同一个运用组件的窗口组织在了一同;

DisplayContent:假如说WindowToken依照窗口之间的逻辑联系将其分组,那么DisplayContent则依据窗口的显现方位将其分组。隶归于同一个DisplayContent的窗口将会被显现在同一个屏幕中。每一个DisplayContent都对应这一个仅有的ID,在增加窗口时能够经过指定这个ID决定其将被显现在那个屏幕中。DisplayContent是一个非常具有阻隔性的一个概念。处于不同DisplayContent的两个窗口在布局、显现次序以及动画处理上不会产生任何耦合。因而,就这几个方面来说,DisplayContent就像一个孤岛,一切这些操作都能够在其内部独立履行。因而,这些本来归于整个WMS全局性的操作,变成了DisplayContent内部的操作了。

**WindowState:**表明一个窗口的一切特点,所以它是WMS中事实上的窗口;当向WMS增加一个窗口时,WMS会为其创立一个WindowState。别的WMS.addWindow()函数中看到新的WindowState被保存到mWindowMap中,键值为IWindow的Bp端。mWindowMap是整个体系一切窗口的一个全集。

WMS.addWindow()流程浅析

.......
//窗口检查,对要增加的窗口进行检查,假如窗口不满足条件,则直接返回
 int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName,appOp);
 if (res != WindowManagerGlobal.ADD_OKAY) {
      return res;
}
.......
//WindowToken相关处理,有的窗口类型需求供给WindowToken
WindowToken token = displayContent.getWindowToken(hasParent ? parentWindow.mAttrs.token : attrs.token);
// If this is a child window, we want to apply the same type checking rules as the
// parent window type.
final int rootType = hasParent ? parentWindow.mAttrs.type : type;
boolean addToastWindowRequiresToken = false;
if (token == null) {
     if (!unprivilegedAppCanCreateTokenWith(parentWindow, callingUid, type,
                        rootType, attrs.token, attrs.packageName)) {
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
        }
      final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
      token = new WindowToken(this, binder, type, false, displayContent, session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
       } else if (rootType >= FIRST_APPLICATION_WINDOW
          && rootType <= LAST_APPLICATION_WINDOW) {}
......
//创立windowState
final WindowState win = new WindowState(this, session, client, token, parentWindow,appOp[0], seq, attrs, viewVisibility, session.mUid, userId,session.mCanAddInternalSystemWindow);
......
//调用WMP的办法,此办法会依据窗口的Type对LayoutParams的一些成员进行修正
displayPolicy.adjustWindowParamsLw(win, win.mAttrs, callingPid, callingUid);
.......
// 将窗口增加到体系中
res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid);
.......
// windowState保存到Map中
mWindowMap.put(client.asBinder(), win);
.......
// 绑定Token和WindowState联系
win.mToken.addWindow(win);

4.3. WMS.removeWindow()流程

 void removeWindow(Session session, IWindow client) {
         synchronized (mGlobalLock) {
            	//获取WindowState
             WindowState win = windowForClientLocked(session, client, false);
             if (win != null) {
                 //履行删去
                 win.removeIfPossible();
                 return;
             }
             // Remove embedded window map if the token belongs to an embedded window
             mEmbeddedWindowController.remove(client);
         }
     }

win.removeIfPossible办法和它的名字一样, 并不是直接履行删去操作,而是进行多个条件判断过滤,满足其中一个条件就会return,推延删去操作。比方View正在运转一个动画,这是就会推延删去操作直到动画完结。然后调用removeImmediately办法。

4.4.事情派发

事情输入监听以及分发

Android WMS基础与原理解析
EventHub:
1.运用inotify监听输入设备的增加和移除;
2.运用epoll机制监听输入设备的数据改变;
3.读取设备文件数据;
4.将原始数据返回给InputReader;

InputReader:不断读取由EventHub监听到的input事情,将多个事情组合成一个可供上层消费的事情(比方将一组接触事情合并成一个action_down事情),然后交给InputDispatcher进行事情分发;

InputDispatcher:拿到InputReader获取的事情后,对事情进行包装,寻找并分发到目标窗口,对应inputChannel输入;

Android体系是由事情驱动的,而input是常见的事情之一,点击、滑动、长按等操作,都归于input事情,中心类便是InputReaderInputDispatcher;

Android WMS基础与原理解析

解释: ViewRootImpl#WindowInputEventReceiver:从下面源码能够看出,此类用于InputChannel输入事情接收以及处理输入事情分发;

final class WindowInputEventReceiver extends InputEventReceiver {
  		//结构办法,接受inputchannel输入事情
         public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
             super(inputChannel, looper);
         }
         @Override
         public void onInputEvent(InputEvent event) {
          	...
             if (processedEvents != null) {
                 if (processedEvents.isEmpty()) {
                     // InputEvent consumed by mInputCompatProcessor
                     finishInputEvent(event, true);
                 } else {
                     for (int i = 0; i < processedEvents.size(); i++) {
                     	//对输入事情进行分发
                         enqueueInputEvent(
                                 processedEvents.get(i), this,
                                 QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY, true);
                     }
                 }
             } else {
                 enqueueInputEvent(event, this, 0, true);
             }
         }

ViewRootImpl#InputStage:笼统类,首要用来将事情的处理分成若干个阶段(stage)进行,假如该事情没有被处理,则该stage就会调用onProcess办法处理,然后调用forward履行下一个stage的处理;假如该事情被标识为处理则直接调用forward,履行下一个stage的处理,直到没有下一个stage;

ViewPostImeInputStage:InputStage的子类,将输入事情传递到上层视图;至此,输入事情一层层向上传递,终究交由详细的view进行处理;

重视大众号:Android苦做舟
解锁 《Android十大板块文档》,让学习更靠近未来实战。已构成PDF版

内容如下

1.2022最新Android11位大厂面试专题,128道附答案
2.音视频大合集,从初中高到面试包罗万象
3.Android车载运用大合集,从零开端一同学
4.功能优化大合集,离别优化烦恼
5.Framework大合集,从里到外分析的明明白白
6.Flutter大合集,进阶Flutter高级工程师
7.compose大合集,拥抱新技术
8.Jetpack大合集,全家桶一次吃个够
9.架构大合集,轻松应对作业需求
10.Android根底篇大合集,根基安定楼房平地起