跨应用调用Activity的1点理解

跨应用调用Activity的一点理解
       Android程序与Windows程序有个很大的区别,就是自己的应用可以利用其它应用的组件和功能,不用重复开发。非常典型的就是调用其它应用的Activity,相信大家都用过,比如调用系统拨号、拍照功能或是现成的浏览器。
    调用起来非常简单,利用Intent的隐式跳转,比如调用拨号:

    Intent intent = new Intent(Intent.ACTION_CALL,Uri.parse   ("tel:"+phone_number));
    startActivity(intent);

    非常简单,复杂一些的会用到intent-filter中的其它参数,但本质上都如调用拨号一样。当然在同一应用中也可以这么调用,Android的好处就是你调用自己或是其它应用的功能,看起来像一样,非常人性化。是不是知道怎么调用,或是达到会配置intent-filter,能使自己的Activty被其它应用隐式调用就完事了。如果仔细想想,应用在后台还有进程,Android中还有Task存在,应该就会疑问了。多个应用配合工作时,进程是如何工作的、Task是怎么分配的?
    所以想懂得多一些,有必要把这些疑问理一下,而且非常必要,为什么呢,因为有时候我们程序发生一些莫名其妙的问题时,往往是因为有些原理我们没有掌握,至少是没有理解的深一些,所以从表面上看不出个所以然,只有深入理解了,其实非常简单,很好解释。
    需要弄懂三个方面的知识:
    一、”android:process“属性的作用
    二、Task与Activity关系
    三、Application实例的创建

    ”android:process“属性的作用
    该属性在”AndroidManifest.xml“文件中设置,包括application、activity、service等都有该属性,一般不需要进行设置,系统会给个默认值,即包名。
    那这个属性是设置什么的呢?设置组件所在的进程名称。所以很好理解在默认情况下,同一应用的所有组件都是运行在同一进程中的,而且两个不同应用的组件,肯定是运行在不同进程中的。因为包名不一样,进程名则不一样,进程名不一样,肯定不是同一进程。
    另一个问题,组件所在进程名一样了,是不是肯定就是同一个进程? 不是的。确定是否为同一个进程,除了进程名一样外,组件所在应用的userId也要一样才行。应用的userId在”AndroidManifest.xml“中结点”manifest“下设置”android:sharedUserId“属性,默认值通常也是包名(为什么我们不能同时安装两个同包名的apk,应该也好解释了)。
    回到我们的疑问。结论是在默认情况下,调用其它应用Activity时,会新建被调用应用的进程;我们可以配置调用与被调用应用相关组件的”android:sharedUserId“、”android:process“属性,达到被调用Activity运行在调用者的进程之中,即共享一个进程。
    总结一下,把握几点:
    1.进程全名、userId都相同的进程为同一进程。
    2.同一应用能存在多个进程,不同应用可以共享一个进程。
    3.手工设置相关参数,能达到不同应用的组件同进程。
   
    Task与Activity关系
    Task就是一个栈,符合先进后出的逻辑,里面放的就是Activity实例。关于对Task的理解,很多人都讲得很清楚了,这里只讲讲与主题相关的。
    首先要明白几点:
    1.Task与应用即apk的关系是多对一的关系,即一个Task只属于一个应用,一个应用中可能包含多个Task。
    2.Task与进程没有多大关系,即同一个Task中可能放着属于多个进程的Activity。
    3.Task虽然属于某个应用,但是Task中可以放其它应用的Activity实例。

    在默认情况下,打开另一个应用的Activity时,虽然又创建了新的进程,但是不会创建另一个应用的Task(记住是默认的情况),被调用的Activity会直接放入调用应用的Task中。所以打开另一个应用的Activity后,我们再点击返回键时,会回到前一个Activity,完全感觉不到是两个应用,与一个应用的操作感受是一样的。
    既然有默认情况,那肯定有自定义情况,就是说我们能通过配置或指定相关的参数,来改变默认的系统行为。也就是什么情况下,在调用其它应用的Activity时,系统会创建新的Task呢?有几个因素:
    1.被调用Activity的launchMode参数被配置成"singleInstance"。这个很好理解,就算是同一应用调用都会新建Task,何况不同应用。
    2.调用Activity时,指定Intent的Flag参数为"Intent.FLAG_ACTIVITY_NEW_TASK"时,且被调用应用本身事先没有被已经打开了。(暂假定被调用应用的所有Activity都在同一个Task中)

    实际情况是这块还能细分,会受其它几个参数的影响(比如"taskAffinity"),但是了解到这一层,也算知道八层了。

   
    Application实例的创建
    这个知识点非常重要,没有搞懂,会影响到我们程序的健壮性。因为我们写应用时,常常会自定义Application,在里面写一些逻辑,如果没有搞懂,写出的代码可能会在一些特殊情况下,出现bug,非常典型的就是调用百度定位,当然这个话题与跨应用调用没有直接关系了。还是回到主题,来讨论。
    如果我们自定义了Application,即继承系统的“Application”类,同时在“AndroidManifest.xml”配置application结点的“android:name”属性为我们自定义的Application子类名称,则应用打开时,会创建我们自定义的Application对象,否则系统会创建默认的Application对象。
    不管是自定义的还是默认的,其创建时机都是一样的。先明白几点:
    1.Application对象与进程的关系。
      Application对象与进程是多对一的关系,即Application对象肯定属于某个进程,同时一个进程可能包含多个Application对象。
    2.Application对象与Activity的关系
      只要应用中的某个组件(当然包括Activity)启动了,Application对象会先被创建或者已存在,不管Activity对象是否运行在调用它的应用进程中。

    通过上面两点,来分析Application对象创建时机:
    1.当被调用Activity所属进程不存在时。
    2.当被调用Activity所属进程已存在,但被调用Activity所属应用的Application对象没创建,而且不在被调用Activity所在进程中时。
    关于第2点,可以去验证:配置调用应用的Activity与被调用应用的的Activity所属进程一样(记得配置应用的userId一样),打开调用应用的Activity后,会发现调用应用的Application会被创建,此时调用被调用应用的Activity,会发现虽然没有再创建进程,但是被调用应用的Application实例会被创建,且运行于同一进程中。

     上面有些机制就是在同一应用中调用Activity也是值得学习的。
     进程、Task、Application的新建或是复用我们可以进行控制,Android提供这个机制,就是要达到灵活并为开发人员所用,开发出适应性强应用的目的。当然更深层次的原理,还需要不断探索。