整体绘制流程
看本篇文章时,要考虑以下几点:
- Android程序是如何启动的,Activity生命周期是如何调用的?
- 在Activity的
onCreate()
方法中setContentView()
是如何加载UI文件的? UI
是如何绘制的?
流程分析源码基于 API 29。
Android程序流程
在刚接触Java
时,了解到Java程序运行的入口是main()
, 但是在日常开发App
中并没有发现main()的存在,那么Android中的程序是如何开始运行的?
查阅官方资料中我们会发现在Android中有一个叫做ActivityThread
的类,这个类代表的就是Android当中的主线程,在ActivityThread中可以看到有main()。
main()
public static void main(String[] args) {
//...
Looper.prepareMainLooper();
//...
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
// ...
Looper.loop();
}
attach()
在main
方法中可以看到ActivityThread
中调用了attach()
。
@UnsupportedAppUsage
private void attach(boolean system, long startSeq) {
sCurrentActivityThread = this;
mSystemThread = system;
if (!system) {
// ...
final IActivityManager mgr = ActivityManager.getService();
try {
mgr.attachApplication(mAppThread, startSeq);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
// ...
}
}
ActivityManager#getService()
在ActivityThread#attach()
方法中可以看到通过ActivityManager#getService()
方法获取了IActivityManager
。
@UnsupportedAppUsage
public static IActivityManager getService() {
return IActivityManagerSingleton.get();
}
@UnsupportedAppUsage
private static final Singleton<IActivityManager> IActivityManagerSingleton =
new Singleton<IActivityManager>() {
@Override
protected IActivityManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
}
};
进入到上述代码块中, getService()
通过Binder
机制获取系统的ActivityManagerService
服务。
ActivityManagerService#attachApplication()
在ActivityThread#attach()
中调用了attachApplication()
方法。
@Override
public final void attachApplication(IApplicationThread thread, long startSeq) {
synchronized (this) {
// ...
attachApplicationLocked(thread, callingPid, callingUid, startSeq);
// ...
}
}
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid, int callingUid, long startSeq) {
//....
thread.bindApplication(processName, appInfo, providers,
instr2.mClass,
profilerInfo, instr2.mArguments,
instr2.mWatcher,
instr2.mUiAutomationConnection, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.isPersistent(),
new Configuration(app.getWindowProcessController().getConfiguration()),
app.compat, getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
buildSerial, autofillOptions, contentCaptureOptions);
//...
}
attachApplication
的作用实际上是将ActivityThread
与ApplciationThread
关联,ApllicationThread的类为activity的各种状态做了相对应的代理工作,并进行Application
初始化操作。
ApplicationThread
private class ApplicationThread extends IApplicationThread.Stub {}
通过上述分析,ApplicationThread当中在Activity
各种生命周期中扮演了什么角色呢?
在ApplicationThread
类中,系统服务通过scheduleTransaction()
方法进行Activity生命周期
的各种转换。(本次只是简单的从整个App运行流程上讲,Activity的启动流程后续补充。)
setContentView
在onCreate当中我们往往会使用setContentView去进行设置我们自己的布局文件或者view,那么在这当中他到底是怎么做的?通过观察源码,这个时候通过一系列线索我找到了最终的位置PhoneWindow类。
setContentView
// Activity.java
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
// Window.java
/* <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 {
public abstract void setContentView(@LayoutRes int layoutResID);
}
// PhoneWindow.java
@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();
// ...
}
在PhoneWindow#setCcontentView()
中做了两件事(installDecor
和 inflate
)。
installDecor
installDecor
中主要是创建了Decor
(顶层视图)和contentParent
视图(我们写的布局的父视图)。
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);
// ...
}
// ...
}
generateDecor
在Window
中创建顶级的view。
protected DecorView generateDecor(int featureId) {
// ...
return new DecorView(context, featureId, this, getAttributes());
}
generateLayout
根据设置App的不同主题获取不同的系统父布局文件,进行布局初始化并查找id为com.android.internal.R.id.content
的contentParent
。
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
// 根据配置的不同主题,选择系统父布局文件
layoutResource = R.layout.screen_simple;
mDecor.startChanging();
// 加载布局文件
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
// ...
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
// ...
return contentParent;
}
DecorView#onResourcesLoaded
onResourcesLoaded
加载系统父布局文件,并将其添加到DecorView
中。
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));
// ...
}
R.layout.screen_simple.xml
<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>