Android 四大组件之Activity

本篇博文主要讲解Activity,分成四个部分:Activity的生命周期,Activity的启动模式,IntentFilter的匹配规则,Activity的工作过程。

一  Activity的生命周期

1.在正常情况下,Activity的生命周期分析

在正常情况下,Activity会经历如下生命周期。

(1).onCreate: 表示Activity正在被创建,这是生命周期的第一个方法。在这个方法中我们可以做一些初始化操作。

(2).onRestart: 表示Activity 正在重新启动。一般情况下,当当前Activity从不可见重新变为可见状态时,onRestart就会被调用。这种情形一般是用户行为导致的。比如用户按Home键切换到桌面或者用户打开了一个新的Activity, 这时当前的Activity就会暂停,也就是onPauseonStop 被执行了,接着用户又回到了这个Activity,这种情形就会拿出现。

(3).onStart: 表示Activity正在被启动,即将开始,这时Activity已经可见了,但是还没有出现在前台,还无法和用户进行交互。这个时候可以理解为Activity已经显示出来了,只是我们还看不到。

(4).onResume: 表示Activity已经可见了,并且出现在前台开始活动。要注意这和onStart的对比,onStartonResume都表示Activity已经可见,但是onStart的时候,Activity还在后台,而onResume的时候,Activity才显示到前台。

(5).onPause: 表示Activity正在停止,正常情况下,紧接着onStop就会被调用。在特殊情况下,如果这个时候快速地再回到当前的Activity,那么onResume就会被调用。这种情况属于极端情况,此时可以做一些存储数据,停止动画等工作,但是不能太耗时,因为这个会影响到新Activity的显示,onPause必须先执行完,新ActiviyonResume才会执行。

(6).onStop:表示Activity即将停止,可以做一些稍微重量级的回收工作,同样不能耗时。

(7).onDestory: 表示Activity即将被销毁,这是Activity生命周期中的最后一个回掉,在这里,我们可以做一些回收工作和最终的资源释放。

2.在异常情况下,Activity的生命周期分析

(1).当前Activity处于横屏状态时,如果突然选装屏幕,由于系统配置发生了改变,在默认情况下,Activity就会被销毁并重建,当然,我们也可以阻止系统重建Activity.

Activity销毁重建的生命周期如下:

Activity->意外情况->onSaveInstanceState->onStop->onDestory

Activity ->onCreate->onRestoreInstanceState.

    当Activity被重新创建后,系统会调用onRestoreInstanceState,并且把Activity销毁时onSaveInstanceState 方法保存的Bundle对象作为参数同时传递给OnRestoreInstanceStateOnCreate方法。因此,我们通过onRestoreInstanceStateonCreate方法来判断当前Activity是否被重建了,如果被重建了,那么我们就可以取出之前保存的数据并恢复,从时序上来说,onRestoreInstanceState调用时机再onStart之后。

 

 同时,在onSaveInstanceStateonRestoreInstanceState方法中,系统自动为我们做了一些恢复性工作。当Activity在异常情况下需要重建时,系统会默认为我们保存当前Activity的视图结构,并且在Activity重启之后为我们恢复这些数据,比如文本框中用户输入的数据,ListView滚动的位置等,这些View相关的状态系统都能够默认为我们恢复。具体针对某一个特定的View系统能为我们恢复哪些数据,我们可以查看View源码。和Activity一样,每个View都有onSaveInstanceStateonRestoreInstanceState方法。

 

关于保存和恢复View层次结构,系统的工作流程是这样的:首先Activity被意外终止时,Activity会调用onSaveInstanceState去保存数据,然后,Activity会委托Window去保存数据,接着Window再委托顶层容器去保存数据。顶层容器是一个ViewGroup。一般来说,很可能是DetorView。最后顶层容器再去一一通知他的子元素来保存数据,这样来说,整个数据保存过程就完成了。这是一种典型的委托思想,上层委托下层,父容器委托子容器去处理一件事情,这种思想在Android中有很多应用,比如View的绘制过程,时间的分发都是采用类似的思想。至于数据恢复过程也是类似。

(2).资源内存不足导致低优先级的Activity被杀死

这种情况下数据恢复和存储情况和(1)完全一样。我们考虑一下Activity的优先级情况。Activity按照优先级从高到底,可以分为三种:

1.前台Activity正在和用户交互,优先级最高。

