整体绘制流程

看本篇文章时,要考虑以下几点:

  1. Android程序是如何启动的,Activity生命周期是如何调用的?
  2. 在Activity的onCreate()方法中setContentView()是如何加载UI文件的?
  3. 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的作用实际上是将ActivityThreadApplciationThread关联,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()中做了两件事(installDecorinflate)。

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.contentcontentParent

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视图结构容器





posted @ 2020-11-22 14:25  jxiaow  阅读(169)  评论(0编辑  收藏  举报