源码开始分析
从setContentView()开始分析; AppCompatActivity.java和Activity.java稍微有点不同 AppCompatActivity是Activity的子类,创建了AppCompatDelegate类,来具体实现View的初始化等功能。
应用布局解析、添加到DecorView
public abstract class AppCompatDelegate {
/**
* Create an {@link androidx.appcompat.app.AppCompatDelegate} to use with {@code activity}.
*
* @param callback An optional callback for AppCompat specific events
*/
public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
// 可见,在创建具体类是,传入了当前的Activity和Window
return new AppCompatDelegateImpl(activity, activity.getWindow(), callback);
}
}
class AppCompatDelegateImpl extends AppCompatDelegate
implements MenuBuilder.Callback, LayoutInflater.Factory2 {
@Override
public void setContentView(View v) {
ensureSubDecor();
ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
contentParent.addView(v);
mOriginalWindowCallback.onContentChanged();
}
private void ensureSubDecor() {
if (!mSubDecorInstalled) {
mSubDecor = createSubDecor();
......
applyFixedSizeWindow();
onSubDecorInstalled(mSubDecor);
mSubDecorInstalled = true;
......
}
}
private ViewGroup createSubDecor() {
......
final LayoutInflater inflater = LayoutInflater.from(mContext);
ViewGroup subDecor = null;
// 根据Feature不同选择不同的Layout.xml来初始化布局
......
// 将通过Layout.xml解析的View添加到Window的contentView上
mWindow.setContentView(subDecor);
return subDecor;
}
}
下面看下Activity.java代码,也是调用了Window setContentView
/**
* Set the activity content from a layout resource. The resource will be
* inflated, adding all top-level views to the activity.
*
* @param layoutResID Resource ID to be inflated.
*
* @see #setContentView(android.view.View)
* @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
*/
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
下面看下Window代码
/**
* Abstract base class for a top-level window look and behavior policy. An
* instance of this class should be used as the top-level view added to the
* window manager. It provides standard UI policies such as a background, title
* area, default key processing, etc.
*
* <p>The only existing implementation of this abstract class is
* android.view.PhoneWindow, which you should instantiate when needing a
* Window.
*/
public abstract class Window {
}
可见,Window只有一个子类即PhoneWindow
/**
* Android-specific Window.
* <p>
* todo: need to pull the generic functionality out into a base class
* in android.widget.
*
* @hide
*/
public class PhoneWindow extends Window {
/**
* 1. 创建DecorView
* 2. 解析layoutResId布局,添加到DecorView返回的mContengtParent中
*/
@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;
}
@Override
public void setContentView(View view) {
setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
// 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)) {
view.setLayoutParams(params);
final Scene newScene = new Scene(mContentParent, view);
transitionTo(newScene);
} else {
mContentParent.addView(view, params);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
......
}
}
/**
* 创建DecorView,其实继承FrameLayout
*/
protected DecorView generateDecor(int featureId) {
// System process doesn't have application context and in that case we need to directly use
// the context we have. Otherwise we want the application context, so we don't cling to the
// activity.
Context context;
if (mUseDecorContext) {
Context applicationContext = getContext().getApplicationContext();
if (applicationContext == null) {
context = getContext();
} else {
context = new DecorContext(applicationContext, getContext());
if (mTheme != -1) {
context.setTheme(mTheme);
}
}
} else {
context = getContext();
}
return new DecorView(context, featureId, this, getAttributes());
}
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
// 根据style设置Feature、Flag等属性
......
// Inflate the window decor.
// 根据Feature不同,设置不同的Layout布局文件layoutResource
mDecor.startChanging();
// 解析layoutResource并添加到DecorView上
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
// 因为需要根据style加载不同feature的layout,然后将主程序的layout内容添加到DecorView中
// 所以需要在setContentView()前设置requesetFeature
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
......
// 返回DecorView中的id为ID_ANDROID_CONTENT的View
return contentParent;
}
}
通过上面分析,我们知道,应用的布局最后是添加到DecorView中的,而DecorView是在PhoneWindow中维护的,那么DecorView是如何添加到PhoneWindow的呢?
DecorView添加到Window
Android处理Activity的入口是ActivityThread,且是通过Handler的消息来执行onCreate、onResume...等生命周期的方法。
/**
* This manages the execution of the main thread in an
* application process, scheduling and executing activities,
* broadcasts, and other operations on it as the activity
* manager requests.
*
* {@hide}
*/
public final class ActivityThread extends ClientTransactionHandler {
@Override
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
String reason) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
mSomeActivitiesChanged = true;
// TODO Push resumeArgs into the activity for consideration
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
......
final Activity a = r.activity;
final int forwardBit = isForward
? WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
// If the window has not yet been added to the window manager,
// and this guy did not finish itself or start another activity,
// then go ahead and add the window.
boolean willBeVisible = !a.mStartedActivity;
......
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) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);
} else {
// The activity will get a callback for this {@link LayoutParams} change
// earlier. However, at that time the decor will not be set (this is set
// in this method), so no action will be taken. This call ensures the
// callback occurs with the decor set.
a.onWindowAttributesChanged(l);
}
}
// If the window has already been added, but during resume
// we started another activity, then do not yet make the
// window visible.
} else if (!willBeVisible) {
if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");
r.hideForNow = true;
}
......
}
wm.addView(decor, l);
可见,是将DecorView添加到了WindowManagerImpl上。
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
}
/**
* Provides low-level communication with the system window manager for
* operations that are not associated with any particular context.
*
* This class is only used internally to implement global functions where
* the caller already knows the display and relevant compatibility information
* for the operation. For most purposes, you should use {@link WindowManager} instead
* since it is bound to a context.
*
* @see WindowManagerImpl
* @hide
*/
public final class WindowManagerGlobal {
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
// 参数校验
......
// Param参数调整
......
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
// Start watching for system property changes.
......
// If this is a panel window, then find the window it is being
// attached to for future reference.
// 获取panelParentView
if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
final int count = mViews.size();
for (int i = 0; i < count; i++) {
if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
panelParentView = mViews.get(i);
}
}
}
// 创建根View
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
......
// do this last because it fires off messages to start doing things
try {
// 将DecorView和panelParentView进行关联
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
}
即调用View的measure、layout、draw核心方法。
后面继续分析该部分。