全面了解Activity

Image.png

大家好,我叫石头

Activity是什么?

相信大家都知道Android中的4大组件(Activity活动,Service服务,ContentProvider内容提供者,BroadcastReceiver广播接收器),Activity是我们使用最多的也是最基本的组件,Activity提供窗口和用户进行交互.

Android中的activity全都归属于task管理(task是一个具有栈结构的容器),task是多个activity的集合,android默认情况会为每个App维持一个task来存放app的所有activity(当然这只是默认情况),task的默认name为该apppackagename(包名)

当然我们也可以在AndroidMainfest.xml中申明activity taskAffinity 属性来自定义task,但是不建议使用,因为如果其他app也申明了同样的task,那个app可能启动到你的activity,这样会带来各种安全问题(比如拿到你的Intent).

Activity的内部调用

上面我们介绍过了,系统是通过task(栈)的方式来管理activity的,当一个新的activity开始的时候,该activity会被放置在堆栈(back stack)的顶部,成为正在运行的活动,之前的activity始终保持低于它在堆栈,而不会出现在前台.

官方activity_lifecycle:

activity

当我们打开一个新的activity实例的时候,系统会以此调用

onCreate() -> onStart() -> onResume() 然后开始running

activity在running的时候如果被覆盖(打开新的activity,或者被锁屏,但是它依然在前台运行,lost focus but is still visible),系统就会调用onPause();

onpause()方法中,我们通常会提交未保存的更改到持久化数据,停止动画和其他东西.但是我们要知道现在这个activity还是完全活着(它保存所有的状态和成员信息,并且保存到窗口管理器的连接)

  • 1.启动Activity:系统会先调用onCreate方法,然后调用onStart方法,最后调用onResumeActivity进入运行状态。
  • 2.当前Activity被其他Activity覆盖其上或被锁屏:系统会调用onPause方法,暂停当前Activity的执行。
  • 3.当前Activity由被覆盖状态回到前台或解锁屏:系统会调用onResume方法,再次进入运行状态。
  • 4.当前Activity转到新的Activity界面或按Home键回到主屏,自身退居后台:系统会先调用onPause方法,然后调用onStop方法,进入停滞状态。
  • 5.用户后退回到此Activity:系统会先调用onRestart方法,然后调用onStart方法,最后调用onResume方法,再次进入运行状态。
  • 6.当前Activity处于被覆盖状态或者后台不可见状态,即第2步和第4步,系统内存不足,杀死当前Activity,而后用户退回当前Activity:再次调用onCreate方法、onStart方法、onResume方法,进入运行状态。
  • 7.用户退出当前Activity:系统先调用onPause方法,然后调用onStop方法,最后调用onDestory方法,结束当前Activity

onSaveInstanceState:(1)在Activity被覆盖或退居后台之后,系统资源不足将其杀死,此方法会被调用;(2)在用户改变屏幕方向时,此方法会被调用;(3)在当前Activity跳转到其他Activity或者按Home键回到主屏,自身退居后台时,此方法会被调用。第一种情况我们无法保证什么时候发生,系统根据资源紧张程度去调度;第二种是屏幕翻转方向时,系统先销毁当前的Activity,然后再重建一个新的,调用此方法时,我们可以保存一些临时数据;第三种情况系统调用此方法是为了保存当前窗口各个View组件的状态。onSaveInstanceState的调用顺序是在onPause之前。

onRestoreInstanceState:(1)在Activity被覆盖或退居后台之后,系统资源不足将其杀死,然后用户又回到了此Activity,此方法会被调用;(2)在用户改变屏幕方向时,重建的过程中,此方法会被调用。我们可以重写此方法,以便可以恢复一些临时数据。onRestoreInstanceState的调用顺序是在onStart之后。

activity被回收的状态和信息保存和恢复过程

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        if(savedInstanceState!=null){ //判断是否有以前的保存状态信息
             savedInstanceState.get("Key"); 
             }
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
   @Override
protected void onSaveInstanceState(Bundle outState) {
    // TODO Auto-generated method stub
     //可能被回收内存前保存状态和信息,
       Bundle data = new Bundle(); 
       data.putString("key", "last words before be kill");
       outState.putAll(data);
    super.onSaveInstanceState(outState);
}
   @Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    // TODO Auto-generated method stub
       if(savedInstanceState!=null){ //判断是否有以前的保存状态信息
             savedInstanceState.get("Key"); 
             }
    super.onRestoreInstanceState(savedInstanceState);
}
} 

onSaveInstanceState()方法,在activity被回收之前调用,用来保存自己的状态信息,以便回收后重建时恢复数据(在onCreate()onRestoreInstanceState()中恢复).旋转屏幕重建activity会调用该方法,但其他情况在onRause()onStop()状态的activity不一定会调用,
下面是官方文档说明:

  One example of when onPause and onStop is called and not this method is when a user navigates back from activity B to activity A: there is no need to call onSaveInstanceState on B because that particular instance will never be restored, so the system avoids calling it. An example when onPause is called and not onSaveInstanceState is when activity B is launched in front of activity A: the system may avoid calling onSaveInstanceState on activity A if it isn't killed during the lifetime of B since the state of the user interface of A will stay intact. 

也就是说,系统灵活的来决定会不会调用该方法,但是如果要调用就一定发生在onStop方法之前,但不保证发生在onPase的前面还是后面.

