很久之前的一邊文章介紹了 Android系統(tǒng)的啟動路程 以及APP的啟動流程 ,這篇文章將介紹APP啟動后到顯示在界面這一過程中 又做了些是什么,在上一篇文章中介紹到APP啟動之后首先會執(zhí)行onCreate()方法,在Oncreate()當(dāng)中去調(diào)用setContentView() 將我們的布局傳入到參數(shù)中,下面我們就從setContentView 方法開始分析.
1. setContentView()
調(diào)用這個方法是,super到了Activity.java中, 可看到直接調(diào)用了PhoneWindow 的setContentView方法
public void setContentView(View view) {
getWindow().setContentView(view);
initWindowDecorActionBar();
}
在PhoneWindow 中的setContentView中 首先創(chuàng)建根布局 DecorView 再將我們的布局文件引用inflate到 decorView上
@Override
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
1.創(chuàng)建DecorView
首先會執(zhí)行installDecor()方法里的generateDecir去創(chuàng)建DecorView ,可以看到是直接new 一個DecorView 對象,并將當(dāng)前Window 通過構(gòu)造函數(shù)傳入
protected Deco rView generateDecor(int featureId) {
......................
return new DecorView(context, featureId, this, getAttributes());
}
2. 設(shè)置Them 以及創(chuàng)建ContentView
接下來 又調(diào)用了generateLayout()方法去設(shè)置主題以及 布局的rootView ,在下面可以看到 首先會將根據(jù)主題配置 獲取到對應(yīng)的layoutResource 資源引用,調(diào)用mDecor.onResourcesLoaded(mLayoutInflater, layoutResource); 將布局文件綁定到 DecorView 之上,再通過ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);獲取到內(nèi)容布局返回到上一級,到這里 我們就創(chuàng)建好了頁面需要的根布局,下一步就是講我們自己的布局綁定到 系統(tǒng)根布局上.
protected ViewGroup generateLayout(DecorView decor) {
.............
// Inflate the window decor.
int layoutResource;
..........
mDecor.startChanging();
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
.............
return contentParent;
}
3. inflate 自己的布局
在調(diào)用setContentView時傳入了自己的布局文件,下一步就需要將 這個布局文件進(jìn)行綁定,執(zhí)行mLayoutInflater的
inflate 方法,去解析布局文件 得到不想對應(yīng)的屬性信息,追蹤帶LayoutInflate.java中 可以看到面對我們傳入的參數(shù)進(jìn)行了 xml 解析,獲取到屬性以及控件信息,通過Factory 的 onCreateVeiw 方法去創(chuàng)建這個對象.這里我們不在帶大家了解Inflate的源碼,后續(xù)文章再做詳解.
@Override
public void setContentView(int layoutResID) {
................
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
............
}
4. 總結(jié)
到這里我們就分析完了Activity 的生命周期,可以知道 自己的布局并沒有在OnCreate 方法中繪制到頁面上,只是 對頁面的布局文件進(jìn)行了解析.所以接著往下看ActivityThread的源碼,也就是追蹤下一個生命周期.
2. handleResumeActivity
接著上篇文章講解, ActivityThread 在調(diào)用onCreate 回調(diào)之后會執(zhí)行 handleResumeActivity 通過源碼分析可以知道 在這里,執(zhí)行了 另外的周期方法 onStart() onResume(),并且windowManager 會將根布局DecorView 添加到Phonewindow 上,
@Override
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
String reason) {
......................
// 周期回調(diào)
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
final Activity a = r.activity;
.......................
if (r.window == null && !a.mFinished && willBeVisible) {
//獲取window decorview
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;
l.softInputMode |= forwardBit;
if (r.mPreserveWindow) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
//將根布局添加到WindowManagerImpl上
wm.addView(decor, l);
} else {
a.onWindowAttributesChanged(l);
}
}
// If the window has already been added, but during resume
// we started another activity, then don't yet make the
// window visible.
} else if (!willBeVisible) {
if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");
r.hideForNow = true;
}
...................
}
1. WindowManagerImpl
上面可以看到 我們的decorView 添加到了WindowManagerImpl中 ,接著會在WindowManagerGlobal中調(diào)用AddView()方法,這里記錄了傳入的view 和ViewRootImpl 所以在這里就記錄了整個APP的根布局, 再之后會 走到和ViewRootImpl里的setView 方法
private void addView(...){
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 {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
2.ViewRootImpl
這個方法里就看到了我們熟悉的requestLayout();方法,所以頁面繪制是從這里開始的 是在onresume()之后進(jìn)行繪制的
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
................
//全局賦值mView
mView = view;
...
//頁面繪制的起點
requestLayout();
...
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
//通過WindowSession將window添加到屏幕
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
}
......................
}
requestLayout()中先做了線程判斷,子線程不能更新UI的錯誤就是在這里報出的,接著調(diào)用了scheduleTraversals()方法 ,將回執(zhí)信號發(fā)送到了 Choreographer的postCallback()中 并且傳入了一個runnable 對象,這個runnable 會在之后的操作流程中,執(zhí)行里面的run方法
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
//線程檢測
checkThread();
mLayoutRequested = true;
//繪制操作
scheduleTraversals();
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
3.Choreographer
postCallback()-->postCallbackDelayed()-->postCallbackDelayedInternal()-->scheduleFrameLocked()
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
if (DEBUG_FRAMES) {
Log.d(TAG, "PostCallback: type=" + callbackType
+ ", action=" + action + ", token=" + token
+ ", delayMillis=" + delayMillis);
}
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
//delayMillis 傳入的值為0 所以條件判斷為true
if (dueTime <= now) {
scheduleFrameLocked(now);
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
這里USE_VSYNC 為true 在主線程時,調(diào)用到了scheduleVsyncLocked(),
private void scheduleFrameLocked(long now) {
if (!mFrameScheduled) {
mFrameScheduled = true;
if (USE_VSYNC) {
if (DEBUG_FRAMES) {
Log.d(TAG, "Scheduling next frame on vsync.");
}
// If running on the Looper thread, then schedule the vsync immediately,
// otherwise post a message to schedule the vsync from the UI thread
// as soon as possible.
if (isRunningOnLooperThreadLocked()) {
scheduleVsyncLocked();
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
msg.setAsynchronous(true);
mHandler.sendMessageAtFrontOfQueue(msg);
}
} else {
final long nextFrameTime = Math.max(
mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
if (DEBUG_FRAMES) {
Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");
}
Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, nextFrameTime);
}
}
}
DisplayEventReceiverd的實現(xiàn)類FrameDisplayEventReceiver 對象去調(diào)用native代碼,去做16ms 間隔刷新處理, 16毫秒之后又會回調(diào)會java 層面,做信號分發(fā), 調(diào)用到dispatchVsync()里的onVsync()方法
private void scheduleVsyncLocked() {
mDisplayEventReceiver.scheduleVsync();
}
public void scheduleVsync() {
if (mReceiverPtr == 0) {
Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "
+ "receiver has already been disposed.");
} else {
//發(fā)送vsync 信號
nativeScheduleVsync(mReceiverPtr);
}
}
// Called from native code.
@SuppressWarnings("unused")
private void dispatchVsync(long timestampNanos, int builtInDisplayId, int frame) {
//16毫秒之后回調(diào)回java層
onVsync(timestampNanos, builtInDisplayId, frame);
}
在實現(xiàn)類FrameDisplayEventReceiver中做信號分發(fā),這里使用了handler ,在handler 中 調(diào)用doFrame(System.nanoTime(), 0); 然后是doCallbacks()這里面直接將ViewRootImpl 中傳入的Runnable 獲取到去調(diào)用run方法 ,這里需要注意下 信號同步是用handler 來通知的如果主線程的任務(wù)繁重 這個信號就會延遲,會使頁面得不到及時重繪,出現(xiàn)頁面卡頓問題,所以這里就解釋了頁面卡頓的原因 ,當(dāng)然引起拉頓的原因不僅僅只有這個.
@Override
public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
........................
mTimestampNanos = timestampNanos;
mFrame = frame;
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
```\
void doFrame(long frameTimeNanos, int frame) {
...........................
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
mFrameInfo.markInputHandlingStart();
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
mFrameInfo.markAnimationsStart();
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
mFrameInfo.markPerformTraversalsStart();
//這個方法響應(yīng) 信號同步
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
} finally {
AnimationUtils.unlockAnimationClock();
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
if (DEBUG_FRAMES) {
final long endNanos = System.nanoTime();
Log.d(TAG, "Frame " + frame + ": Finished, took "
+ (endNanos - startNanos) * 0.000001f + " ms, latency "
+ (startNanos - frameTimeNanos) * 0.000001f + " ms.");
}
}
void doCallbacks(int callbackType, long frameTimeNanos) {
CallbackRecord callbacks;
synchronized (mLock) {
final long now = System.nanoTime();
callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
now / TimeUtils.NANOS_PER_MS);
if (callbacks == null) {
return;
}
mCallbacksRunning = true;
....................
}
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);
for (CallbackRecord c = callbacks; c != null; c = c.next) {
if (DEBUG_FRAMES) {
Log.d(TAG, "RunCallback: type=" + callbackType
+ ", action=" + c.action + ", token=" + c.token
+ ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime));
}
//回調(diào)run方法
c.run(frameTimeNanos);
}
} finally {
synchronized (mLock) {
mCallbacksRunning = false;
do {
final CallbackRecord next = callbacks.next;
recycleCallbackLocked(callbacks);
callbacks = next;
} while (callbacks != null);
}
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
### 4. ViewRootImpl
上面會返回ViewRootImpl 中開始做頁面的繪制,也就是View的繪制分發(fā).
doTraversal()--> performTraversals() 這個方法里就是我們熟悉的 繪制的三大方法 以及關(guān)聯(lián)suface 的部分
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
//下面這三個方法 會使用我們開始傳入的mView 也就是DecorView 來調(diào)用內(nèi)部的測量 排列 繪制方法,之后及時View繪制流程了 這里不再贅述這一部分
public void performTraversals(){
......................
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
.......................
performLayout(lp, mWidth, mHeight);
.......................
performDraw();
}
performMeasure()--->mView.measure()
performLayout()--> host.layout()
performDraw()-->draw()-->drawSoftware()