Activity 启动流程详解

流程简介

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

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

《Activity 启动流程详解》

有人可能会问,那系统如何知道启动哪一个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。让只有具备处理特定intent的Activity才会被启动。比如在AndroidManifest中有如下声明:


     
      
       
        
       
      
      
    

那么启动该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中内容


     
      
       
        
            
       
      
      
      
       
        
         
       
      
      
    

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

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) 致力于让技术变得易懂且有趣
个人博客:pengtao.me, GitHub地址:github.com/CPPAlien

    原文作者:Android源码分析
    原文地址: https://juejin.im/entry/580d71378ac247005b593eda
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