0%

深入了解Intent

概述

  Intent是一个动作的完整描述,包含了产生组件、接收组件和传递数据信息。并且,Intent利用消息实现应用程序之间的交互机制,这种消息描述了应用中一次操作的动作、数据以及附加数据,系统通过该Intent的描述负责找到对应的组件,并将Intent传递给调用的组件,完成组件的调用。

Intent属性

  Intent由动作、数据、分类、类型、组件和扩展信息等内容组成,每个组成都由相应的属性进行表示,并提供设置和获取相应属性的方法。如下图所示:

image-20200423165652289

Action属性

  Action属性用于描述Intent要完成的动作,对要执行的动作进行简单的描述。Intent类定义了一系列Action属性常量,用来标识一套标准动作,如ACTION_CALL(打电话)、ACTION_EDIT(编辑)等。

  注意:action只是一个普通的字符串,代表Intent要完成的一个抽象“动作”,而具体由哪个组件来完成,Intent并不负责!就是仅仅知道会有这个动作,谁来完成这个动作那就要交给Intent-filter来进行筛选了!根据我的理解,这个Intent应该就是起到一个中介的作用吧!

以下是一些action属性常量:

image-20200423165910011

Data/Type属性

  Data属性是执行动作的URI和MIME类型。通常用来向Action属性提供操作的数据,接受一个URI对象,URI的格式:scheme://host:post/path 参数依次为:协议头、主机、端口、路径;

  Type通常用于指定Data所指定的Uri对应的MIME类型,比如能够显示图片数据的组件不应该用来播放音频文件,可以是自定义的MIME类型,只要符合abc/xyz格式的字符串就可以了。

Data常量如下:

image-20200423170056495

Action和Data匹配使用,不同的Action由不同的Data数据指定

image-20200423170147995

Category属性

Category属性指明一个执行Action的分类。

**注意:同样是普通的字符串,Category用于为Action提供额外的附加类别信息,两者通常结合使用,一个Intent对象只能有一个Action,但是能有多个Category。**

image-20200423170350296

Component属性

  该属性用于指明Intent目标组件的类名称。通常Android会根据Intent包含的其他属性的信息,比如Action、 Data/Type、 Category 进行查找,最终找到一个与之匹配的目标组件。但是,如果指定了Component这个属性,Intent 则会直接根据组件名查找到相应的组件,而不再执行上述查找过程。指定Component属性后, Intent的其他属性都是可选的。

Extra属性

  该属性用于添加附加信息,通常用于多个Action之间的数据交换,Extras属性是一个Bundle对象,通过键值对进行数据存储。下面介绍如何利用Intent在activity之间传递数据部分会详细介绍并举例实现。

Flag属性

表示不同来源的标记,flag可以指导系统以何种方式启动一个activity、是否将启动的activity放在该应用的任务栈中等等。常用flags如下:

FLAG_ACTIVITY_NEW_TASK:设置这个标记位,是为 Activity 指定 “singleTask” 启动模式,它的作用和在清单文件中指定该启动模式的效果一样。

FLAG_ACTIVITY_SINGLE_TOP:设置这个标记位,是为 Activity 指定 “singleTop” 启动模式,它的作用和在清单文件中指定该启动模式的效果一样。

FLAG_ACTIVITY_CLEAR_TOP:具有此标记位的 Activity,在它启动时,在同一个任务栈中所有位于它上面的 Activity 都要出栈。

FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:具有这个标记的 Activity 不会出现在历史 Activity 的列表中。它等同于在清单文件中指定 Activity 的属性android:excludeFromRecents=“true”

Intent的类别

  根据Intent寻找目标组件时所采用的方式不同,可以将Intent分为两类:显示Intent和隐式Intent。

显示Intent

  显示Intent 通过直接指定组件来实现,常用方法有setComponent(、 setClassName0或setClass(),如下示例:

1
2
3
4
5
6
findViewById(R.id.open).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this,UpdateUi.class));
}
});

注意:显式Intent直接用组件的名称定义目标组件,这种方式很直接。但是由于开发人员往往并不清楚别的应用程序的组件名称,因此,显式Intent更多用于在应用程序内部传递消息。比如在某应用程序内,一个Activity启动一个Service。

隐式Intent

  隐式Intent,这种方式通过Intent Filter过滤实现,过滤时通常根据Action、Data和Category属性进行匹配查找。Android提供了两种生成Intent Filter的方式: 一种是通过IntentFilter类生成;另一种通过在配置文件AndroidManifest.xml中定义元素生成。在AndroidManifest.xml配置文件中,Intent Filter以元素来指定。一个组件中可以有多个元素,每个元素描述不同的能力。

  注意:它更广泛地用于在不同应用程序之间传递消息,由于没有明确的目标组件名称,所以必须由Android系统帮助应用程序寻找与Intent请求意图最匹配的组件。

一个通过隐式Intent启动不同应用程序的activity的例子:

  • 首先要在一个应用程序中先设置好action

