Android 图形架构 之五—— 深入分析addView所发生的的一切
Posted 薛瑄
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 图形架构 之五—— 深入分析addView所发生的的一切相关的知识,希望对你有一定的参考价值。
前言
前几篇文章,分析了在SurfaceFlinger 进程,WMS进程 中,图形架构的流程和关键类的介绍。现在我们来分析一下,app进程中是如何与这些进程进行交互,以及何时交互。
Android 图形架构 之一 ——概述
Android 图形架构 之二—— SurfaceFlinger 启动和连接
Android 图形架构 之三—— 创建Layer、Surface、SurfaceControl
Android 图形架构 之四——图形缓冲区的申请和消费流程及核心类
Android 图形架构 之五——深入分析addView所发生的的一切
Android 图形架构 之六——深入分析draw()是如何工作的
Android 图形架构 之七——Choreographer 源码分析
android图形架构 之八——硬件VSync、VSync-app、Vsync-sf
UI显示过程的三个进程
Android显示的整个过程由App进程、System_server进程、SurfaceFlinger进程一起配合完成。
-
App进程: App需要将自己的内容显示在屏幕上,所以需要负责发起Surface创建的请求。同时触发对控件的测量、布局、绘制以及输入事件的派发处理,这些主要在ViewRootImpl中触发;
-
System_server进程: 主要是WindowManagerService,负责接收App请求,同时和SurfaceFlinger建立连接,向SurfaceFlinger发起具体请求创建Surface,并且创建Surace的辅助管理类SurfaceControl(和window一一对应)(AMS作用是统一调度所有App的Activity);
-
SurfaceFlinger: 为App创建具体的Surface,在SurfaceFLinger对应成Layer,然后负责管理、合成所有图层,最终显示。
前几篇文章分析了第二点,第三点,这篇文章主要分析第一点
Activity、Window、PhoneWindow、DecorView、View的对应关系
-
一个Activity对应创建一个Surface,每个Surface 对应SurfaceFlinger中的一个Layer
-
Window:每一个Activity都包含一个Window对象(抽象类,提供了绘制窗口的一组通用API),通常由PhoneWindow实现。是Activity和整个View系统交互的接口。
-
PhoneWindow:继承于Window,是Window类的具体实现。该类内部包含了一个DecorView对象,该DecorView对象是所有应用窗口(Activity界面)的根View。
简而言之,PhoneWindow类是把一个FrameLayout类,即DecorView对象进行一定的包装,将他作为应用窗口的根View,并提供一组通用的窗口操作接口。 -
DecorView:PhoneWindow构造函数中定义,继承FrameLayout类,是所有应用窗口的根View。
-
WindowManager 继承ViewManager ,它们都是操作UI的接口
-
WindowManagerImpl 实现了WindowManager,并持有WindowManagerGlobal 的对象
-
WindowManagerGlobal 是单例模式,每个进程只有一个。持有ViewRootImpl 的对象
-
ViewRootImpl 在整个Android的GUI系统中占据非常重要的位置,如果把Activity和View 看作 ‘MVC’ 中的V, 把各种后台服务看作Modal,ViewRootImpl 则是’MVC’ 中的’C’ - Controller. Controller在MVC架构里承担着承上启下的作用,一般来说它的逻辑最为复杂。从下图可以看到,ViewRootImpl 与 用户输入系统(接收用户按键,触摸屏输入), 窗口系统(复杂窗口的布局,刷新,动画),显示合成系统(包括定时器Choreograph, SurfaceFlinger), 乃至Audio系统(音效输出)等均有密切的关联。
流程分析
我们从 activity的源码 handleResumeActivity 开始分析,熟悉activity启动流程的应该知道,handleResumeActivity 中会执行onResume,添加显示界面。下面这张时序图,可以说概括了,本系列文章的主干流程,可以细细品味 (图片来源)
开始源码分析
frameworks/base/core/java/android/app/ActivityThread.java
代码一:
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason)
ActivityClientRecord r = mActivities.get(token);
....
// TODO Push resumeArgs into the activity for consideration
r = performResumeActivity(token, clearHide, reason);
if (r != null)
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);
//获取WindowManagerImpl,因为它implements WindowManager,后者implements ViewManager ,此处是重点,接下来会分析
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
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();
if (a.mVisibleFromClient)
if (!a.mWindowAdded)
a.mWindowAdded = true;
//重要,添加根布局
wm.addView(decor, l);
else
....
....
if (r.activity.mVisibleFromClient)
//显示根布局
r.activity.makeVisible();
....
接着分析上面ViewManager wm = a.getWindowManager();
代码路径:frameworks/base/core/java/android/app/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)
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
//activity 关联的Window 是PhoneWindow
mWindow = new PhoneWindow(this, window, activityConfigCallback);
。。。省略代码,一些设置回调和赋值的语句,。。。
//该函数为mWindowManager 变量赋值,获取到
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null)
mWindow.setContainer(mParent.getWindow());
mWindowManager = mWindow.getWindowManager();
mCurrentConfig = config;
mWindow.setColorMode(info.colorMode);
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE)
获取WINDOW_SERVICE服务,它是在
代码路径:frameworks/base/core/java/android/view/Window.java
代码三:
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated)
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated
|| SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
if (wm == null)
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
//创建本地的 WindowManagerImpl,它关联了当前的Window(PhoneWindow),之后所有的操作 都是交给WindowManagerImpl ,这些操作定义在接口ViewManager
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
到此,我们回到代码一,看看如何添加根布局的 wm.addView(decor, l);
代码路径:frameworks/base/core/java/android/view/WindowManagerImpl.java
代码四:
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params)
applyDefaultToken(params);
//这里的mGlobal 全局变量,一个进程中只有一个实例
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
代码路径:frameworks/base/core/java/android/view/WindowManagerGlobal.java
代码五:
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow)
....
//因为本类是单例,所以这里创建的ViewRootImpl ,一个进程中也只有一个
//代码六 分析
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try
// 通过ViewRootImpl 把View 添加到Surface
// 代码九 分析
root.setView(view, wparams, panelParentView);
catch (RuntimeException e)
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0)
removeViewLocked(index, true);
throw e;
接着 看看ViewRootImpl 的创建,因为这里涉及到Session,它负责创建Surface、SurfaceSession(java端的SurfaceComposerClient)
代码路径:frameworks/base/core/java/android/view/ViewRootImpl.java
代码六:
public ViewRootImpl(Context context, Display display)
mContext = context;
// 还是由WindowManagerGlobal ,来创建Session
//ViewRootImpl 持有Session 对象后,后续就可以控制UI的绘制了(measure,layout,draw等),涉及到Surface 等操作,就交给mWindowSession 来操作了
mWindowSession = WindowManagerGlobal.getWindowSession();
....
代码七:
public static IWindowSession getWindowSession()
synchronized (WindowManagerGlobal.class)
if (sWindowSession == null)
try
InputMethodManager imm = InputMethodManager.getInstance();
//获取WindowManagerService ,IWindowManager 是aidl
IWindowManager windowManager = getWindowManagerService();
//创建Session
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub()
@Override
public void onAnimatorScaleChanged(float scale)
ValueAnimator.setDurationScale(scale);
,
imm.getClient(), imm.getInputContext());
catch (RemoteException e)
throw e.rethrowFromSystemServer();
return sWindowSession;
public static IWindowManager getWindowManagerService()
synchronized (WindowManagerGlobal.class)
if (sWindowManagerService == null)
// 获取WMS服务,并转换为IWindowManager.Stub 类型
sWindowManagerService = IWindowManager.Stub.asInterface(
ServiceManager.getService("window"));
....
return sWindowManagerService;
这里的openSession 是跨进程通信。Session、WMS 是在另外的一个进程
代码八:
public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
IInputContext inputContext)
....
//创建Session,所以每一个View 对应一个
Session session = new Session(this, callback, client, inputContext);
return session;
上面创建了Session,这里我们回到代码五 root.setView(view, wparams, panelParentView);
这里的root ,是ViewRootImpl对象,调用了setView函数
代码九:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView)
synchronized (this)
....
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
//这里最终就是调用了三大UI 函数 onMesure, onLayout,onDraw
requestLayout();
try
....
//调用Session 为添加UI 做准备,
//这里主要是创建WindowState(每个View 都对应一个)、创建SurfaceSession,下面会分析
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
....
这里先分析mWindowSession.addToDisplay
,再分析requestLayout()
,因为它主要执行的就是把View输出到屏幕,在下一次屏幕刷新时,也会有相应的逻辑,放在后面分析,流程看上去更符合逻辑。
这里为什么requestLayout()
的执行要放在mWindowSession.addToDisplay
之前,我没太明白,知道的大佬,评论里指点一下
代码路径:frameworks/base/services/core/java/com/android/server/wm/Session.java
代码十:
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
Rect outOutsets, InputChannel outInputChannel)
//调用WMS 的addWindow
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outStableInsets, outOutsets, outInputChannel);
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
代码十一:
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
InputChannel outInputChannel)
synchronized(mWindowMap)
。。。。。
//创建WindowState 对象,每一个View 对应一个
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], seq, attrs, viewVisibility, session.mUid,
session.mCanAddInternalSystemWindow);
。。。。。
//下面看看这个函数
win.attach();
。。。。
。。。。
return res;
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowState.java
代码十二:
void attach()
if (localLOGV) Slog.v(TAG, "Attaching " + this + " token=" + mToken);
//调用到Session ,创建SurfaceSession
mSession.windowAddedLocked(mAttrs.packageName);
代码路径:frameworks/base/services/core/java/com/android/server/wm/Session.java
代码十三:
void windowAddedLocked(String packageName)
mPackageName = packageName;
mRelayoutTag = "relayoutWindow: " + mPackageName;
if (mSurfaceSession == null)
//创建mSurfaceSession
mSurfaceSession = new SurfaceSession();
//把当前的Session对象,与WMS 关联起来
mService.mSessions.add(this);
....
mNumWindow++;
看到这个SurfaceSession 的创建,我们应该很熟悉了,里面的流程 在Android 图形架构 之三—— 创建Layer、Surface、SurfaceControl 文章中分析过了
到此,我们已经有了可以控制Surface 的SurfaceSession (也就是SurfaceComposerClient),也有了View,下面就该创建Surface,并开始绘制了。
我们现在回到代码九,看看requestLayout()
,它
代码十四:
@Override
public void requestLayout()
if (!mHandlingLayoutInLayoutRequest)
//检查是否在主线程,
checkThread();
mLayoutRequested = true;
//执行UI遍历,它就是向Choreographer 发送CALLBACK_TRAVERSAL事件消息
scheduleTraversals();
void scheduleTraversals()
if (!mTraversalScheduled)
mTraversalScheduled = true;
//发送同步消息屏障,会优先处理异步消息
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//该函数发送了事件后,就等待mChoreographer ,回调mTraversalRunnable,最终执行doTraversal
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch)
scheduleConsumeBatchedInput();
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
关于同步消息屏障的,可阅读深入源码分析Handler 消息机制 、Looper、MessageQueue 消息同步屏障、IdleHandler、Message 复用
在Android 图形架构 之六——Choreographer 源码分析文章中,我们可以知道,屏幕收到刷新的信号,会调用到Choreographer,由该类去处理分发事件,屏幕绘制会调用到 ViewRootImpl#doTraversal()
下面我们就来分析ViewRootImpl#doTraversal函数:
代码十五:
void doTraversal()
if (mTraversalScheduled)
mTraversalScheduled = false;
//取消同步消息屏障
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
.....
performTraversals();
.....
代码十六:
private void performTraversals()
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
//下面这三大函数,大家应该很熟悉了,就不分析了
.....
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
.....
performLayout(lp, mWidth, mHeight);
.....
performDraw();
代码十七:
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending) throws RemoteException
.....
//调用Session 的relayout,
//最后一个参数是mSurface,该成员变量的初始值 final Surface mSurface = new Surface();
//但是此时的mSurface 还没有与底层的surface 关联起来,后面会通过copyFrom 进行关联
int relayoutResult = mWindowSession.relayout(
mWindow, mSeq, params,
(int) (mView.getMeasuredWidth() * appScale + 0.5f),
(int) (mView.getMeasuredHeight() * appScale + 0.5f),
viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,以上是关于Android 图形架构 之五—— 深入分析addView所发生的的一切的主要内容,如果未能解决你的问题,请参考以下文章
Android 图形架构 之七——Choreographer 源码分析
Android 图形架构 之七——Choreographer 源码分析
Android 图形架构 之二—— SurfaceFlinger 启动和连接
Android 图形架构 之二—— SurfaceFlinger 启动和连接