(四)Activity与Activity调用栈

本文为Android的Activity相关知识整理,具体参考了

  • 《Android开发艺术探索》第一章
  • 《Android群英传》第八章
  • 《第一行代码》第二章

Activity是与用户交互的第一接口。

  1. Activity生命周期

Activity具有多种形态。其生命周期图如下:


Paste_Image.png

其中,有三个稳定态,其他都是过渡态:

  • Resumed ,此时,Activity处于栈顶,处理用户交互。
  • Paused,当Activity的一部分被挡住的时候进入这个状态,不会与用户交互。
  • Stopped,当Activity完全被覆盖时进入这个状态,此时Activity不可见,仅在后台运行。

1.1 Activity经典生命周期

(以下均指MainActivity的生命周期)

  • Activity启动,点开一个应用展示MainActivity
    • onCreate()->onStart()->onResume() 到达Resumed状态,此时MainActivity被打开。
  • Activity暂停与恢复,1:打开了一个半屏幕的dialog,2:按返回键返回MainActivity
    1. onPause()到达Paused状态,此时dialog被打开。
    2. onResume()到达Resumed状态,此时dialog被关闭,显示MainActivity
  • Activity停止与恢复,1:打开另一个SecondActivity,2:按返回键返回MainActivity
    1. onPause()->onStop()到Stopped状态,此时SecondActivity被打开。
    2. onRestart()->onStart()->onResume()到Resumed状态,此时SecondActivity被关闭,重新回到MainActivity。
  • Activity销毁,在MainActivity按返回键
  • onPause()->onStop()->onDestory(),此时已经销毁 MainActivity。

注意:

  • 在onPause()中一定要释放使用的系统资源,比如Camera,sensor,receivers!!
  • 在onStop()中执行更大、更多CPU密集的关闭操作。比如写入信息到数据库。
  • 当系统长期处于onStopped状态而且此时系统内存紧张时,系统会回收此Activity,而此时,系统会通过onSaveInstanceState()方法将Activity状态保存到Bundle对象中(finish()方法销毁时不会保存)。当重新创建此Activity时,保存的Bundle对象会传递到onRestoreInstanceState()与onCreate()中。
  • onCreate和onDestroy是配对的,分别标识着Activity的创建和销毁; onStart和onStop是配对的,标识着Activity是否可见;onResume和onPause是配对的,标识着Activity是否在前台
  • 假设当前Activity为MainAcitivty ,这时用户启动SecondActivity ,那么MainAcitivty 的onPause先执行,SecondActivity 的onResume后执行。Android规定,不能在onPause中做重量级操作,就是基于这里。我们应该尽量在onStop中做操作,使得新Activity尽快显示
  • MainAcitivty->onPause
  • SecondActivity->onCreate
  • SecondActivity->onStart
  • SecondActivity->onResume
  • MainAcitivty->onStop

1.2 异常生命周期

以下几种情况下,Activity的生命周期会发生异常
1,资源相关的系统配置发生改变
比如,旋转屏幕,在默认状态下,Activity就会被销毁并且重新创建。

  • 首先,Activity就会被销毁,onPause(), onStop(),onDestory()均会被调用,同时由于Activity是在异常状态下终止的,系统会在onStop()之前,调用onSaveInstanceState()来保存当前Activity的状态。
  • 然后,Activity被重新创建,系统会在onStart()之后,调用onRestoreInstanceState(),并且把之前保存的Bundle对象传递给onRestoreInstanceState()和onCreate()方法。
  • PS: 这两个方法都能对Bundle数据进行处理,但是一般用 onRestoreInstanceState()。因为onRestoreInstanceState()一旦被调用,其参数 Bundle savedInstanceState 一定是有值的,我们不必判断其是否为空。
@Override
protected void onCreate(Bundle savedInstanceState){
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  if(savedInstanceState != null){
       String test = savedInstanceState.getString("extre_test");
       Log.d(TAG, test);
   }
 }

@Override
protected void onSaveInstanceState(Bundle outState){
  super.onSaveInstanceState(outState);
  outState.putString("extre_test", "test");
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState){
  super.onRestoreInstanceState(savedInstanceState);
  String test = savedInstanceState.getString("extre_test");
  Log.d(TAG, test);
}

