前语
上班第一天摸摸鱼,由于前几天录制了一个关于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
的发动进程来看Activity
,PhoneWindow
,View
,DecoView
,ViewRootImpl
这几者之间的联系
1.1.view的制作调用示意图
1.2.View的制作时序图
整个调用进程
1.3.从Activity发动进程来看PhoneWindow,DecoView,ViewRootImpl,View的联系
- 从
Activity
的发动进程可知,Activity
中创立了一个PhoneWindow
(PhoneWindow
是Android中仅有完结笼统类Window的类,除了Activity
创立了PhoneWindow
, 还有Dialog
,和PhoneWindowManage
) 故一个Activtity
至少拥有一个Window -
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
中开端的。
咱们经过一张图来认识一下它:
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
,然后经过 WindowManager
把 DecorView
增加到窗口上,这个进程中是由 RootViewImpl
来完结丈量,制作,等操作的。
2.1 RootViewImpl 的初始化
View 的三大流程都是经过 RootViewImpl
来完结的,在 ActivityThread
中,当 Activity
目标被创立完毕后,在 onResume
后,就会经过 WindowManager
将 DecorView
增加到窗口上,在这个进程中会创立 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
办法中。在这个办法中会调用 View
的 measure()
,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布局,制作;
-
在
ActivityThread
中创立Actiivty
后,调用activity.attach()
时,创立一个窗体目标PhoneWindow
-
PhoneWindow
创立了一个WMS的署理桥接类WindowManagerImpl
目标,作为WMS在app中的代表; -
WindowManagerImpl
目标中的(mGlobal)WindowManagerGlobal
专门和WMS通讯,在mGlobal里面获取了到了WMS的Binder:getWindowSession()->WMS::openSession();
setContentView()
-
调用
PhoneWindow.setContentView(resouseID)
-
PhoneWindow
中:创立mDector
:窗体上的整个View:里面有官方的主题布局+用户自己的布局; -
PhoneWindow
中:创立mContentParent
:官方主题布局中供给给用户装载布局的容器:id为content
; -
调用
mLayoutInflater.inflater(resouseID,mContentParent)
: -
解析用户的布局xml
-
递归调用:解析根布局,经过反射创立根布局;解析子view,经过反射创立view;
-
最后PhoneWindow中的
mContentParent
加载用户的根布局; -
提交view数据
ps:这里递归调用,若嵌套层级太多,会导致栈溢出;由于递归调用不会释放栈;
ViewRootImpl 单例,办理一切View的制作策略;
注意onCreate.setContentView
后view数据已解析并实例化了;
-
在状态机为
Resume
时: -
调用
WindowManagerImpl
中的mGlobal.addView(view)
-
addView
中创立ViewRootImpl root=new ViewRootImpl()
: -
root.setView(view);
-
在setView总调用
requestLayout()
-
requestLayout()
恳求制作,编舞者出场
帧速率: CPU/GPU出图速率;
改写率: 屏幕改写速率;
-
帧速率>改写率时,呈现丢帧(出图好多张了,可是只显现了最初和结束两张,中心的丢了)
-
帧速率<改写率,呈现卡顿(屏幕改写好多次了,可是还是显现的第一帧)
Vsync: 笔直同步制作信号; 因或许硬件帧速率和改写率不共同,用来同步改写的问题;
Choreographer编舞者: 担任办理帧率节奏;
-
在内部维护了个Handler和Looper,保证制作发生在UI主线程:
Looper.myLooper==mLooper
判断是否是主线程,是的话去调同步制作信号,不是的话发送消息,走主线程去调同步制作信号 -
走native层恳求笔直同步信号,实际是找底层驱动要前次制作的时刻
-
恳求到笔直同步信号后回调
onVsync
-
走
doFrame
去逻辑管控, 判断当时时刻离前次制作的时刻大于了1帧的时刻(16.66毫秒) 就跳帧(卡顿优化有用到),若小于16.66毫秒就再次恳求笔直同步信号,避免堆叠 -
履行callback,让
ViewRootImpl
去真实制作,调用ViewRootImpl.performTraversals()
真实的制作: ViewRootImpl.performTraversals()
-
调用
relayoutWindow()
: -
创立用户java层的
surface
:只有用户供给的画面数据; -
创立native层的
surface
:包括用户供给的画面数据(java层的surface
)+体系的画面数据(状态栏,电池、wifi等等); -
创立完
surface
后:顺次调用:performMeasure(对应view的onMeasure)、performLayout(onLayout)、performDraw(onDraw);
在performDraw()
中:
-
将view的数据传至native层的
surface
-
surface
中的canvas
记录数据 -
生成
bitmap
图画数据(此时数据是在surface
中) -
将
surface
放入行列中;生产者顾客模式; -
告诉
surfaceflinfer
进程去行列中取surface
数据 -
surfaceflinfer
拿到不同的surface
,进行融合,生成bitmap
数据 -
将
bitmap
数据放入framebuffer
中,进行展现
简单版总结: Activity.setContentView(R.layout.resId)
:
解析xml并实例化;
-
调用
phoneWindow.setContentView(resId)
-
在
setContentView
中调用installDector()
:依据不同的主题,找到体系默许的xml,初始化出mDector
和mContentParent
(反射实例化出对应的ViewGroup
) -
初始化完结后,调用
mLayoutInflater.inflate(resId,mContentParent)
: -
解析
resId
的xml文件,将解析的view反射实例化;递归增加到各节点的viewgroup
中;最后将自己界说的xml根布局view增加到mContentParent;
制作发生时刻: 在AMS回调ActivityThread中的handleResumeActivity
时,也便是Resume时,而不是onCreate()
;
-
获取
PhoneWindow
-
获取
PhoneWindow
中的mDector
布局视图view -
将
mDector
布局视图view
传给ViewRootImpl
-
ViewRootImpl
中调用requestLayout()
-
requestLayout()
中顺次调用:performMeasure()、performLayout()、performDraw()
三丶WMS原理:WMS人物与实例化进程
-
window:它是一个笼统类,详细完结类为
PhoneWindow
,它对 View 进行办理。Window是View的容器,View是Window的详细表现内容; -
windowManager:是一个接口类,继承自接口
ViewManager
,从它的称号就知道它是用来办理 Window 的,它的完结类为WindowManagerImpl;
- 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显现层级
窗口相关标志
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
(当用户的脸靠近屏幕时(比方打电话),不会去呼应此事情 )
FLAG_SHOW_WHEN_LOCKED
(当用户的脸靠近屏幕时(比方打电话),不会去呼应此事情 9.FLAG_IGNORE_CHEEK_PRESSES
; 窗口能够在锁屏的 Window 之上显现, 运用Activity#setShowWhenLocked(boolean)
办法替代)
3.3. WindowManager
在了解WindowManager
办理View完结之前,先了解下WindowManager
相关类图以及Activity
界面各层级显现联系;
3.4.addView完结流程
3.5.removeView流程
3.6.重要类简介:
-
WindowManagerImpl:
WindowManager
的仅有完结类; -
WindowManagerGlobal:窗口办理操作,一切
Activity
都是经过这个进程内仅有的WindowManagerGlobal
目标和WMS通讯; - ViewRootImpl:
- View树的树根并办理View树;
- 触发View的丈量、布局以及制作;
- 输入相应的中转站;
- 担任与WMS进行进程间通讯;
3.7.TextView.setText改写流程
ViewRootImpl
会调用scheduleTraversals
预备重绘,可是,重绘一般不会立即履行,而是往Choreographer
的Choreographer.CALLBACK_TRAVERSAL
行列中增加了一个mTraversalRunnable
,同时请求VSYNC
,这个mTraversalRunnable
要一向等到请求的VSYNC
到来后才会被履行;
3.8.小结
android一般60fps,是VSYNC
信号决定的,【每16ms改写一帧(连续调用setText
只会触发一次重绘)】,VSYNC
信号要客户端自动请求,才会有VSYNC
改写,UI没更改,不会恳求VSYNC
信号进行改写;
四丶WMS原理:WMS作业原理
4.1.WMS的责任
4.2.WMS中重要的一些特点释义
从WMS类中包括的特点也能够看出WMS的首要责任,窗口办理、窗口动画以及监听输入进行事情派发。
WMS中addWindow源码分析
在分析addWindow之前,先了解几个类;
WindowToken:WindowToken
具有令牌的作用,是对运用组件的行为进行标准办理的一个手段。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.事情派发
事情输入监听以及分发
EventHub:
1.运用inotify
监听输入设备的增加和移除;
2.运用epoll
机制监听输入设备的数据改变;
3.读取设备文件数据;
4.将原始数据返回给InputReader;
InputReader:不断读取由EventHub
监听到的input
事情,将多个事情组合成一个可供上层消费的事情(比方将一组接触事情合并成一个action_down
事情),然后交给InputDispatcher
进行事情分发;
InputDispatcher:拿到InputReader
获取的事情后,对事情进行包装,寻找并分发到目标窗口,对应inputChannel
输入;
Android体系是由事情驱动的,而input
是常见的事情之一,点击、滑动、长按等操作,都归于input
事情,中心类便是InputReader
和InputDispatcher;
解释:
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根底篇大合集,根基安定楼房平地起