2.可见但非前台Activity,biru Activity中弹出了一个对话框,导致Activity可见但无法和用户交互。

3.后台Activity,也就是已经被暂停的Activity,比如执行了onStop,优先级最低。

 

  当系统内存不足时,系统会按照上述优先级去杀死目标Activity所在进程,并在后续通过onSaveInstanceStateonRestoreInstanceState来存储和恢复数据。如果一个进程没有在四大组件中执行,那么这个进程将很快被杀死,因此,一些后台工作不适合脱离四大组件而独自运行在后台。比较好的方法是将后台工作放入Service中,从而保证一定的优先级,这样就不会被系统轻易杀死。

 

二  Activity的启动模式

Activity有四种启动模式,分别为: standard,singleTop,singleTask,singleInstance.

(1).Standard

Standard 是标准模式,也是系统的模式模式.每次启动一个Activity都会重新创建一个新的实例,不管这个实例是否已经存在。这时一种典型的多实例实现,一个人物栈中可以有多个实例,每个实例也可以属于不同的人物栈。这种模式一下,谁启动了这个Activity,那么这个Activity就运行在启动它的那个Activity中。所以我们无法使用ApplicationContext去启动一个Standard模式的Activity,因为非Activity类型的Context(比如ApplicationContext)并没有任务栈。解决这个问题的方式是为待启动的Activity指定FLAT_ACTIVITY_NET_TASK标记位,这样启动时就会为他创建一个新的任务栈,这个时候启动的Activity实际上是一singleTask模式启动的。

 

(2).singleTop

SingleTop 是栈顶复用模式。这种模式下,如果新的Activity已经处于栈顶,那么此Activity就不会重新创建。同时他的onNewIntent方法会被调用,通过此方法的参数,我们可以取出当前请求的信息。这个时候,ActivityonCreate,onStart不会被系统调用,因为他并没有发生改变。如果新的Activity实例已存在,但不是位于栈顶,那么新的Activity仍然会被重新创建。

(3).singleTask

SingleTask是栈内复用模式。这是一种单例模式。在这种模式下,只要Activity在一个栈中存在,那么次次启动Activity就不会创建新的Activity实例。和SingletTop一样,系统也会回调其onNewIntent。具体一点,当一个具有SingleTask模式的Activity启动后,比如Activity A,系统首先会寻找是否存在A想要的任务栈。如果不存在,就重新创建一个任务栈,然后创建A的实例后放入A中。如果存在所需的A的任务栈,这时要看A的实例是否在栈中存在,如果存在实例(由于SingleTast自带ClearTop效果,如果A不是处于栈顶,那么A之前的所有Activity都要出栈,使亲处于栈顶,太霸道了。。。。),那么系统就会把A掉到栈顶并调用它的onNetIntent方法,如果实例不存在,那么就会创建A的实例,并把他押入栈中。

(4).singleInstance

SingleInstance即是单例模式。这是一种加强的SingleTask模式,它除了具有singleTop模式的所有特性外,还加强了一点,那就是此种模式的Activity只能单独地位于一个任务栈中,换句话说,比如Activity AsingleInstance模式,当A启动后,系统会为它创建一个新的任务栈,然后A独自在这个新的任务栈中(。。。。。。),由于栈内福永的特性,后续的请求均不会创建新的Activity,除非这个任务栈被销毁了。

 

 

singleTask模式中,多次提到某个Activity所需要的任务栈,什么是任务栈呢?先说另一个参数:TaskAffinity,可以翻译为任务相关性。这个参数标识了一个Activity所需要的任务栈名字,默认情况下,所有的Activity所需要的任务栈名字为应用的包名。当然,我们可以为每个Activity都单独指定TaskAffinity属性,这个属性值必须不能和包名相同,不然就没有意义。TaskAffinity属性主要和singleTask启动模式或者allowTaskReparenting属性配对使用,在其他情况下没有意义。另外,任务栈分为前台任务栈和后台任务栈,后台任务栈中的Activity位于暂停状态,用户可以通过切换将后台任务栈再次调到前台。

TaskAffinitysingleTask启动模式配对使用的时候,它具有该模式Activity的目前任务栈名字,待启动的Activity会运行在名字和TaskAffinity相同的任务栈中。

 