执行顺序:
1 onPause()
2 onSaveInstanceState(Bundle outState)
3 onStop()
4 onDestory()
5 onCreate(Bundle savedInstanceState)
6 onStart()
7 onRestoreInstanceState(Bundle savedInstanceState)

注意:系统仅仅在Activity异常终止时才会调用onRestoreInstanceState()。

2,系统内存不足
此时的系统中数据的存储和恢复情况和上面第一种情况一致。Activity优先级如下:

1 前台Activity
2 可见但非前台Activity
3 后台Activity
当系统资源不足的时候,会按照这个优先级使用onSaveInstanceState()和onRestoreInstanceState()来存储和恢复数据。

还有一些后台进程不是四大组件,这样就会很容易被杀死~~ 一般是将这些后台工作放入Service中,从而保证有一定的优先级。

PS:阻止Activity被重新创建

使用android:configChanges="orientation|screenSize"属性。

常用属性:

  • locale:设备的本地位置发生变化,一般指切换了系统语言。
  • orientation:屏幕方向发生变化
  • screenSize:屏幕大小发生变化,当旋转屏幕的时候,屏幕尺寸会变!!!!这个比较特殊,当minSdkVersion和targetSdkVersion均低于13时,此选项不会导致Activity重启,否则会导致Activity重启。
  • keyboardHidden:键盘的可访问性发生变化,比如调出键盘。
 <activity
    android:name=".MainActivity"
    android:label="@string/app_name"
    android:configChanges="orientation|screenSize|keyboardHidden" >
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

@Override
public void onConfigurationChanged(Configuration newConfig){
    super.onConfigurationChanged(newConfig);
    ......//当系统配置发上变换时,进行的工作
}
  1. Android任务栈

Android的APP通常会有多个Activity,各个Activity之间通过Intent进行连接,系统通过栈结构来保存整个APP的Activity。系统有两种方式控制Activity的启动模式。

  • AndroidMainifest的launchMode
  • Intent Flag(优先级更高)

2.1AndroidMainifest启动模式

在AndroidMainifest.xml文件里面的activity标签设置启动模式。

<activity
    android:name=".FirstActivity"
    android:launchMode="singleTop"
    android:label="This is FirstActivity" >
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
  • standard,标准模式,每次都会创建新的Activity覆盖在原Activity上
    Paste_Image.png
  • singleTop,栈顶复用模式,首先判断栈顶Activity是否是要启动的Activity,如果是则不创建新的Activity而直接引用这个Activity;如果不是则创建新的Activity。
Paste_Image.png
  • singleTask,栈内复用模式,检测整个Activity栈中是否存在当前需要启动的Activity,如果存在则将该Activity置于栈顶,并销毁其上所有Activity。
Paste_Image.png
  • singleInstance,单实例模式,创建新的任务栈,且该任务栈仅有一个Activity。
Paste_Image.png

TaskAffinity
taskAffinity,任务相关性。xml中的一个属性,标识了一个Activity所需要的任务栈的名字。默认是包名。如果设置了其他的名字如com.test.task1,那启动它的时候就会新建一个名为com.test.task1的任务栈。

 <activity
    android:name="com.test.task0.MainActivity"
    android:label="@string/app_name"
    android:launchMode="standard">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category andorid:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

<activity
    andorid:name="com.test.SecondActivity"
    android:taskAffinity="com.test.task1"
    android:label="@string/app_name"
    android:launchMode="singleTask"/>

<activity
    andorid:name="com.test.ThirdActivity"
    android:taskAffinity="com.test.task1"
    android:label="@string/app_name"
    android:launchMode="singleTask"/>

如果从MainActivity启动SecondActivity,然后再启动ThirdActivity,那么任务栈如下:
com.test.task0 MainActivity
com.test.task1 SecondActivity ThirdActivity
若再从ThirdActivity启动MainActivity,那么任务栈如下:
com.test.task0 MainActivity
com.test.task1 SecondActivity ThirdActivity MainActivity

2.2 Intent Flag启动模式

对Intent进行设置

 Intent intent = new Intent();
 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  

重要的Flag:

  • FLAG_ACTIVITY_NEW_TASK,启动的Activity在新的Task中,相当于android:launchMode="newTask"
  • FLAG_ACTIVITY_SINGLE_TOP,相当于android:launchMode="singleTop"
  • FLAG_ACTIVITY_CLEAR_TOP,相当于android:launchMode="singleTask"
  • FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS,当以此种模式启动A,A再启动B时,A会被销毁。等同于android:excludeFromeRecents="true"

