Android Activity启动源码分析 (一)

源码使用API 16进行分析

Demo

创建两个Activity用于源码分析:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.startactivitydemo">
       ...
        <activity android:name=".TargetActivity" />
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        ...

MainActivity.java:

...
public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(MainActivity.this, TargetActivity.class));
            }
        });
    }
}

TargetActivity.java:

...
public class TargetActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_target);
    }
}
...

向AMS发送startActivity请求

App启动Activity时,需要向系统发送请求启动信号,处理该请求的服务称为AMS(ActivityManagerService)。这里的跨进程操作发生在App进程和系统进程之间。
从onClick中的startActivity开始分析:
android.app.Activity#startActivity(android.content.Intent):

    @Override
    public void startActivity(Intent intent) {
        startActivity(intent, null);
    }

android.app.Activity#startActivity(android.content.Intent, android.os.Bundle)

    @Override
    public void startActivity(Intent intent, Bundle options) {
        if (options != null) {
                        ...
        } else {
            startActivityForResult(intent, -1);
        }
    }

android.app.Activity#startActivityForResult(android.content.Intent, int):

    public void startActivityForResult(Intent intent, int requestCode) {
        startActivityForResult(intent, requestCode, null);
    }

android.app.Activity#startActivityForResult(android.content.Intent, int, android.os.Bundle):

    public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
    ...
      //mParent的使用场景很古老了,通常都是null
      if (mParent == null) {
            Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);
            //ar始终是null,详见下文
            if (ar != null) {
                mMainThread.sendActivityResult(
                    mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                    ar.getResultData());
            }
            if (requestCode >= 0) {
                mStartedActivity = true;
            }
        } else {
    ...
    }

android.app.Instrumentation#execStartActivity(android.content.Context, android.os.IBinder, android.os.IBinder, android.app.Activity, android.content.Intent, int, android.os.Bundle):

    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
        IApplicationThread whoThread = (IApplicationThread) contextThread;
        if (mActivityMonitors != null) {
           //InstrumentationTest相关逻辑
           ...
        }
        try {
           ...
           //这里向AMS发送启动信号
            int result = ActivityManagerNative.getDefault()
                .startActivity(whoThread, intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, null, options);
            //处理AMS返回值
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
        }
        return null;
    }

跨进程发生在ActivityManagerNative.getDefault().startActivity(...)中,紧接着会对跨进程的返回值做处理。不考虑InstrumentationTest逻辑,该函数始终返回null。
android.app.Instrumentation#checkStartActivityResult:

static void checkStartActivityResult(int res, Object intent) {
        if (res >= ActivityManager.START_SUCCESS) {
            return;
        }
        
        switch (res) {
            case ActivityManager.START_INTENT_NOT_RESOLVED:
            case ActivityManager.START_CLASS_NOT_FOUND:
                if (intent instanceof Intent && ((Intent)intent).getComponent() != null)
                    throw new ActivityNotFoundException(
                            "Unable to find explicit activity class "
                            + ((Intent)intent).getComponent().toShortString()
                            + "; have you declared this activity in your AndroidManifest.xml?");
                throw new ActivityNotFoundException(
                        "No Activity found to handle " + intent);
            case ActivityManager.START_PERMISSION_DENIED:
                throw new SecurityException("Not allowed to start activity "
                        + intent);
            case ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:
                throw new AndroidRuntimeException(
                        "FORWARD_RESULT_FLAG used while also requesting a result");
            case ActivityManager.START_NOT_ACTIVITY:
                throw new IllegalArgumentException(
                        "PendingIntent is not an activity");
            default:
                throw new AndroidRuntimeException("Unknown error code "
                        + res + " when starting " + intent);
        }
    }

ActivityManager.START_SUCCESS的值是0,App进程无需处理返回值。其他的情况则依据返回值抛出对应的异常。当启动一个没有在xml中声明的,或者无权限启动的Activity,App会Crash,便是在这里产生的。

AMS跨进程发送信号

下面分析:ActivityManagerNative.getDefault().startActivity(...):

    ...
    static public IActivityManager getDefault() {
        return gDefault.get();
    }
    ...
        private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
        protected IActivityManager create() {
            //通过系统ServiceManager获取AMS句柄
            IBinder b = ServiceManager.getService("activity");
            ...
            //将句柄转换成IActivityManager接口,实际上是android.app.ActivityManagerProxy实例
            IActivityManager am = asInterface(b);
            ...
            return am;
        }
    };

可以看到ActivityManagerNative.getDefault()是个单例,返回值是从ServiceManager.getService("activity")中获取的。asInterface会检查Binder是否跨进程了,若跨进程了,会把IBinder封装成android.app.ActivityManagerProxy。Binder是Android实现跨进程的主要方式。
因此会调用到android.app.ActivityManagerProxy#startActivity中:

    public int startActivity(IApplicationThread caller, Intent intent,
            String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, String profileFile,
            ParcelFileDescriptor profileFd, Bundle options) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        //Parcel data写入要传递给AMS的数据 start
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeStrongBinder(caller != null ? caller.asBinder() : null);
        intent.writeToParcel(data, 0);
        data.writeString(resolvedType);
        data.writeStrongBinder(resultTo);
        data.writeString(resultWho);
        data.writeInt(requestCode);
        data.writeInt(startFlags);
        data.writeString(profileFile);
        if (profileFd != null) {
            data.writeInt(1);
            profileFd.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
        } else {
            data.writeInt(0);
        }
        if (options != null) {
            data.writeInt(1);
            options.writeToParcel(data, 0);
        } else {
            data.writeInt(0);
        }
        //Parcel data写入要传递给AMS的数据 end
        //跨进程
        mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
        //reply是AMS所在进程返回的数据,检查是否需要处理异常
        reply.readException();
        //读取返回结果
        int result = reply.readInt();
        //回收Parcel
        reply.recycle();
        data.recycle();
        return result;
    }

该函数向AMS发送了跨进程数据,且获得了AMS的返回数据,一次握手就完成了。Parcel是配合Binder使用的,用于承载跨进程数据结构的,是Native实现。

startActivity跨进程传递的参数

android.app.ActivityManagerProxy#startActivity中的参数较多,这里单独分析一下:

  • caller:android.app.ActivityThread.ApplicationThread对象,用于区分请求方进程,当前的请求方就是当前APP进程。
  • intent:启动意图,表示要启动什么,怎么启动
  • resolvedType::Intent中包含的type,和当前场景无关
  • resultTo:android.app.Activity#mToken传递过来的,用于区分发起请求的Activity,也可以理解为:AMS要返回数据的接收方
  • resultWho:场景无关的参数,可不关心
  • requestCode:startActivityForResult时的requestCode,当前场景是-1,场景无关
  • startFlags:当前传入的0,场景无关
  • profileFile:当前传入null,场景无关
  • profileFd:当前传入null,场景无关
  • options:启动Activity的一些其他参数,当前场景是null,场景无关
  • START_ACTIVITY_TRANSACTION:AMS接收的操作有很多,它标识了当前是startActivity操作

核心参数是caller、START_ACTIVITY_TRANSACTION和intent、resultTo,分别阐述了:

  • 谁发起了请求?
  • 要请求什么?
  • 请求返回数据给谁?

总结

startActivity由App进程中发起,发向AMS,第一次握手的由此开始。App进程侧逻辑简单,系统进程中AMS的处理才是主角。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,142评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,298评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,068评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,081评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,099评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,071评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,990评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,832评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,274评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,488评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,649评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,378评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,979评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,625评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,643评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,545评论 2 352