很久之前的一边文章介绍了 Android系统的启动路程 以及APP的启动流程 ,这篇文章将介绍APP启动后到显示在界面这一过程中 又做了些是什么,在上一篇文章中介绍到APP启动之后首先会执行onCreate()方法,在Oncreate()当中去调用setContentView() 将我们的布局传入到参数中,下面我们就从setContentView 方法开始分析.
1. setContentView()
调用这个方法是,super到了Activity.java中, 可看到直接调用了PhoneWindow 的setContentView方法
public void setContentView(View view) {
getWindow().setContentView(view);
initWindowDecorActionBar();
}
在PhoneWindow 中的setContentView中 首先创建根布局 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.创建DecorView
首先会执行installDecor()方法里的generateDecir去创建DecorView ,可以看到是直接new 一个DecorView 对象,并将当前Window 通过构造函数传入
protected Deco rView generateDecor(int featureId) {
......................
return new DecorView(context, featureId, this, getAttributes());
}
2. 设置Them 以及创建ContentView
接下来 又调用了generateLayout()方法去设置主题以及 布局的rootView ,在下面可以看到 首先会将根据主题配置 获取到对应的layoutResource 资源引用,调用mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
将布局文件绑定到 DecorView 之上,再通过ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
获取到内容布局返回到上一级,到这里 我们就创建好了页面需要的根布局,下一步就是讲我们自己的布局绑定到 系统根布局上.
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 自己的布局
在调用setContentView时传入了自己的布局文件,下一步就需要将 这个布局文件进行绑定,执行mLayoutInflater
的
inflate
方法,去解析布局文件 得到不想对应的属性信息,追踪带LayoutInflate.java中 可以看到面对我们传入的参数进行了 xml 解析,获取到属性以及控件信息,通过Factory 的 onCreateVeiw 方法去创建这个对象.这里我们不在带大家了解Inflate的源码,后续文章再做详解.
@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. 总结
到这里我们就分析完了Activity 的生命周期,可以知道 自己的布局并没有在OnCreate 方法中绘制到页面上,只是 对页面的布局文件进行了解析.所以接着往下看ActivityThread的源码,也就是追踪下一个生命周期.
2. handleResumeActivity
接着上篇文章讲解, ActivityThread 在调用onCreate 回调之后会执行 handleResumeActivity
通过源码分析可以知道 在这里,执行了 另外的周期方法 onStart() onResume(),并且windowManager 会将根布局DecorView 添加到Phonewindow 上,
@Override
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) {
//获取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
中调用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()之后进行绘制的
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的错误就是在这里报出的,接着调用了scheduleTraversals()
方法 ,将回执信号发送到了 Choreographer的postCallback()中 并且传入了一个runnable 对象,这个runnable 会在之后的操作流程中,执行里面的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 在主线程时,调用到了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的实现类FrameDisplayEventReceiver 对象去调用native代码,去做16ms 间隔刷新处理, 16毫秒之后又会回调会java 层面,做信号分发, 调用到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 {
//发送vsync 信号
nativeScheduleVsync(mReceiverPtr);
}
}
// Called from native code.
@SuppressWarnings("unused")
private void dispatchVsync(long timestampNanos, int builtInDisplayId, int frame) {
//16毫秒之后回调回java层
onVsync(timestampNanos, builtInDisplayId, frame);
}
在实现类FrameDisplayEventReceiver中做信号分发,这里使用了handler ,在handler 中 调用doFrame(System.nanoTime(), 0);
然后是doCallbacks()这里面直接将ViewRootImpl 中传入的Runnable 获取到去调用run方法 ,这里需要注意下 信号同步是用handler 来通知的如果主线程的任务繁重 这个信号就会延迟,会使页面得不到及时重绘,出现页面卡顿问题,所以这里就解释了页面卡顿的原因 ,当然引起拉顿的原因不仅仅只有这个.
@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();
//这个方法响应 信号同步
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));
}
//回调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的绘制分发.
doTraversal()--> performTraversals() 这个方法里就是我们熟悉的 绘制的三大方法 以及关联suface 的部分
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
//下面这三个方法 会使用我们开始传入的mView 也就是DecorView 来调用内部的测量 排列 绘制方法,之后及时View绘制流程了 这里不再赘述这一部分
public void performTraversals(){
......................
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
.......................
performLayout(lp, mWidth, mHeight);
.......................
performDraw();
}
performMeasure()--->mView.measure()
performLayout()--> host.layout()
performDraw()-->draw()-->drawSoftware()