TaskAffinityallowTaskReparenting结合的时候,这种情况比较复杂。例如,当一个应用A启动了应用B的某个Activity后,如果这个ActivityalllowTaskReparenting true的话,那么应用B被启动后,此Activity会直接从应用A的任务栈转移到B的任务栈。

那么如何指定Activity的启动模式呢。有两种方法,第一种通过Android ManifestActivity指定启动模式:android:launchMode=”singleTask”;另一种情况是通过在Intent中设置标志位来为Activity指定启动模式,比如intent.addFlags(Intent.FLAT_ACTIVITY_NEW_TASK);这两种方式都可以为Activity指定启动模式,但是二者还是有区别的。首先优先级的话第二种要高于第一种。其次,上述方式在限定范围上有所不同,比如第一种无法直接为Activity设定FLAT_ACTIVITY_NEW_TASK标识,而第二种无法为Activity指定singleInstance模式。

 

介绍几个常用Android标记位:

FLAG_ACTIVITY_NEW_TASK:这个标记位为Activity指定singleTask启动模式,其效果与在XML中指定相同。

FALG_ACTIVITY_SINGLE_TOP: 等同于singleTop;

FLAT_ACTIVITY_CLEAR_TOP: 具有此标记位的Activity,当它启动时,在同一个任务栈中所有位于它上面的Activity都要出栈。这个标记位一般与singleTop模式一起出现,在这种情况下,被启动Activity的实例如果已经存在,那么系统会调用它的onNewIntent。如果被启动的Activity采用standard模式启动,那么他连同之上的Activity都要出栈,系统会创建新的Activity实例来放入栈顶。

FALG_ACTIVITY_EXCLUDE_FROM_RECENTS: 具有这个标记的Activity不会出现在历史Activity列表中,当没某些情况下我们不希望用户通过历史列表回到我们的Activity的时候比较有用。它也可以写在XML中:android:excludeFromRecents=”true”

 

三 IntentFilter的匹配规则

Activity的启动分为两种:显示调用和隐式调用。显示调用比较简单,就不讲了。下面主要讲隐式调用。隐式调用需要Intent能够匹配目标组件的IntentFilter中所设置的过滤信息,如果不匹配,将无法启动Activity.  IntentFilter中过滤信息有action ,category,data. 为了匹配过滤列表,需要同时匹配过滤列表中的action,category,data信息,否则匹配失败。一个过滤列表中的actioncategorydata可以有多个,所有的actioncategorydata分别构成不同的类别,同一类别的信息共同约束当前类别匹配过程。只有个一个Intent同时匹配action类别,category类别,data类别才算完全匹配,只有匹配成功才能启动一个目标Activity。另外一点,一个Activity中可以有多个IntentFilter,一个Intent只要能匹配任何一组intent-filter既可以启动对应的Activity.例如:

<activity
    android:name=".MainActivity"
    android:label="@string/app_name" >
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
<intent-filter>
         <action android:name="android.intent.action.SEND/>
          <category android:name="android.intent.category.DEFAULT/>

           <data android:mimeType="text/plain/>
  </intent-filter>
</activity>

 

 

1.action 的匹配规则

  action是一个字符串,系统预定了一些action,同时我们也可以在应用中定义自己的Action.action的匹配规则是Intent中的action必须能够和过滤规则中的Action匹配,也就是字符串完全一样(区分大小写哦~)。一个过滤规则可以有多个action,只要Intent匹配过滤规则中的任何一个都算匹配成功。如果Intent没有指定action,那么匹配失败。

2.category 的匹配规则

   Category是一个字符串,系统预定义了一些category,同时我们也可以定义自己的categorycategory的匹配规则和action不同,他要求Intent中如果含有category,那么所有的category都必须和过滤规则中的一个category相同。也就是如果Intent中定义了category,那这些category都必须在过滤规则中存在。当然,Intent可以没有category,如果没有的话,仍然是可以成功的,这里和Action不同。

当我们不设置category时,系统在用startActivity的时候会默认为Intent加上“android.intent.category.DEFAULT”这个category,所以就能匹配成功(为了使activity能够接收隐式调用,就必须在Intent-filter中指定默认的category).

3.data 的匹配规则

Data的匹配规则和action类似,如果过滤规则中定义了data,那么Intent中必须也要定义可以匹配的data.

data的语法如下:

<data android:scheme="string"
    android:host="string"
    android:port="string"
    android:path="string"
    android:pathPattern="string"
    android:pathPrefix="string"
    android:mimeType="string"
    />

 

