Android

Android四大组件之Activity

2018-12-28  本文已影响16人  MoonJoy

一. Activity的生命周期

1. 正常情况

Activity生命周期

2. 异常情况

当系统配置发生改变后,Activity会被重建,系统配置中有很多内容,当某项内容发生改变后,可以通过给Activity指定configChanges属性,不让系统重建Activity。具体属性如下:

如:android:configChangeds="orientation | keyboardHidden | screenSize" 添加在AndroidManifest.xml的Activity的声明里。Activity不会被重建,也没有调用onSaveInstanceState和onRestoreInstanceState方法,系统调用了Activity的onConfigurationChanged方法。

二. Activity的启动方式

默认情况下,当我们多次启动同一个activity时,系统会创建多个实例并一一放入任务栈中。任务栈的工作原理是后进先出。

什么是任务栈:

  1. task是存在于framework层的一个概念,用于控制界面的跳转和返回。这个task存在于一个称为back stack的数据结构中,所以framework是以栈的形式管理用户开启的activity。

  2. 当app中存在A,B,C三个Activity,点击图标启动主Activity A,接着A启动B,B启动C,这是栈中有三个Activity,并且这三个Activity默认在同一个task中,当一直按返回键时,C,B,A相继被弹出,task被移除。

  3. task是可以跨应用的,也可以跨进程。有的Activity虽然不在同一个app中,但为了保持用户操作的连贯性,可以把它们放在同一个task中。比如某个应用中Activity A点击发送短信,会启动短信app的一个Activity B来发送邮件,此时AB是在同一个task中的, 当发送完短信后,按back键返回,可以返回到Activity A中。

  4. 任务栈分为前台和后台任务栈,后台任务栈中的Activity处于暂停状态,用户可以通过切换讲后台任务栈再次调到前台。

TaskAffinity:

可以理解为任务相关性,通过设置该属性可以标识一个Activity所需要的任务栈名字。

  1. 当Activity以FLAG_ACTIVITY_NEW_TASK标志启动时,它会被分配到相同的TaskAffinity的任务栈中。

  2. 默认情况下,所有Activity所需的任务栈名字应为应用的包名。也可以为每个Activity单独指定TaskAffinity属性,这个属性值不能和包名相同。

  3. 为一个activity的taskAffinity设置一个空字符串,表明这个activity不属于任何task。

  4. 假如Activity A未设置taskAffinity,在Activity A中以singleTask方式启动Activity B,但Activity B未设置不同的taskAffinity,Activity A跟Activity B会在同一个任务栈中。因为Activity B所需的任务栈已经在启动Activity A时创建,所以不需要重新创建。

  5. 当跟allowTaskReparenting一起使用时,会产生特殊的效果。当应用A启动了应用B的某个Activity后,如果这个activity的allowTaskReparenting设为true,当应用B启动后,该activity会直接从应用A的任务栈转移到应用B的任务栈中。举个例子,在电子邮件中打开一个web网页,按home键回到主页界面后,再打开浏览器,就能看到之前的web网页。
    allowTaskReparenting的主要作用是activity的迁移,即从一个task迁移到另一个task,这个迁移跟activity的taskAffinity有关,必须是从一个跟该activity taskAffinity不同的task中迁移到跟它taskAffinity相同的task中,该属性在AndroidManifest中设置

四种启动模式(launchMode)

还有一种情况,假设现在有2个任务栈,前台任务栈的情况为AB,后台任务栈的情况为CD,且CD的启动模式均为singleTask。现在请求启动D,则整个后台任务栈都会被切换到前台,此时整个后退列表变成了ABCD,当用户按back键时,ABCD会依次出栈,假如请求启动C,则整个后退列表变成ABC。

Flags

三. Activity的调用方式

Activity的启动分为显式调用和隐式调用两种。

显式调用
直接指定需要打开的activity对应的类,明确指定被启动对象的组件信息,包括包名和类名。

Intent intent = new Intent(MainActivity.this,SecondActivity.class);
startActivity(intent);
ComponentName componentName = new ComponentName(this, SecondActivity.class); 
Intent intent = new Intent();
intent.setComponent(componentName); 
startActivity(intent);
Intent intent = new Intent();
intent.setClass(this, SecondActivity.class); 
startActivity(intent);

隐式调用
需要Intent能匹配目标组件的IntentFilter中所设置的过滤信息,如果不匹配将无法启动目标Activity,会抛出ActivityNotFoundException,可以使用try catch,当然最好的提前判断,可以使用PackageManager的resolveActivity方法或Intent的resolveActivity方法,如果找不到匹配的Activity就会返回null,通过判断返回值就可以避免异常的出现。

Intent intent = new Intent(Intent.ACTION_XXX);
ComponentName componentName = intent.resolveActivity(getPackageManager());
if(componentName != null) {
  startActivity(intent);
}

只有一个Intent同时匹配action(动作)、category(类别)、data(数据)才算完全匹配。

一个Activity中可以有多个intent-filter,一个intent只要能匹配任一组intent-filter就能启动对应的Activity。

  1. intent中action必须能够和过滤规则中的任一个action相同(字符串值一样,区分大小写)即匹配成功。

  2. 若intent中缺少action,则不会匹配。

  1. intent中如果有category,那么所有的category必须和过滤规则中的其中一个category相同。

  2. 若intent中没有category,intent也可能匹配成功。因为系统在调用startActivity或startActivityForResult时,会默认为intent加上“android.intent.category.DEFAULT”,此时必须在intent-filter中指定category为“android.intent.category.DEFAULT”

  1. 如果intent-filter中只有mimeType,那intent中的mimeType必须一致才能匹配。虽然过滤规则没有指定URI,但URI的默认值为content和file,所以intent中URI部分的scheme必须为content或file才能匹配。

  2. 如果intent-filter有两组data规则,那只要intent的data满足其中某一组就可以匹配成功。

注:为intent指定完整的data时,必须调用setDataAndType方法,不能先调用setData再调用setType,反之也不行,因为这个两个方法会相互消除对方的值。

内容参考:《Android开发艺术探索》

上一篇 下一篇

猜你喜欢

热点阅读