本文只关注界面是如何显示出来的 , 对于activity启动流程就不多讨论了 ,
涉及到的关键类 :
- ActivityThread
- Activity / PhoneWindow / DecorView
- WindowManager / WindowManagerImpl / WindowManagerGlobal
- ViewRootImpl
- WindowManagerService
- Choreographer
先从 ActivityThread #handleResumeActivity 说起 , 此时activity 已经创建出来并调用了onCreate onStart ... 等一系列生命周期方法了
ActivityThread #handleResumeActivity
@Override
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
String reason) {
//这里会调用onResume
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
final Activity a = r.activity;
//willBeVisible =true 表示activity 将要变为Visible
boolean willBeVisible = !a.mStartedActivity;
if (!willBeVisible) {
try {
willBeVisible = ActivityManager.getService().willActivityBeVisible(
a.getActivityToken());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
if (r.window == null && !a.mFinished && willBeVisible) {
//r.activity.getWindow(); = PhoneWindow
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
//将decor 设置为不可见
decor.setVisibility(View.INVISIBLE);
//wm = WindowManagerImpl
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
//设置窗口类型为TYPE_BASE_APPLICATION
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
// 关键代码
a.mWindowAdded = true;
//调用WindowManagerImpl.addView(decor, l);
wm.addView(decor, l);
} else {
a.onWindowAttributesChanged(l);
}
}
}
}
a.getWindowManager(); 返回的是WindowManagerImpl , parentWindow =phoneWindow
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mContext, parentWindow);
}
关键代码 wm.addView(decor, l) , 将decor添加到WindowManager中
WindowManagerImpl#addView , 又会交给WindowManagerGlobal 调用
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
WindowManagerGlobal 是个单例 , 里面有三个比较重要的集合
private final ArrayList<View> mViews = new ArrayList<View>();
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
private final ArrayList<WindowManager.LayoutParams> mParams =
new ArrayList<WindowManager.LayoutParams>();
mViews 用来保存调用addView添加的view
mRoots 用来保存ViewRootImpl
mParams 用来保存view 的布局参数
接着看 addview方法
WindowManagerGlobal #addview
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
//view = decorView
//parentWindow 为PhoneWindow
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (parentWindow != null) {
parentWindow.adjustLayoutParamsForSubWindow(wparams);
}
ViewRootImpl root;
synchronized (mLock) {
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
//将view ViewRootImpl params 保存起来
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
try {
//调用ViewRootImpl.setView
// do this last because it fires off messages to start doing things
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
看官方代码注释最后做这个是因为它发送消息开始做事情
setview里面到底发送了什么消息
继续跟进root.setView
ViewRootImpl#setView
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
if (view instanceof RootViewSurfaceTaker) {
//decorView 实现了RootViewSurfaceTaker , 这里会进来
mSurfaceHolderCallback =
((RootViewSurfaceTaker)view).willYouTakeTheSurface();
if (mSurfaceHolderCallback != null) {
mSurfaceHolder = new TakenSurfaceHolder();
mSurfaceHolder.setFormat(PixelFormat.UNKNOWN);
mSurfaceHolder.addCallback(mSurfaceHolderCallback);
}
}
CompatibilityInfo compatibilityInfo =
mDisplay.getDisplayAdjustments().getCompatibilityInfo();
mTranslator = compatibilityInfo.getTranslator();
// If the application owns the surface, don't enable hardware acceleration
if (mSurfaceHolder == null) {
// While this is supposed to enable only, it can effectively disable
// the acceleration too.
enableHardwareAcceleration(attrs);
final boolean useMTRenderer = MT_RENDERER_AVAILABLE
&& mAttachInfo.mThreadedRenderer != null;
if (mUseMTRenderer != useMTRenderer) {
// Shouldn't be resizing, as it's done only in window setup,
// but end just in case.
endDragResizing();
mUseMTRenderer = useMTRenderer;
}
}
int res; /* = WindowManagerImpl.ADD_OKAY; */
// 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.
requestLayout();
try {
//wms 添加 mWindow , mWindow和activity的PhoneWindow 不是一个概念
//mWindow 中有两个属性 1. 弱引用持有ViewRootImpl 和 2. WindowSession
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
}
//wms添加window失败 抛异常
if (res < WindowManagerGlobal.ADD_OKAY) {
switch (res) {
case WindowManagerGlobal.ADD_BAD_APP_TOKEN:
case WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN:
throw new WindowManager.BadTokenException(
"Unable to add window -- token " + attrs.token
+ " is not valid; is your activity running?");
case WindowManagerGlobal.ADD_NOT_APP_TOKEN:
throw new WindowManager.BadTokenException(
"Unable to add window -- token " + attrs.token
+ " is not for an application");
case WindowManagerGlobal.ADD_APP_EXITING:
throw new WindowManager.BadTokenException(
"Unable to add window -- app for token " + attrs.token
+ " is exiting");
case WindowManagerGlobal.ADD_DUPLICATE_ADD:
throw new WindowManager.BadTokenException(
"Unable to add window -- window " + mWindow
+ " has already been added");
case WindowManagerGlobal.ADD_STARTING_NOT_NEEDED:
// Silently ignore -- we would have just removed it
// right away, anyway.
return;
case WindowManagerGlobal.ADD_MULTIPLE_SINGLETON:
throw new WindowManager.BadTokenException("Unable to add window "
+ mWindow + " -- another window of type "
+ mWindowAttributes.type + " already exists");
case WindowManagerGlobal.ADD_PERMISSION_DENIED:
throw new WindowManager.BadTokenException("Unable to add window "
+ mWindow + " -- permission denied for window type "
+ mWindowAttributes.type);
case WindowManagerGlobal.ADD_INVALID_DISPLAY:
throw new WindowManager.InvalidDisplayException("Unable to add window "
+ mWindow + " -- the specified display can not be found");
case WindowManagerGlobal.ADD_INVALID_TYPE:
throw new WindowManager.InvalidDisplayException("Unable to add window "
+ mWindow + " -- the specified window type "
+ mWindowAttributes.type + " is not valid");
}
throw new RuntimeException(
"Unable to add window -- unknown error code " + res);
}
}
}
}
WMS 添加mWindow之前会调用 requestLayout
官方注释: 在添加到窗口之前调用 requestLayout , 确保我们在从系统接收任何其他事件之前进行重新布局。
按理说wms 添加了window 界面才会显示出来 , 所以应该是wms添加窗口成功之后再调用requestLayout , 但是为什么这里先调用了requestLayout
进入requestLayout 看看
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
//不在主线程调用会报错
checkThread();
scheduleTraversals();
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
上面代码给Choreographer post 一个 callback , 对于屏幕刷新为60hz(即一秒60帧)来说 , Choreographer 会每隔16.6ms 回调一次给 TraversalRunnable ,
TraversalRunnable
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
doTraversal
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
performTraversals();
}
}
performTraversals 方法会调用 measure layout draw , 完成view的元素显示
performTraversals 代码太多 , 这里贴点关键的代码
private void performTraversals() {
// cache mView since it is used so much below...
final View host = mView;
if (host == null || !mAdded)
// 当wms添加mWindow失败直接return
return;
if (!mStopped || mReportNextDraw) {
boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
(relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
|| mHeight != host.getMeasuredHeight() || contentInsetsChanged ||
updatedConfiguration) {
// Ask host how big it wants to be
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
if (didLayout) {
performLayout(lp, mWidth, mHeight);
}
if (!cancelDraw && !newSurface) {
performDraw();
}
}
在WMS添加mWindow 成功情况下 , 当 performDraw 调用完成 , 界面就显示出来了
总结 :
activity 执行完 onResume 后 , ViewRootImpl 会向Choreographer 发送一个 CallBack ,
Choreographer 在下一帧时会回调给ViewRootImpl , ViewRootImpl 调用
performTraversals方法完成 measure layout draw , 等到 WMS 将 window 添加成功 , 界面就显示出来了