image-20200423173522234

  • 然后在另一个应用程序里通过完整的action的name来启动。

image-20200423173548963

  • 启动时要将两个应用程序都打开。

image-20200423173706410

image-20200423173712997

  • 在另一个activity上禁止启动的属性android:exported=”false”,则可以拒绝启动,并抛出异常,这时候可以捕获异常并弹出提示。

利用Intent在Activity之间传递数据

Intent传递简单数据

  可以直接通过调用Intent的putExtra()方法存入数据,然后在通过getIntent()获得Intent后调用getXxxExtra获得 对应类型的数据;传递多个数据的话,可以使用Bundle对象作为容器,通过调用Bundle的putXxx先将数据存储到Bundle中,然后调用Intent的putExtras()方法将Bundle存入Intent中,然后调用getExtras()获得Bundle容器,最后调用其getXXX获取对应的数据!

举例如下:

  • 传递单个数据:

    image-20200813173857961

​ 在另一个activity中先获取intent然后在获取数据

image-20200813173946836

  • 传递多个数据:

    利用Bundle传递多个数据:

    image-20200813174050623

    在另一个activity中先获取Intent再获取Bundle

    image-20200813174105999

image-20200813174119098

Intent传递数组

​ 和以上传递数据类型差不多,直接举例如下:

image-20200813174205607

​ 另一个activity里面获取,直接利用日志打印。

image-20200813174222600

image-20200813174226434

Intent传递集合

  • List:

    主activity写入集合

    image-20200813174334748

    子activity获取并打印

image-20200813174347484

image-20200813174353028

Intent传递对象

Serializable实现:

​ ①业务Bean实现:Serializable接口,写上getter和setter方法

​ ②Intent通过调用putExtra(String name, Serializable value)传入对象实例 当然对象有多个的话多个的话,我们也可以先Bundle.putSerializable(x,x);

​ ③新Activity调用getSerializableExtra()方法获得对象实例: eg:Product pd = (Product) getIntent().getSerializableExtra(“Product”);

​ ④调用对象get方法获得相应参数。

举例如下:

​ 首先,创建一个User类,实现Serializable接口,写上getter和setter方法。

image-20200813174524424

然后在主activity中设置要传递的对象。

image-20200813174541278

最后在子activity中获取该对象。

image-20200813174554777

image-20200813174603727

Parcelable实现:

​ ①业务Bean继承Parcelable接口,重写writeToParcel方法,将你的对象序列化为一个Parcel对象;

​ ②重写describeContents方法,内容接口描述,默认返回0就可以

​ ③实例化静态内部对象CREATOR实现接口Parcelable.Creator

​ ④同样式通过Intent的putExtra()方法传入对象实例,当然多个对象的话,我们可以先 放到Bundle里Bundle.putParcelable(x,x),再Intent.putExtras()即可

说明:通过writeToParcel将你的对象映射成Parcel对象,再通过createFromParcel将Parcel对象映射 成你的对象。也可以将Parcel看成是一个流,通过writeToParcel把对象写到流里面, 在通过createFromParcel从流里读取对象,只不过这个过程需要你来实现,因此写的 顺序和读的顺序必须一致。

举例如下:

​ 首先,修改User类,实现Parcelable接口,并添加以下方法:

image-20200813174710754

​ 在主activity中设置对象,设置对象与Serializable设置一样:

image-20200813174745547

​ 在子activity中获取,注意获取方式不同

image-20200813174806920

image-20200813174813547

两种对象序列化方式的比较:

​ 1)在使用内存的时候,Parcelable比Serializable性能高,所以推荐使用Parcelable。

​ 2)Serializable在序列化的时候会产生大量的临时变量,从而引起频繁的GC。

​ 3)Parcelable不能使用在要将数据存储在磁盘上的情况,因为Parcelable不能很好的保证数据的 持续性在外界有变化的情况下。尽管Serializable效率低点,但此时还是建议使用Serializable。

​ 4)Parcelable是android特有的序列化API,Serializable是java的序列化技术。

Intent返回一个activity数据给另一个activity

​ 获取另一个activity返回的数据时,启动另一个activity方法要改成startActivityForResult(i, 0);其中i是指的Intent,0是请求状态码,用来识别第二个activity传回的值

image-20200813174922356

​ 在第二个activity中用setResult(1,i);准备好要返回的数据,并通过finish()方法结束改activity,

image-20200813174935694

​ 然后返回到第一个activity中执行会调回函数onActivityResult

image-20200813174952752

image-20200813175040559

利用Intent调用系统组件

​ 个人理解利用Intent调用系统组件实际上是利用了隐式Intent匹配组建的功能,通过匹配Action、data、category来打开对应的组件,如果有多个,则会出现选择列表来选择。

打开系统联系人

image-20200813175200241

image-20200813175204489

打电话

image-20200813175247322

image-20200813175252983

发短信

image-20200813175313735

image-20200813175317463

打开浏览器

image-20200813175333490

image-20200813175337613

播放音乐

image-20200813175357358

image-20200813175407272