本文查看的源码是Android 7.1
所有方法中的代码只保留了本文关注的代码。
抽空研究了一下Activity的显示过程,主要是熟悉Android中的Activiy从设置布局到绘制显示的整个流程。
按照老惯例先上时序图
涉及的文件路径:
frameworks/base/core/java/android/app/Activity.java
frameworks/base/core/java/android/view/ViewRootImpl.java
frameworks/base/core/java/android/app/ActivityThread.java
frameworks/base/core/java/android/view/WindowManagerGlobal.java
frameworks/base/services/core/java/com/android/server/wm/Session.java
frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
一、建立ViewTree数据结构
我们查看的入口函数是Activity 的oncreate方法中使用的setContentView,我们开发过程中必须要设置布局文件或者布局对象,因此以此为入口比较合适。
frameworks/base/core/java/android/app/Activity.java
public void setContentView(@LayoutRes int layoutResID) {
//拿到Window对象,调用Window的setContentView方法
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
setContentView 调用的是PhoneWindow的setContentView,使用PhoneWindow 首先要先拿到PhoneWindow ,而PhoneWindow是在Activity进程创建之后由ActivityThread调用attach创建的。
Activity的创建大体分为如下步骤,这里细节不在赘述。
1.创建Activity对象。
2.创建COntext对象。
3.准备APPlication对象。
4.attach上下文。
5.Activity onCreate。
final void attach(Context context, ......) {
//Window 创建PhoneWindow对象
//attach 方法是在Activity启动的时候创建的
//Activity 创建的步骤
//1.创建Activity对象
//2.创建COntext对象
//3.准备APPlication对象
//4.attach上下文
//5.Activity onCreate
mWindow = new PhoneWindow(this, window);
}
继续查看PhoneWindow 中的setContentView 方法。
这个方法中:
调用installDecor 创建了DecorView对象。
调用mLayoutInflater.inflate(layoutResID, mContentParent); 方法将生成的ViewTree添加到mContentParent对象中。
frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
//创建DecorView
installDecor();
}
// 加载布局用的
//layoutResID 加载布局的id 会生成ViewTree
// mContentParent 是ViewTree的Patent 把生成的layoutResID 添加到 mContentParent 中
mLayoutInflater.inflate(layoutResID, mContentParent);
}
我们继续查看installDecor 方法。
这个方法中除了创建DecorView 还设置了Activity的主题以及mContentParent对象。
到这里只是建立了ViewTree数据结构,并没有执行绘制动作。
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
//创建 mDecor 对象 是一个FrameLayout 是手机页面的整个RootView
//generateDecor 设置页面的主题
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
//获取 mContentParent 对象
mContentParent = generateLayout(mDecor);
}
}
二、真正的绘制流程
我们都知道真正的绘制流程是在ActivityThread的handleResumeActivity方法发起的,也就是在调用Activity的onResume之后。
我们查看ActivityThread的handleResumeActivity方法,在handleResumeActivity 方法中大体做了如下绘制相关的事情:
调用Activity的onResume方法。
获取ViewManager 将 DecorView添加到ViewManager中。
使Activity变成可见。
frameworks/base/core/java/android/app/ActivityThread.java
final void handleResumeActivity(IBinder token,.........) {
ActivityClientRecord r = mActivities.get(token);
//触发了 Activity的onResume 方法
//触发之后才会处理UI的显示问题
r = performResumeActivity(token, clearHide, reason);
final Activity a = r.activity;
r.window = r.activity.getWindow();
//先拿到Activity 的DecorView
View decor = r.window.getDecorView();
ViewManager wm = a.getWindowManager();
a.mDecor = decor;
if (a.mVisibleFromClient && !a.mWindowAdded) {
//将Activity 的DecorView 添加到ViewManager里面
wm.addView(decor, l);
}
if (r.activity.mVisibleFromClient) {
//使界面变成可见界面
r.activity.makeVisible();
}
}
ViewManager是一个接口类,具体的实现在WindowManagerImpl 中,我们查看到WindowManagerImpl的addView方法又调用了WindowManagerGlobal的addView方法。
继续往下查找。
frameworks/base/core/java/android/view/WindowManagerImpl.java
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
android.util.SeempLog.record_vg_layout(383,params);
applyDefaultToken(params);
//调用到 WindowManagerGlobal 中的 addView 方法
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
在WindowManagerGlobal中我们看到创建了ViewRootImpl对象,并且将DecorView交付给ViewRootImpl进行管理。
frameworks/base/core/java/android/view/WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
ViewRootImpl root;
synchronized (mLock) {
//初始化 ViewRootImpl
root = new ViewRootImpl(view.getContext(), display);
}
try {
//将DecorView 交给 ViewRootImpl 进行管理
root.setView(view, wparams, panelParentView);
}
}
继续查看ViewRootImpl的setView方法,发现了请求绘制的方法 requestLayout();。
在请求绘制之后又调用了 mWindowSession.addToDisplay 让WMS进行显示。在这里我们分开查看 requestLayout 和 mWindowSession.addToDisplay 分别做了那些事情?
frameworks/base/core/java/android/view/ViewRootImpl.java
public void setView(View view,.......) {
synchronized (this) {
//一个ViewRootImp 只能管理一个ViewTree
if (mView == null) {
mView = view;
//请求绘制
requestLayout();
// Binder 调用 调用到 IWindowSession.aidl
// Session.java 实现了 IWindowSession.Stub 也就是
// Session 实现了 addToDisplay 方法 跨进程到了 WindowManagerService中
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
}
}
2.1首先查看requestLayout
requestLayout方法调用了scheduleTraversals 方法,继续往下看。
frameworks/base/core/java/android/view/ViewRootImpl.java
@Override
public void requestLayout() {
scheduleTraversals();
}
查看到scheduleTraversals方法在Choreographer 里面 post 一个mTraversalRunnable对象而mTraversalRunnable对象会在下一次Vsyn 信号来的时候执行mTraversalRunnable 方法。
Vsyn什么时候来?怎么来?我们在这篇文档里面不去深究。
我们只需要关心mTraversalRunnable 干了什么事情。继续查看mTraversalRunnable。
void scheduleTraversals() {
if (!mTraversalScheduled) {
// 往Choreographer 里面 post 一个mTraversalRunnable
// mTraversalRunnable 会在下一次Vsyn 信号来的时候执行绘制操作
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
执行了 doTraversal();方法,继续往下查看。
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
调用到了 performTraversals()方法 performTraversals()方法才是真正绘制的地方。
继续查看 performTraversals()方法。
void doTraversal() {
if (mTraversalScheduled) {
// 真正执行绘制的地方
performTraversals();
}
}
performTraversals()方法中我们只关心几个重要方法和重要事情。
向WMS申请Surface,如果Surface申请不成功,就不能进行绘制操作。
1.测量ViewTree中的所有控件。
2.摆放控件。
3.画出控件。
关于 performMeasure,performLayout ,performDraw();这三个方法我们在这里不再赘述。
private void performTraversals() {
// 向WMS申请Surface
//非常关键
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
//测量
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
//摆放
performLayout(lp, mWidth, mHeight);
//绘制
performDraw();
}
我们查看relayoutWindow方法中调用的 WindowSession.relayout 调用的是WMS中的relayout
方法,而这个方法主要做的事情是将申请的Surface可用化,如果未申请之前Surface是个空壳,是不可用的状态,无法进行显示。
申请完成Surface之后,就可以调用performDraw将结果绘制到Buffer中,然后提交给SurfaceFiling进行合成图片,再由SurfaceFiling提交到屏幕显示缓冲区,等待VSYN信号显示到屏幕上。
private int relayoutWindow(WindowManager.LayoutParams params, .......) {
//申请Surface
// 申请Surface 通过Binder调用之后 Surface才能使用,
// 未调用之前Surface是个空壳
// 1.有了surface之后就有了Buffer
// 2.在Buffer中绘制完成之后开始提交给SurfaceFling
// 3.SurfaceFling合成图片 提交到屏幕的缓冲区
// 4.经过VSYN周期刷新到屏幕上面显示
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,
mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingConfiguration,
mSurface);
}
2.2查看mWindowSession.addToDisplay
首先我们先看看 mWindowSession 是在什么地方获取的?
mWindowSession是在WindowManagerGlobal类getWindowSession方法获取的。
getWindowSession方法调用了windowManager.openSession是属于Binder调用。
继续查看windowManager.openSession方法。
frameworks/base/core/java/android/view/WindowManagerGlobal.java
public static IWindowSession getWindowSession() {
if (sWindowSession == null) {
InputMethodManager imm = InputMethodManager.getInstance();
IWindowManager windowManager = getWindowManagerService();
sWindowSession = windowManager.openSession(new IWindowSessionCallback.Stub() {},....);
}
return sWindowSession;
}
openSession方法在WindowManagerService中,查看到此方法创建了Session 而 Session 继承于IWindowSession,IWindowSession是个aidl文件用来跨进程通讯的,有此知道可以将WindowManagerGlobal 和 WindowManagerService通过 Binder跨进程通讯。
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
@Override
public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
IInputContext inputContext) {
if (client == null) throw new IllegalArgumentException("null client");
if (inputContext == null) throw new IllegalArgumentException("null inputContext");
Session session = new Session(this, callback, client, inputContext);
return session;
}
frameworks/base/services/core/java/com/android/server/wm/Session.java
final class Session extends IWindowSession.Stub
implements IBinder.DeathRecipient {
......
}
因为Session继承了 IWindowSession.Stub 因此调用 mWindowSession.addToDisplay 其实调用的是 Session.java 的 addToDisplay 方法。
其中 mService.addWindow调用WindowManagerService的 addwindows 方法
frameworks/base/services/core/java/com/android/server/wm/Session.java
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
Rect outOutsets, InputChannel outInputChannel) {
//调用WMS的 addwindows 方法
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outStableInsets, outOutsets, outInputChannel);
}
我们查看到WindowManagerService 中的addWindow 方法有两个参数值得注意 ,这两个参数可以让WMS和WIndow相互之间调用。
IWindow client binder参数对象 WMS与Windows进行通讯
Session session binder参数对象 Window与WMS进行通讯
WindowManagerService的addWindow方法中我们大体能看到WindowManagerService的方法主要包括下面四个方面:
1.分配Serface。
2.掌管Surface显示顺序以及位置尺寸等。
3.控制窗口动画。
4.输入事件分发。
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
//IWindow client binder对象 WMS与Windows进行通讯
//Session session binder对象 Window与WMS进行通讯
public int addWindow(Session session, IWindow client, int seq,.......) {
| 1.分配Serface
WMS的主要作用 --->| 2.掌管Surface显示顺序以及位置尺寸等
| 3.控制窗口动画
| 4.输入事件分发
}
最后我们总结一下:
Activity创建的时候会创建一个PhoneWindow,PhoneWindow会有一个DecorView,DecorView就是这个Activity的整个View的ViewTree。ContentView只是DecorView的一部分,DecorView会对应一个ViewRootImpl对象,ViewRootImpl可以和WindowManagerService双向通信,ViewRootImpl通过IWindowSession向WindowManagerService发起Binder调用,WindowManagerService可以通过IWindow向ViewRootImpl发起Binder调用。Activity之所以能显示是因为DecorView创建了ViewRootImpl对象,并且ViewRootImpl全权负责DecorView的绘制,ViewRootImpl会在WindowManagerService注册一个窗口,然后WindowManagerService会统一管理子View的窗口大小,位置,还有层级。在第一次绘制的时候ViewRootImpl会向WindowManagerService申请一个Surface,有了Surface之后应用端就可以进行绘制了,绘制完了之后SurfaceFling就会按照WindowManagerService窗口大小,位置,还有层级进行合成,合成之后就会写到屏幕的显示缓冲区显示出来。