onRestoreInstanceState()该方法在onstartonPostCreate之间调用,当然我们也可以在onCreat中恢复之前我们在onSaveInstanceState()中保存下来的数据,但是我们有时候需要在初始化布局完成之后再恢复数据,这是就可以在onRestoreInstanceState()中恢复数据.
onPostCreate():一般我们都没有实现这个方法,它的作用是在代码开始运行之前,调用系统做最后的初始化工作.

关于启动模式

启动模式是什么?

简单点说就是:定义activity实例与task的关联方式

为什么我们要定义启动模式呢?

为了实现默认启动(standard)模式之外的需求:

  • 让某个activity启动一个新的task(而不是被放入当前task)
  • 让activity启动时只调用已有的某个实例(而不是在back stack(之前我们有提到过哟) 顶创建一个新的实例)
  • 当用户离开task时只想保留根activity,而back stack中的其他activity都要清空.

怎样定义启动模式

定义启动模式的方法有2种:
  • 使用manifest文件定义
  • 使用Intent标志定义

使用manifest文件定义启动模式:
在 manifest 文件中activity声明时,利用 activity 元素的 launchMode 属性来设定 activity 与 task 的关系。

<activity
            ......
            android:launchMode="standard"
             >
           .......
</activity>

注意: 你用 launchMode 属性为 activity 设置的模式可以被启动 activity 的 intent 标志所覆盖,代码的优先级最高。

现在我们知道了怎么定义启动模式了,但是有哪些启动模式呢?

  • standard (默认模式)
    当通过这种模式启动activity时,Android总会为目标 Activity创建一个新的实例(之前有过也会重新创建),并将该Activity添加到当前Task栈中。这种方式不会启动新的Task,只是将新的 Activity添加到原有的Task中。
  • singleTop
    该模式和standard模式基本一致,但有一点不同:当将要被启动的Activity已经位于Task栈顶时,系统不会重新创建目标Activity实例,而是直接复用Task栈顶的Activity。
  • singleTask
    Activity在同一个Task内只有一个实例。
    如果将要启动的Activity不存在,那么系统将会创建该实例,并将其加入Task栈顶;

如果将要启动的Activity已存在,且存在栈顶,直接复用Task栈顶的Activity。

如果Activity存在但是没有位于栈顶,那么此时系统会把位于该Activity上面的所有其他Activity全部移出Task,从而使得该目标Activity位于栈顶。

  • singleInstance
     无论从哪个Task中启动目标Activity,只会创建一个目标Activity实例且会用一个全新的Task栈来装载该Activity实例(全局单例).

如果将要启动的Activity不存在,那么系统将会先创建一个全新的Task,再创建目标Activity实例并将该Activity实例放入此全新的Task中。

如果将要启动的Activity已存在,那么无论它位于哪个应用程序,哪个Task中;系统都会把该Activity所在的Task转到前台,从而使该Activity显示出来。

和"singleTask"类似,唯一不同的是系统不会在这个activity的实例所在的task中启动任何其他activity。

这个activity的实例永远是这个task中的唯一一个成员,这个activity启动的任何其他activity都将在另外的task中打开。


使用Intent标识定义启动模式:

  • FLAG_ACTIVITY_NEW_TASK
    和之前讨论过的"singleTask"相同,在新的task中启动activity,如果一个你需要的activity的task已经存在,则将它推向前台,恢复其上一个状态,它通过onNewIntent()收到这个新的intent。

  • FLAG_ACTIVITY_SINGLE_TOP
    和"singleTop"行为相同,如果被启动的activity是当前顶部的activity,则已经存在的实例收到 onNewIntent()
    ,而不是新建实例。

  • FLAG_ACTIVITY_CLEAR_TOP
    如果被启动的activity已经在当前task运行,不创建它的新实例,而是销毁在它之上的其他所有activities,然后通过 onNewIntent()
    传递一个新的intent给这个恢复了的activity。
      这个行为在 launchMode
    中没有对应的属性值。
      注意,如果activity的启动模式是"standard",它自己也将被移除,然后一个新的实例将被启动。
      这是因为当启动模式是"standard"时,为了接收新的intent必须创建新的实例。


处理affinities

Affinity指示了activity更倾向于属于哪个task。
  默认情况下,同一个应用的activities倾向于在同一个task中。你可以通过<activity>标签中的 taskAffinity
来修改这种行为。
  详细内容请查看:API Guides: Tasks and Back Stack
  http://developer.android.com/guide/components/tasks-and-back-stack.html


开启一个task

你可以通过给activity一个intent filter(action是"android.intent.action.MAIN",category是"android.intent.category.LAUNCHER"),让这个activity是一个task的进入点。

如下:

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

一个这样的intent filter会使得这个activity的icon和label显示在程序启动处,提供了一种方法,使得用户可以启动这个activity,当它启动后,用户也可以通过它来返回到这个task。
  第二个能力是很重要的:用户必须能够离开一个task,然后通过activity launcher返回到它。
  因为这个原因,两个让activity永远实例化一个task的启动模式:"singleTask" 和"singleInstance",应该仅在activity有一个 ACTION_MAINCATEGORY_LAUNCHER filter的时候用它们。

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

推荐阅读更多精彩内容