UI整体绘制流程
在阅读本篇文章时,我们需要考虑以下几点:
- Android程序是如何启动的,Activity生命周期是如何被调用的?
- 在Activity的
onCreate()
方法中setContentView()
是如何加载UI文件的? - UI是如何绘制的?
源码分析基于API 29
Android 程序简单的启动流程
在刚接触Java
时,我们知道Java程序的运行入口是main()
方法,然而我们在日常App的开发中并没有发现main()
方法的存在,那么Android
中的程序是如何开始运行的呢?
查阅官方资料中我们会发现在Android
的源码中有一个叫做ActivityThread
的类,这个类就是Android 中的主线程
,在ActivityThread中我们可以看到main()
方法的存在。调用时序图如下:
ActivityThread#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();
}
在main()
方法中实例化了ActivityThread
类并调用了attach()
方法。
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();
}
// ...
}
}
在attach()
方法中可以看到通过ActivityManager.getService()
获取到了IActivityManager
,然后调用了IActivityManager.attachApplication()
方法并将ApplicationThread
实例传入。
注意: IActivityManager
是一个接口,它的的实现类类是ActivityManagerService
。
ActivityManager#getService()
@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;
}
};
通过上述两段代码可知,ActivityManager.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
的作用实际上是将Application
与ApplicationThread
关联并进行初始化工作,并且ApplicationThread
类为Activity
的各种生命周期状态做了相对应的代理工作。
ApplicationThread
private class ApplicationThread extends IApplicationThread.Stub {}
ApplicationThread
的方法分析就不在此处赘述了,后面的Activity的启动流程
会详细分析。
Activity 视图设置
在 onCreate()
当中我们往往会使用setContentView()
去设置我们自己的布局文件或者view, 我们可以通过源码分析看看具体的流程。
Activity#setContentView()
// Activity.java
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
Activity
中的setContentView()
通过getWindow()
调用了setContentView()
,而 getWindow()
返回的是PhoneWindow
。
PhoneWindow#setContentView()
// 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#setContentView()
中做了两件事(installDecor和 inflate
)。
PhoneWindow#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);
// ...
}
// ...
}
PhoneWindow#generateDecor()
在Window
中创建顶级的view。
protected DecorView generateDecor(int featureId) {
// ...
return new DecorView(context, featureId, this, getAttributes());
}
PhoneWindow#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>
Activity的窗口UI布局层级
本文链接:https://jxiaow.gitee.io/posts/69abe299/
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!