在阅读本篇文章时,我们需要考虑以下几点:

  1. Android程序是如何启动的,Activity生命周期是如何被调用的?
  2. 在Activity的onCreate()方法中setContentView() 是如何加载UI文件的?
  3. UI是如何绘制的?

源码分析基于API 29

Android 程序简单的启动流程

在刚接触Java时,我们知道Java程序的运行入口是main()方法,然而我们在日常App的开发中并没有发现main() 方法的存在,那么Android 中的程序是如何开始运行的呢?

查阅官方资料中我们会发现在Android的源码中有一个叫做ActivityThread的类,这个类就是Android 中的主线程,在ActivityThread中我们可以看到main() 方法的存在。调用时序图如下:

UI整体绘制流程

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 的作用实际上是将ApplicationApplicationThread关联并进行初始化工作,并且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设置的不同主题获取不同的系统父布局文件,进行布局初始化并查找idcom.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布局层级

Activity-Activity视图结构容器