Data由两部分组成,mimeTypeURI.mimeType指媒体类型,比如image/jpegaudio/mpeg4-generic video/*等,可以表示图片,文本,视频等不同的媒体格式,而URI中包含的数据就比较多了,下面是URI的结构:

<scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]

  比如 

content://com.example.project:200/folder/sub/etc,

http://www.baidu.com:80/search/info

其中 scheme:URI模式,比如http,content,file等,如果uri中没有指定scheme,那么uri无效。

Host,Port就不讲了。

Path,pathPatternpathPrefix:这三个参数表述路径的详细信息,其中path表示完整的路径信息,pathPattern也表示完整的路径信息,但是它里面可以包含通配符”*”,”*”表示0或者任意多个字符,需要注意的是,由于正则表达式的规范,如果想表示真实的字符串,那么“*”要写成“\\*,\”要写成‘\\\\’;pathPrefix表示路径前缀信息.

 

Data的过滤规则和Action类似,也要求只要出现在intent中的data都出现在了过滤规则的data里。

另外,URI是有默认值的,为contentfile。也即是说,如果没有指定URI,那么只能content或者file才能匹配成功。针对下面的例子,我们可以:intent.setDataAndTypeUri.parse(“file:://abc”),”image/png”).而不能单独调用setData 或者setType,它们会相互清除。

 <intent-filter>
       <data 
        android:mimeType="image/*"
        />
</intent-filter>

 稍微和action不同的是

<data android:scheme="string"
    android:host="string"
    />

等价于

<data android:scheme="string"
        />

<data android:host="string"
    />

当我们采用隐式的方式去调用Activity时,要做一下判断,是否调用成功。判断方法有两种:采用PackageManager的resolveActivity方法或者Intent的resolveActivity方法,如果它们找不到Activity,就会返回null.另外,PackageManager还提供了queryIntentActivities方法,这个方法和resolveActivity不同的是:它不是返回最佳匹配的Activity信息而是返回所有匹配成功Activity信息。

 

四 Activity的工作过程

为了方便日常开发,系统对四大组件的工作过程进行了很大程度的封装,这使得开发者无需关注实现细节即可快速的使用四大组件。在日常卡覅中,我们不需要了解系统的底层工作原理,但如果想要成为高级Android开发工程师的话,就必须了解系统的底层工作原理。

下面我们对Activity的整体工作流程进行讲解,目的是让我们对四大组件的工作过程有一个感性的认识,并能够给予上层开发一些指导意义。当然,如果你从事的是Android Rom开发的话,那么底层代码还是要有所涉猎的。

 

下面我们分析Activity的启动流程。从当我们在程序中调用startActivity开始分析。StartActivity有好几种重载方式,但他们最终会调用startActivityForResult方法,它的实现如下:

 

在上面的代码中,我们只要关注mParent == null 这部分逻辑即可。mParent代表的是ActivityGroup,ActivityGroup最开始用来在一个界面上嵌入多个子Activity,但是其在API13中已经废弃了,所以系统采用Fragment来代替ActivityGroup。在上面的代码中需要注意的是mMainThread.getApplicationThread()这个参数,它的类型是ApplicationThread,ApplicationThreadActivityThread的一个内部类,最后通过分析可以发现,ApplicationThreadActivityThreadActivity启动过程中发挥着重要作用。接着看一下InstrumentationexecStartActivity方法,如下:

 

从上面的可以看出,启动Activity真正的实现由ActivityManagerNative.getDefault()startActivity来完成。ActivityManagerServiceAMS)继承自ActivityManagerNative,ActivityManagerNative继承自Binder并实现了IActivityManager这个Binder接口,因此AMS也是一个Binder,它是IActivityManager的具体实现。由于ActivityManagerNative.getDefault()其实是一个IActivityManager类型的binder对象,因此它的具体实现是AMS.可以发现,在ActivityManagerNative中,AMS这个Binder对象采用单例模式对外提供,Singleton是一个单例模式的封装类,第一次调用它的get方法时会通过create方法来初始化AMS这个Binder对象。后续的调用中则直接返回之前创建的对象。//TODO

 

 

 

以前看别人写的博客时,挺愉快的,而当自己开始写博客时,真的蛮累的,坚持下去!下篇文章讲其他三大组件。

 

  

     

   

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值