Activity启动流程详解

流程简介

启动一个Activity可能是我们在Android编程中最长用的功能之一了,方式也很简单,就是调用Context类的startActivity方法。可能有些开发者误认为startActivity就是通过调用另一个Activity子类的初始化方法来唤起这个Activity的,其实不然。

当你在使用startActivity时,这个调用会发送给属于系统一部分的ActivityManager一个信息。由ActivityManager来创建该Activity实例并且调用它的onCreate方法。流程如下图:


有人可能会问,那系统如何知道启动哪一个Activity的呢?答案是通过Intent这个组件,以下是摘自Android源码的两个最基本的Intent构造函数:

public Intent(Context packageContext, Class<?> cls) {
    mComponent = new ComponentName(packageContext, cls);
}

public Intent(String action) {
    setAction(action);
}

第一个构造函数中,ActivityManager可以获得包名,和具体的类名,进而启动这个Activity。第二个构造函数中,ActivityManager还可以通过一个叫action的字符串来找到对应的Activity来启动。我们可以感受出来,通过此种架构,可以让Activity的启动具有更大的灵活性,也更加松耦合。既然ActivityManager是系统一部分,那在每个应用安装时,也就需要告诉系统自己哪些部分是可以被这种方式启动的,此时我们就有了AndroidManifest文件,所以才有了我们需要在AndroidManifest文件中声明Activity这个组件的做法,否则就会报ActivityNotFoundException这个异常。

显示和隐式启动

理解了上面的流程,我们再来看下启动的两种方式就很简单了,一个叫显示启动,一个叫隐式启动。我们来举两个显示启动的例子

Intent intent = new Intent(this, SecondActivity.class);  
startActivity(intent);  

或者

ComponentName componentName = new ComponentName(this, SecondActivity.class);  
// 或者 new ComponentName(this, "com.example.SecondActivity");  
// 或者 new ComponentName(this.getPackageName(), "com.example.SecondActivity");  
  
Intent intent = new Intent();  
intent.setComponent(componentName);  
startActivity(intent);  

第一种我想大家再熟悉不过了,第二种在了解启动流程后耶比较容易理解,如果把第二种方法的第一个参数换成其他App的包名,还可以直接启动其它App。

我们再来介绍下隐式启动,在介绍之前,我们需要了解一个标签<intent-filter>,从字面上来看,很好理解,就是用来过滤掉一些intent。让只有具备处理特定intent的Activity才会被启动。比如在AndroidManifest中有如下声明:

<activity  
    android:name="com.example.SecondActivity">  
    <intent-filter>  
        <action android:name="12345"/>  
        <category android:name="android.intent.category.DEFAULT"/>  
    </intent-filter>  
</activity>

那么启动该Activity除了以上介绍的显示方法外,我们还可以用以下方法

Intent intent = new Intent("12345");  
intent.addCategory("android.intent.category.DEFAULT");  //可以不加,默认被加上
startActivity(intent);  

隐式启动是由action、category和data共同决定的,只有三者都满足,该Activity才会被列入可处理列表,一般intent-filter包含一个action,可以包含多个category和data,所有category和某一个data满足才行。如果你手机上多个应用同时拥有相同的intent-filter,那么就会出现一个应用列表供用户选择。

注:Android对待所有隐式Intent,默认它们已经具有了"android.intent.category.DEFAULT",所以如果你想你的Activity被隐式调用,那一定需要在其intent-filter中加上该条category。不过入口Activity被"android.intent.action.MAIN" 和"android.intent.category.LAUNCHER"标记的除外,虽然你也可以加上DEFAULT的category,但不是必须的

应用

学习了以上几点后,我们来应用一下,做一个在不接入微信SDK的情况下的微信分享。你可以用apktool反编译微信,然后通过看smali代码和AndroidManifest文件,了解它规则后就能知道如何调用它的分享界面了。这里反编译方法先不介绍了。经过分析,我们找到ShareImgUI类极为微信分享的朋友列表页面,我们来看下它的AndroidManifest.xml中内容

<activity android:configChanges="keyboardHidden|orientation|screenSize" android:icon="@drawable/icon" android:name="com.tencent.mm.ui.tools.ShareImgUI">
            <intent-filter android:label="@string/c">
                <action android:name="android.intent.action.SEND"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <data android:mimeType="image/*"/>
                <data android:mimeType="video/*"/>
                <data android:mimeType="text/*"/>
                <data android:mimeType="application/*"/>
            </intent-filter>
            <intent-filter android:label="@string/c">
                <action android:name="android.intent.action.SEND_MULTIPLE"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <data android:mimeType="image/*"/>
            </intent-filter>
 </activity>

根据以上分析,对应分享代码如下

Intent it = new Intent(Intent.ACTION_SEND);
it.setComponent(new ComponentName("com.tencent.mm","com.tencent.mm.ui.tools.ShareImgUI"));
it.putExtra(Intent.EXTRA_TEXT, "分享成功");
it.setType("text/plain");  //可以不要
mActivity.startActivity(Intent.createChooser(it, "本机未安装微信"));

相信上面的代码大家很容易就能看明白,不过有人可能会迷惑,我们已经用显示启动了,为什么还要用action呢,这是因为微信在代码里判断了action,如果你不传入这个action就无法分享。另外代码的最后一行我们为了避免微信未安装产生ActivityNotFoundException异常,用了Intent.createChooser来处理,如果本机未安装微信,在执行上述代码时,会出现一个标题为“本机未安装微信”的弹窗。根据同理,分享图片、视频、应用到微信是可以做到的,但你需要具体分析下ShareImgUI的smali代码,让自己传入的数据可以被微信接受就行。当然分享到朋友圈也一样道理。

写在后面

虽然上面分析了Activity启动流程,但该流程只是简化版,实际的启动流程要复杂很多,涉及到很多类。虽是简化版,但对我们理解有关Activity间跳转的代码足矣。后续也为大家能继续深入源码来了解这些流程提供了一点指导。

作者简介
彭涛(@彭涛me) 致力于让技术变得易懂且有趣
个人博客:http://pengtao.me, GitHub地址:https://github.com/CPPAlien

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

推荐阅读更多精彩内容