每当我们在Activity或者Fragment里面setContentView 是怎么将一个布局或都一个View添加到窗口的呢.
当setContentView(R.layout.activity_main)的时候
会跳转到Window.setContentView这里,从代码里的注释可以看出
Window的唯一实现类是PhotoWindow
所以直接可以看PhotoWindow.setContentView方法
PhotoWindow.setContentView方法里面会对一个成员变量mContentParent进行判断
一般开始都是为null,而mContentParent是一个什么?
这里就直接给出答案,从后面源码可以看出,他就是一个id为com.android.internal.R.id.content
public void setContentView(View view) {
getWindow().setContentView(view);
initWindowDecorActionBar();
}
public Window getWindow() {
return mWindow;
}
public class PhoneWindow extends Window implements MenuBuilder.Callback {
@Override
public void setContentView(int layoutResID) {
// 1
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
}
}
看注释1 第一次过来这个mContentParent必定为空,所以执行installDecor();
这里把installDecor()里面的核心代码贴一下
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DE
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFe
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
}
上面代码可以看出,首先会通过generateDecor(-1)这个方法创建出DecorView mDecor,面DecorView又是一个Framlayout,然后再通过
mContentParent = generateLayout(mDecor);这个方法创建了mContentParent,
上面出现了二个方法一个是创建mDecor的generateDecor 还有个是创建mContentparent的generateLayout方法
先来看generateDecor(-1)这个方法
protected DecorView generateDecor(int featureId) {
// activity.
Context context;
if (mUseDecorContext) {
Context applicationContext = getContext().getApplicationContext();
if (applicationContext == null) {
context = getContext();
} else {
context = new DecorContext(applicationContext, getContext().getResources());
if (mTheme != -1) {
context.setTheme(mTheme);
}
}
} else {
context = getContext();
}
return new DecorView(context, featureId, this, getAttributes());
}
这个方法简单,直接new了一个DecorView.
再来看下generateLayout(mDecor)这个方法,它将上面创建好的mDecor当作参数传入进来
protected ViewGroup generateLayout(DecorView decor) {
int layoutResource;
int features = getLocalFeatures();
if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogTitleDecorLayout, res, true);
layoutResource = res.resourceId;
} else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
layoutResource = a.getResourceId(
R.styleable.Window_windowActionBarFullscreenDecorLayout,
R.layout.screen_action_bar);
} else {
layoutResource = R.layout.screen_title;
}
// System.out.println("Title!");
} else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
layoutResource = R.layout.screen_simple_overlay_action_mode;
}
mDecor.startChanging();
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (getContainer() == null) {
final Drawable background;
if (mBackgroundResource != 0) {
background = getContext().getDrawable(mBackgroundResource);
} else {
background = mBackgroundDrawable;
}
mDecor.setWindowBackground(background);
final Drawable frame;
if (mFrameResource != 0) {
frame = getContext().getDrawable(mFrameResource);
} else {
frame = null;
}
mDecor.setWindowFrame(frame);
mDecor.setElevation(mElevation);
mDecor.setClipToOutline(mClipToOutline);
if (mTitle != null) {
setTitle(mTitle);
}
if (mTitleColor == 0) {
mTitleColor = mTextColor;
}
setTitleColor(mTitleColor);
}
mDecor.finishChanging();
return contentParent;
}
这面代码很长,但是很多代码都不是重点,都是根据主题来做一些操作,比如是否是全屏,actitonBar ....最核心的代码其实就是
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);这一行代码
系统根据我们的主题会创建一个layoutResource,然后onResourcesLoaded会将这个layoutResource布局转换成一个View,然后添加到mDecor里面,下面源码可以得出结论
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
mStackId = getStackId();
if (mBackdropFrameRenderer != null) {
loadBackgroundDrawablesIfNeeded();
mBackdropFrameRenderer.onResourcesLoaded(
this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
getCurrentColor(mNavigationColorViewState));
}
mDecorCaptionView = createDecorCaptionView(inflater);
final View root = inflater.inflate(layoutResource, null);
if (mDecorCaptionView != null) {
if (mDecorCaptionView.getParent() == null) {
addView(mDecorCaptionView,
new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mDecorCaptionView.addView(root,
new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
} else {
// Put it below the color views.
addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mContentRoot = (ViewGroup) root;
initializeElevation();
}
当下面这行代码执行完毕的时候
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
接着就是下面这行代码
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
最后会将contentParent这个View给Return出去
也就是mContentParent这个成员变量.id就是下面这个静态常量
public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
当上面方法执行完之后,我们回到PhoneWindow的setContentView方法里,会执行到下面这行代码
mLayoutInflater.inflate(layoutResID, mContentParent);
layoutResID:也就是我们的activity_main.xml文件
mContentParent就是根容器,也就是把我们的xml文件给渲染到了window上.
至此,onCreate方法执行完毕,所以onCreate方法执行完的时候创建了mDecro和mContentparent这二个容器.而我们View的绘制在哪?,这就得从源头开始探究,源头来自于,ActivityThread这个类.
看过Android启动流程的都知道,Android是一个通过消息机制来传递信息的,所以我们每次lauchActivity的时候都会通过ActivityThread里的的H这个类来发送消息,最后在handlemessage里面接收消息,第一次启动Activity走的是
case LAUNCH_ACTIVITY:这个what,接着就是下面这行代码
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");,接着是创建Activity
Activity a = performLaunchActivity(r, customIntent);如果创建成功接着就是下面这行代码
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
从handleResumeActivity这个方法里面抓住最关键的几行代码
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;
...
.
.
.
wm.addView(decor, l);
这里有个关键的局部变量
ViewManager wm
wm是什么 ViewManager是一个接口,而wm则就是其实现类WindowManager
它是通过Activity.getWindowManager这个方法获取到的一个成员变量mWindowManager
而mWindowManager则是通过mWindow.getWindowManager()获取
这个mWindow就是Window类
再来看看Window.getWindowManager()这个方法里的mWIndowManager是怎么创建的,下面这行代码
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
最后直接new WindowManagerImpl(mContext, parentWindow);
得到了mWindowManager
所以从上面可以看出wm就是WindowManagerImpl
wm.addView就是走的WindowManagerImpl这个类里的addView方法
进来探索一下,看下面这行代码
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
mGlobal是WindowManagerGlobal,再看mGlobal.addView方法,去掉一些无关代码,很简洁了
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
int index = findViewLocked(view, false);
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;
}
}
}
从上面代码可公分析出来,最后会走到一个root.setView方法里面,root是ViewRootImpl,进入方法里面,在setView里面有一行关键的代码
requestLayout();啥也不说进去看一看,关键性代码来了
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
//这里只有是false的时候才会进来,所以如果下面代码没有执行完是不会继续绘制
mTraversalScheduled = true;
//发送一个同步屏障消息,意思就是叫消息轮询器不要管我这边了,去执行其它的消息
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
这里来研究一下Choreographer mChoreographer这个类里面的mChoreographer.postCallback这个方法
public void postCallback(int callbackType, Runnable action, Object token) {
postCallbackDelayed(callbackType, action, token, 0);
}
public void postCallbackDelayed(int callbackType,
Runnable action, Object token, long delayMillis) {
if (action == null) {
throw new IllegalArgumentException("action must not be null");
}
if (callbackType < 0 || callbackType > CALLBACK_LAST) {
throw new IllegalArgumentException("callbackType is invalid");
}
postCallbackDelayedInternal(callbackType, action, token, delayMillis);
}
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
//1 .将一个任务添加到了队列里面.
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
//2. 正常情况走这段逻辑
if (dueTime <= now) {
scheduleFrameLocked(now);
} else {
//3 . TextView有设置不看.
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
最后是来到了mChoreographer.postCallbackDelayedInternal(...)这里
从上面逻辑可以看也肯定是走注释2里面的代码scheduleFrameLocked(now);
private void scheduleFrameLocked(long now) {
if (!mFrameScheduled) {
//标志们,防止多次执行
mFrameScheduled = true;
if (USE_VSYNC) {
//1 .如果是UI线程执行 scheduleVsyncLocked();
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);
}
}
}
注释1,可以看出,如果是UI线程则走scheduleVsyncLocked()这方法,
mDisplayEventReceiver.scheduleVsync(),这个mDisplayEventReceiver是什么,FrameDisplayEventReceiver mDisplayEventReceiver是Choreographer的内部类,继承DisplayEventReceiver实现了Runnable接口,下面会分析这个类
代码如下
private void scheduleVsyncLocked() {
mDisplayEventReceiver.scheduleVsync();
}
private final class FrameDisplayEventReceiver extends DisplayEventReceiver
implements Runnable {
}
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 {
nativeScheduleVsync(mReceiverPtr);
}
}
private static native void nativeScheduleVsync(long receiverPtr);
上面可以看是,通过jni调用底层方法,下一个vsync信号来的时候通知一下,然后就会收到JNI的回调,也就是下面二个方法,面onVsync是一个空方法,由实现类去实现,应用必须向底层请求vsync信号,然后下一下vsync信号来的时候才会去执行接下来会分析到的绘制逻辑.
@SuppressWarnings("unused")
private void dispatchVsync(long timestampNanos, int builtInDisplayId, int frame) {
onVsync(timestampNanos, builtInDisplayId, frame);
}
public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
}
而是谁实现了DisplayEventReceiver,上面分析的FrameDisplayEventReceiver,来看下方法的实现
public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
if (builtInDisplayId != SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
scheduleVsync();
return;
}
long now = System.nanoTime();
if (timestampNanos > now) {
timestampNanos = now;
}
if (mHavePendingVsync) {
} else {
mHavePendingVsync = true;
}
mTimestampNanos = timestampNanos;
mFrame = frame;
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
上面会做一堆判断,比如重新请求信号,更正时间,最后就会通过Handler发送一个异步消息,指定了时间,所以最终又会走到异步方法的run里面,也就是自己的Run方法,
@Override
public void run() {
mHavePendingVsync = false;
doFrame(mTimestampNanos, mFrame);
}
从上面方法可以得出,只有得上一个消息被处理完,才会执行我们这个run方法,也就是说,如果Handler存在了耗时那么这里就会等待.
才会执行run方法里面的doFrame方法,调用慢了会怎样,看分析
void doFrame(long frameTimeNanos, int frame) {
final long startNanos;
synchronized (mLock) {
if (!mFrameScheduled) {
return; // no work to do
}
long intendedFrameTimeNanos = frameTimeNanos;
startNanos = System.nanoTime();
//1 .当前时间减去接收到vsycn信号的时间,也就是主线程耗时
final long jitterNanos = startNanos - frameTimeNanos;
//2. 如果这个时间大于1帧的时候,16ms
if (jitterNanos >= mFrameIntervalNanos) {
//2. 计算大了多少帧
final long skippedFrames = jitterNanos / mFrameIntervalNanos;
//超过了默认的就会Log提示,表示卡顿了
if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
Log.i(TAG, "Skipped " + skippedFrames + " frames! "
+ "The application may be doing too much work on its main thread.");
}
final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
frameTimeNanos = startNanos - lastFrameOffset;
}
if (frameTimeNanos < mLastFrameTimeNanos) {
scheduleVsyncLocked();
return;
}
if (mFPSDivisor > 1) {
long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos;
if (timeSinceVsync < (mFrameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) {
scheduleVsyncLocked();
return;
}
}
mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
mFrameScheduled = false;
mLastFrameTimeNanos = frameTimeNanos;
}
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);
}
}
从上面可以得出结论,开始会计算收到vsync信号到执行doFrame的时间差,vsync信号是16ms一次,大于16ms就是掉帧了.如果超过了默认值30就会打印Log提示.如果中途出现了修改系统时间就重新请求vsync信号就会不走下面绘制逻辑.如果一切正常就会按顺序执行doCallbacks(...)
在doCallbacks里面如果一切正常就会取出其他任务出来执行run方法,也就是mTraversalRunnable这个任务.
我们只看postCallback这个方法里的mTraversalRunnable这个参数
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
一看代码就明白,直接来到doTraversal()方法里,,然后就是下面的它它它...
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
//解除上面的异步屏障
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
performLayout(lp, mWidth, mHeight);
performDraw();
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
if (mView == null) {
return;
}
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);//mView就是mDecor
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
至此差不多结束了,最后就是走到
mDecor.measure mDecor.layout 然后就是自定义View的一些基本原理