1.一切从setContentView说起以下源码均取自Android API 23。
public class ViewActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view);
}
}
AppCompatActivity中
public class AppCompatActivity extends FragmentActivity implements AppCompatCallback{
@Override
public void setContentView(@LayoutRes int layoutResID) {
getDelegate().setContentView(layoutResID);
}
}
AppCompatDelegate中
public abstract class AppCompatDelegate {
public abstract void setContentView(@LayoutRes int resId);
}
其实现AppCompatDelegateImplV7:
class AppCompatDelegateImplV7 extends AppCompatDelegateImplBase{.
private ViewGroup mSubDecor;
@Override
public void setContentView(int resId) {
ensureSubDecor();
ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
LayoutInflater.from(mContext).inflate(resId, contentParent);
mOriginalWindowCallback.onContentChanged();
}
}
// AppCompatDelegateImplBase继承自AppCompatDelegate
abstract class AppCompatDelegateImplBase extends AppCompatDelegate
上面的代码除了第一行外,都很好理解。mSubDecor拿到contentParent,并把传入的resId以子View的形式添加到contentParent。可见contentparent是我们布局文件的父View,也是decor的子View。而第一行代码就是初始化mSubDecor.
private void ensureSubDecor() {
if (!mSubDecorInstalled) {
mSubDecor = createSubDecor();
...
}
}
private ViewGroup createSubDecor() {
mWindow.getDecorView();
final LayoutInflater inflater = LayoutInflater.from(mContext);
ViewGroup subDecor = null;
if (!mWindowNoTitle) {
if (mIsFloating) {
subDecor = (ViewGroup) inflater.inflate(
R.layout.xxx, null);
mHasActionBar = mOverlayActionBar = false;
} else if (mHasActionBar) {
subDecor = (ViewGroup) LayoutInflater.from(themedContext)
.inflate(R.layout.xxx, null);
}
} else {
subDecor = (ViewGroup) inflater.inflate(
R.layout.xxx, null);
}
mWindow.setContentView(subDecor);
return subDecor;
}
PhoneWindow是Window的实现类,看里面的上文出现的两个方法
@Override
public final View getDecorView() {
if (mDecor == null) {
installDecor();
}
return mDecor;
}
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor();
}
}
protected DecorView generateDecor() {
return new DecorView(getContext(), -1);
}
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
mLayoutInflater.inflate(layoutResID, mContentParent);
}
到这里为止,我们的布局文件已经加载到了Window中。下面看布局文件如何显示。
2. ViewRootImpl
先了解Activity的创建过程。我们知道java程序的执行是从main方法开始的,而Activity的main方法在ActivityThread中,Activity的生命周期也是在这里完成的。其他的先不多讲,先看Activity的启动方法
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed);
}
}
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume) {
if (r.window == null && !a.mFinished && willBeVisible) {
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 (a.mVisibleFromClient) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
}
}
看ViewManager的addView方法
public interface ViewManager
{
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}
WindowManager继承ViewManager,也是接口。其实现类WindowManagerImpl
public final class WindowManagerImpl implements WindowManager{
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mDisplay, mParentWindow);
}
}
Global的addVeiw方法
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
}
最终ViewRootImpl把WindowManager和DecorView连在一起。
3. ViewRootImpl
ViewRoot对应于ViewRootImpl类,它是连接WindowManager和DecorView的纽带,View的三大流程均是通过ViewRoot来完成的。在ActivityThread中,当Activity对象被创建完毕后,会将DecorView添加到Window中,同时会创建ViewRootImpl对象,并将ViewRootImpl对象和DecorView建立关联。
View的绘制流程是从ViewRoot的performTraversals方法开始的,它经过measure、layout、draw三个过程才能最终将一个View绘制出来。其中measure测量View的宽高,layout确定View在父容器中放置的位置,而draw负责将View绘制在屏幕上。
performTraversals会依此调用performMeasure、performLayout和performDraw三个方法,这三个方法分别完成顶级View的measure、layout、draw三大流程。其中performMeasure会调用measure方法,在measure方法中又会调用onMeasure方法,在onMeasure方法中则会对所有的子元素进行measure过程,这个时候measure流程就从父容器传递到了子元素中,这样就完成了一次measure过程。performLayout和performDraw的流程类似,唯一不同的是performDraw的传递过程是dispatchDraw来实现的。
4. setContentView
我们知道我们的布局文件和DecorView之间有一个ContentView。这个ContentView的id是android.R.content。所以是setContentView,而不是setView().