3 IntentFilter的匹配规则

  <activity android:name="SecondActivity">
      <intent-filter>
          <action android:name="android.intent.action.SEND">
          <category android:name="android.intent.category.DEFAULT">
          <data android:mimeType="text/plain">
      </intent-filter>
    </activity>

调用一个Activity主要包括两种:
1,显式调用。

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

2, 隐式调用

Intent intent = new Intent(Intent.ACTION_SEND);
intent.setDataAndType(Uri.parse("file://abc"), "text/plain"); 
startActivity(intent);

隐式调用需要Intent能够匹配目标组件的IntentFilter中的过滤信息。
IntentFilter中的过滤信息有action, category, data。
只有一个Intent同时匹配这三个类别才能启动目标Activity。

  • action的匹配要求,Intent中的action存在且必须和过滤规则中的其中一个action相同。
  • data的匹配要求,Intent中的data存在且必须和过滤规则中的其中一个data相同。
  • category匹配要求,如果含有category,那么所有的category都必须和过滤规则中的其中一个category相同。也就是说它的category可以没有!!!!!(原因是默认的category是android.intent.category.DEFAULT,不过得在activity标签的intent-filter中加入android.intent.category.DEFAULT这个category)
  1. 退出程序的两种方法

4.1 利用Activity的singleTask模式退出

  1. 将主Activity设置为singleTask模式

    •  <activity
           android:name=".MainActivity"
           android:launchMode="singleTask"
           ...
      
  • 在要退出的Activity中转到主Activity中,从而将主Activity之上的Activity都清除
    • Intent intent = new Intent(this, MainActivity.class);
      startActivity(intent); 
      
  • 然后重写主Activity的onNewIntent()方法,在方法中加上finish(),从而销毁最后一个Activity。
    • @Override
      protected void onNewIntent(Intent intent) {
            super.onNewIntent(intent);
            finish();
      }
      

4.2 利用专门的集合类对所有的活动进行管理

  1. 创建一个ActivityCollector 类作为活动管理器。List<Activity>作为存放活动的列表。

    • public class ActivityCollector {
        public static List<Activity> activities = new ArrayList<Activity>();
        public static void addActivity(Activity activity) {
          activities.add(activity);
        }
      
        public static void removeActivity(Activity activity) {
            activities.remove(activity);
        }
      
        public static void finishAll() {
            for (Activity activity : activities) {
                if (!activity.isFinishing()) {
                    activity.finish();
                }
            }
        }
      }
      
  2. 创建一个继承自Activity的BaseActivity 。ActivityCollector里存放的活动要随着onCreate而添加,随着onDestory而销毁。

    • public class BaseActivity extends Activity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            Log.d("BaseActivity", getClass().getSimpleName());
            ActivityCollector.addActivity(this);
        }
        @Override
        protected void onDestroy() {
            super.onDestroy();
            ActivityCollector.removeActivity(this);
        }
      }
      
  3. 全部的Activity都要继承自BaseActivity,此时,只要调用 ActivityCollector.finishAll();,就可以销毁所有Activity,关掉程序。

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

推荐阅读更多精彩内容

  • 【 Android四大组件之一 主要用于与用户进行交互,在一个App中可能存在零个或多个Activity 】 1....
    征程_Journey阅读 1,935评论 0 4
  • Activity 一、四种形态 运行状态: 当 Activity 处于栈的顶层,可见,并可与用户进行交互 onRe...
    任教主来也阅读 1,622评论 1 10
  • 今年中秋,月儿主角失约了。 她只管躲在月宫,清辉满人间便成了人们的奢想。倒是凉凉秋雨殷勤得很,一连两三天缠绵于人间...
    滋小然阅读 806评论 35 27
  • 文/ Kurny 这是古老得不能再古老的列车 它长长的长着犄角的头部 不知何时起,已没有了生气 零零散散布了些红锈...
    Kurny91阅读 320评论 2 0
  • “彼岸花” 黄泉彼岸 生生相惜 世世相错 “三生石 ” 忘川三生石 望不穿三生 三生石上却早已一定 “奈何桥” 奈...
    大志志大xcz阅读 263评论 0 0