Foreword
In Android, we know that we can see the interface is Activity, but we can see how these are being added to the View View of Nick? Today in this article we take a track traceability from source to see exactly how the View to be added to the phone screen. Lengthy article please be patient to read.
View step is added to the Activity
Activity time we write a general resource to resource to load the Activity are written as shown below.
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
Here we call the Activity setContentView(@LayoutRes int layoutResID)
, so let's continue to track the Activity of the source code, look at its setContentView(@LayoutRes int layoutResID)
approach.
1、Activity.setContentView(int resourceId)
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
We see here that it calls getWindow()
Gets an object called its setContentView(layoutResID)
methods. Then we look at getWindow()
what is the return object.
/**
* Retrieve the current {@link android.view.Window} for the activity.
* This can be used to directly access parts of the Window API that
* are not available through Activity/Screen.
*
* @return Window The current window, or null if the activity is not
* visual.
*/
public Window getWindow() {
return mWindow;
}
We see a direct return here mWindow
, where we see a comment that says this method returns the current Activity in the window, if the current Activity is not visible then returns null
.
We continue to follow the code, find mWindow
created, we will find that in the attach()
process, find the mWindow
assignment.
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
mWindow = new PhoneWindow(this, window, activityConfigCallback);
/***********省略部分代码*****************/
}
We see here mWindow
is a PhoneWindow
target, so here call PhoneWindow
the setContentView(layoutResID)
method.
2、PhoneWindow.setContentView(int layoutResID)
We then look at PhoneWindow.setContentView(int layoutResID)
the source code:
@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;
}
We see from the first line, the first judge mContentParent
is not to null
, if it is null
, the implementation of the indtallDecor()
method, let's continue to look at its source code.
2.1 installDecor()
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
//初始化 DecorView
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
} else {
//给 DecorView 设置 PhoneWindow
mDecor.setWindow(this);
}
if (mContentParent == null) {
//初始化 mContentParent
mContentParent = generateLayout(mDecor);
// Set up decor part of UI to ignore fitsSystemWindows if appropriate.
//设置 DecorView 忽略fitsSystemWindows
mDecor.makeOptionalFitsSystemWindows();
/*************省略部分代码************************/
}
}
This part of the code we first saw in the third row to mDecor
do the initialization, here mDecor
it is DecorView
. Then, if mDecor
not the current Window is set to null, then to DecorView. Analyzing the back mContentParent
is not empty, initialization is empty mContentParent. To sum up this method does the following things:
- If mDevor for the air-conditioning
generateDecor(-1)
initialization DecorView; - DecorView set to empty the current PhoneWindow if mDecor not;
- If mContentParent is empty initialization mContentParent;
Below we look at this we first look generateDecor(-1)
method is how to initialize DecorView
the.
I, initialization DevoreView
protected DecorView generateDecor(int featureId) {
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());
}
Here we see that this method is relatively single piece, its return value is a new direct DecorView object.
Here we look at mDecor.setWindow(this)
.
II、mDecor.setWindow(this)
void setWindow(PhoneWindow phoneWindow) {
mWindow = phoneWindow;
Context context = getContext();
if (context instanceof DecorContext) {
DecorContext decorContext = (DecorContext) context;
decorContext.setPhoneWindow(mWindow);
}
}
This method is very single piece, is the incoming PhoneWindow
object is assigned to DecorView
the window
property.
III, initialization mContentParent
protected ViewGroup generateLayout(DecorView decor) {
/****************省略部分代码***************************/
// Inflate the window decor.
int layoutResource;
int features = getLocalFeatures();
// System.out.println("Features: 0x" + Integer.toHexString(features));
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
layoutResource = R.layout.screen_swipe_dismiss;
setCloseOnSwipeEnabled(true);
} else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogTitleIconsDecorLayout, res, true);
layoutResource = res.resourceId;
} else {
layoutResource = R.layout.screen_title_icons;
}
// XXX Remove this once action bar supports these features.
removeFeature(FEATURE_ACTION_BAR);
// System.out.println("Title Icons!");
} else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
&& (features & (1 << FEATURE_ACTION_BAR)) == 0) {
// Special case for a window with only a progress bar (and title).
// XXX Need to have a no-title version of embedded windows.
layoutResource = R.layout.screen_progress;
// System.out.println("Progress!");
} else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
// Special case for a window with a custom title.
// If the window is floating, we need a dialog layout
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogCustomTitleDecorLayout, res, true);
layoutResource = res.resourceId;
} else {
layoutResource = R.layout.screen_custom_title;
}
// XXX Remove this once action bar supports these features.
removeFeature(FEATURE_ACTION_BAR);
} else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
// If no other features and not embedded, only need a title.
// If the window is floating, we need a dialog layout
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;
} else {
// Embedded, so no decoration is needed.
layoutResource = R.layout.screen_simple;
// System.out.println("Simple!");
}
//////////////////////我是华丽的分隔线////////////////////////////////////////
mDecor.startChanging();
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
/****************省略部分代码***************************/
return contentParent;
}
Here we see that there are two parts of the text of the code, we can see that a large section of the dividing line above if/else
is based on different criteria for layoutResource
assignment. This large sections of the code is based on our set of Application
themes to select the corresponding resource layout Id loaded.
Then we saw immediately call DecorView
the onResourcesLoaded(mLayoutInflater, layoutResource)
method, so we continue to look at this method specifically what things to do
onResourcesLoaded
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
/**************省略部分代码**************************/
final View root = inflater.inflate(layoutResource, null);
/**************省略部分代码**************************/
// Put it below the color views.
addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mContentRoot = (ViewGroup) root;
initializeElevation();
}
Here we look at the code or part of the core, here we see first passed in resource layout inflate
and assign it to root
, and then root
added to the DecorView
medium. It is a word mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
of this code is to load the corresponding resource file to the layoutmDecor
.
Here we continue to look at generateLayout(DecorView decor)
the method, we found the following it contentParent
through findViewById(ID_ANDROID_CONTENT)
were assigned. Then we look ID_ANDROID_CONTENT
ID corresponding to the value.
/**
* The ID that the main layout in the XML layout file should have.
*/
public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
Comments say this is the ID of the XML master layout must be present. That is, before the assignment layoutResource
must be present. Let's grab a top layout xml file to take a look.
R.layout.screen_simple
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical">
<ViewStub android:id="@+id/action_mode_bar_stub"
android:inflatedId="@+id/action_mode_bar"
android:layout="@layout/action_mode_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="?attr/actionBarTheme" />
<FrameLayout
android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foregroundInsidePadding="false"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>
We see R.id.content
does exist, and it is a FrameLayout
. Here we look at these specific front now to do what?
Shown here holds a PhoneWindow Activity objects, there is a PhoneWindow DecorView, DecorView loaded layout of a basic resource, there are title, ActionBar the like, there is certainly an id below R.id.ccontent
resource layout.
See here would be finished? No, no, and we continue to look down! ! !
2.2 mLayoutInflater.inflate(layoutResID, mContentParent)
We continue to return to PhoneWiondow
the setContentView(int layoutResID)
, as follows:
@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;
}
We read the earlier installDecor()
method, we continue to look down, we see the following call immediately mLayoutInflater.inflate(layoutResID, mContentParent);
before an Activity from the incoming resources will be loaded into the layout we initialize earlier mContentParent
, that is the resource ID of R.id.ccontent
the Framlayout in. Now let's look at our layout passed in resource file is loaded to where?
to sum up
In this article we start from Ativity of setContentView (layoutResourceId), a step by step tracking Android FrameWork layer source. Explores the code we usually write how the layout resource is loaded into the Activity of. According to our source simply draw a flow chart is as follows:
Welcome to leave in the comments area to share your ideas together, grow together. If today's article to help you in work and life, welcome to forward share to more people.
At the same time welcome to join the big front-end learning exchange group I set up, the group we learn from the exchange of Android, Flutter and other knowledge together. From here we can discuss, exchange, upgrade together.
Group number: 872 749 114