4、Android开发-与其他应用进行交互

前言

Android 应用一般具有若干个 activity。每个 activity 显示一个界面,用户可通过该界面执行特定任务(例如查看地图或拍照)。如需将用户从一个 activity 转至另一 activity,您的应用必须使用 Intent 定义应用执行操作的“intent”。当您使用 startActivity() 等方法将 Intent 传递至系统时,系统会使用 Intent 识别和启动相应的应用组件。使用 intent 甚至可以让您的应用启动另一个应用包含的 activity。

Intent 可以为“显式”,以便启动特定组件(特定的 Activity 实例),也可为“隐式”,以便启动任何可以处理预期操作(例如“拍摄照片”)的组件。

本节介绍了如何使用 Intent 执行与其他应用的一些基本交互,例如启动另一个应用、接收来自该应用的结果以及使您的应用能够响应来自其他应用的 intent。

跳转到其他应用

Android 最重要的功能之一是应用能够基于它要执行的“操作”将用户转到其他应用。例如,如果您的应用包含您想要在地图上显示的商家地址,您无需在应用中构建用于显示地图的 activity,而是可以创建使用 Intent 查看地址的请求。然后,Android 系统即会启动能够在地图上显示地址的应用。

正如第一课构建首个应用中所述,您必须使用 intent 在自己应用中的 activity 之间进行导航。通常情况下,您可以使用显式 intent 来实现该目标,显式 intent 可定义您希望启动的组件的确切类名称。但是,如果您希望让另一个应用来执行某项操作(例如,“查看地图”),则必须使用隐式 intent。

本课向您展示如何针对特定操作创建隐式 intent,以及如何使用该 intent 启动在另一个应用中执行操作的 activity。

构建隐式 intent

隐式 intent 不会声明要启动的组件的类名称,而是声明要执行的操作。该操作指定您要执行的操作,例如查看、编辑、发送或获取某项内容

将 intent 操作与数据相关联

intent 通常还包括与操作相关的数据,例如您要查看的地址或您要发送的电子邮件。根据您要创建的 intent,数据可能是 Uri 或其他几种数据类型之一,也可能该 intent 根本不需要数据。

如果您的数据是 Uri,则可以使用一个简单的 Intent() 构造函数来定义操作和数据。

Uri 是什么?
在Android中位于android.net.Uri,URI是统一资源标识符(Uniform Resource Identifier) 的意思。Android上可用的每种资源 (图像、视频片段、网页等) 都可以用Uri来表示。从概念上来讲,URI包括URL(URL显得很宏观,是网络资源定位的,而URI是应用程序内部或之间定位)。
Uri的通用格式为:scheme: scheme-specific-part #fragment
它的作用是根据这个URI找到某个资源文件,基本格式如: file:///sdcard/temp.jpg
通常有下面三种形式
(1)scheme://authority path ?query #fragment
(2)scheme://host:port path ?query #fragment
(3)scheme:scheme-specific-part #fragment
第一种用于访问本地资源,这里的scheme为content或者file,如:
content://contacts/people/5 (ContentProvider是程序间共享数据的,它也需要生成URI供别的程序调用,格式如:
content:///StudentDB/student/name,以后你在别的程序想访问另一个程序里的数据库,就可以用这个URI去访问了,而不用进行数据库连接的操作,非常方便)
//访问sdcard/download/目录下的音频文件
//会利用系统提供的provider去读取文件
//从/data/data/com.android.providers.media/databases中的数据库表中访问
file:///sdcard/download/123.mp3
第二种用于访问网络资源,这里的scheme通常为http或https。
第三种用于打电话等服务,这里的scheme通常为smsto和tel地理位置信息等。
我们很经常需要解析Uri,并从Uri中获取数据。
Android系统提供了两个用于操作Uri的工具类,分别为UriMatcher 和ContentUris 。

intent 打电话
Uri telSchema = Uri.parse("tel:18963889696");
Intent callIntent = new Intent(Intent.ACTION_DIAL, telSchema);
startActivity(callIntent);
intent访问位置
// Map point based on address
// Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California");
// Or map point based on latitude/longitude
 Uri location = Uri.parse("geo:37.422219,122.08364?z=14"); // z param is zoom level
Intent mapIntent = new Intent(Intent.ACTION_VIEW, location);
startActivity(mapIntent);
intent查看网页
Uri page = Uri.parse("https://www.baidu.com");
Intent pageIntent = new Intent(Intent.ACTION_VIEW, page);
startActivity(pageIntent);
向 intent 添加额外数据

其他类型的隐式 intent 需要“额外”数据,以提供字符串等不同数据类型。您可以使用各种不同的 putExtra() 方法添加一条或多条额外数据。

默认情况下,系统根据所包含的 Uri 数据确定 intent 需要的相应 MIME 类型。如果您不在 intent 中包括 Uri,通常应使用 setType() 来指定与 intent 相关联的数据类型。设置 MIME 类型可以进一步指定应接收 intent 的 activity 类型。

以下是一些添加了额外数据来指定所需操作的其他 intent:

发送邮件
 // 发送带有附件的电子邮件(调用Android系统的邮件服务 Gmail)
        Intent emailIntent = new Intent(Intent.ACTION_SEND);
        // The intent does not have a URI, so declare the "text/plain" MIME type
        emailIntent.setType("text/plain");
        emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[] {"jan@example.com"}); // recipients
        emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Email subject");
        emailIntent.putExtra(Intent.EXTRA_TEXT, "Email message text");
        emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://path/to/email/attachment"));

创建日历活动
//创建日历活动
        // Event is on January 23, 2021 -- from 7:30 AM to 10:30 AM.
        Intent calendarIntent = new Intent(Intent.ACTION_INSERT, CalendarContract.Events.CONTENT_URI);
        Calendar beginTime = Calendar.getInstance();
        beginTime.set(2021, 0, 23, 7, 30);
        Calendar endTime = Calendar.getInstance();
        endTime.set(2021, 0, 23, 10, 30);
        calendarIntent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, beginTime.getTimeInMillis());
        calendarIntent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endTime.getTimeInMillis());
        calendarIntent.putExtra(CalendarContract.Events.TITLE, "Ninja class");
        calendarIntent.putExtra(CalendarContract.Events.EVENT_LOCATION, "Secret dojo");

显示应用选择器

注意,当您通过将 Intent 传递给 startActivity() 启动一个 activity,并且有多个应用可以响应该 intent 时,用户可以选择默认使用哪个应用(通过选中对话框底部的复选框;请参见图 1)。当执行用户通常希望每次都使用同一应用完成的操作时,比如打开网页(用户可能只使用某个网络浏览器)或拍照(用户可能更喜欢某个相机),这非常有用。

但是,如果要执行的操作可以由多款应用处理并且用户可能希望每次都使用不同的应用(例如,“分享”操作,用户可能有多款应用可用来分享内容),您应明确显示选择器对话框,如图 2 所示。选择器对话框会强制用户每次都要选择用于相应操作的应用(用户无法针对该操作选择默认应用)。

如需显示选择器,请使用 createChooser() 创建 Intent,并将其传递给 startActivity()。例如:
这会显示一个对话框,其中包含可响应传递给 createChooser() 方法的 intent 的应用列表,并将提供的文本用作对话框标题。

以下示例是所有响应 图片的应用 都会展示到应用选择器的窗口中:

public void goChooser(View view) {
        Intent intent = new Intent(Intent.ACTION_SEND);
//        它使用了Intent.ACTION_SEND 和 MIME 类型来查找支持image/* 的全部Data Picker ,同意用户选择当中之中的一个
        intent.setType("image/*");
// Always use string resources for UI text.
// This says something like "Share this photo with"
        String title = getResources().getString(R.string.chooser_title);
// Create intent to show chooser
        Intent chooser = Intent.createChooser(intent, title);

// Try to invoke the intent.
        try {
            startActivity(chooser);
        } catch (ActivityNotFoundException e) {
            // Define what your app should do if no activity can handle the intent.
        }
    }

在这里插入图片描述

获取 activity 的结果

启动另一个 activity(无论是您应用中的 activity 还是其他应用中的 activity)不一定是单向操作。您也可以启动另一个 activity 并接收返回的结果。例如,您的应用可启动相机应用并接收拍摄的照片作为结果。或者,您可以启动“通讯录”应用以便用户选择联系人,并且您将接收联系人详细信息作为结果。

虽然所有 API 级别的 Activity 类均提供底层 startActivityForResult()onActivityResult() API,但我们强烈建议您使用 AndroidX Activity 和 Fragment 中引入的 Activity Result API。

Activity Result API 提供了用于注册结果、启动结果以及在系统分派结果后对其进行处理的组件。

针对于使用 Activity Result API ,这里先不做具体分析,毕竟想较好的使用它也不是一时半会就能搞定的事,这个会放到单独的文章中进行使用分析与详解。

我们先来尝试一下使用startActivityForResult() 和 onActivityResult() 来获取activity的返回结果。

startActivityForResult 方法的目的是 从一个activity(A)访问令一个activity(B)时 需要B返回结果给A。这里我们有一个需求:
根据学生的编号,获取学生的家庭住址

示例如下:

// startActivityForResult 用法
    public void teststartActivityResult(View view) {

        //根据学生的编号,获取学生的家庭住址
        Intent intent = new Intent(this, OtherThirdActivity.class);
        intent.putExtra("userCode","00112");

        //requestCode一定要>=0,接收返回数据时用requestCode区分到底是哪个子模块返回的数据
        startActivityForResult(intent,1,null);

    }

// 调用OtherThridActivity 的回调
 @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == 1 && resultCode == RESULT_OK) {
            // SearchAddressInfo info = (SearchAddressInfo) data.getParcelableExtra("position");
            String userAddress = data.getStringExtra("userAddress");
            TextView viewById =(TextView)findViewById(R.id.activity_for_result);
            viewById.setText(userAddress);
        }
    }

OtherThirdActivity 代码:

public class OtherThirdActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_other_third);

        String userCode = getIntent().getStringExtra("userCode");
        getIntent().putExtra("userAddress","北京市海淀区"+userCode);
        setResult(RESULT_OK,getIntent());
        finish();
    }


}

上述代码的具体流程如下:

通过点击事件调用方法teststartActivityResult ,方法内部通过startActivityForResult方式启动另外一个activity,并传入请求code,供回调时判断请求来源。

OtherThirdActivity的初始化方法中获取参数并返回地址信息给回调,通过setResult方法。

在请求的activity内通过onActivityResult进行响应数据的接收。

允许其他应用启动您的 activity

如果您的应用可以执行对另一个应用可能有用的操作,您的应用应通过在 activity 中指定适当的 intent 过滤器,准备好响应操作请求。

例如,如果您构建一款可与用户的好友分享消息或照片的社交应用,则应该支持 ACTION_SEND intent。然后,当用户从其他应用发起“分享”操作时,您的应用将在选择器对话框(又名“消歧对话框”)中作为选项显示,如图 1 所示。
在这里插入图片描述

如需允许其他应用以此种方式启动您的 activity,您需要在清单文件中为对应的 <activity> 元素添加一个 <intent-filter> 元素。

当您的应用安装在设备上时,系统会识别您的 intent 过滤器,并将信息添加至所有已安装应用所支持的 intent 的内部目录。当应用通过隐式 intent 调用 startActivity() 或 startActivityForResult() 时,系统会找到可响应该 intent 的一个或多个 activity。

添加 intent 过滤器

为正确定义您的 activity 可以处理的 intent,您添加的每个 intent 过滤器在操作类型和 activity 接受的数据方面应尽可能具体。

如果 activity 具有满足以下 Intent 对象条件的 intent 过滤器,系统可能会向该 activity 发送给定的 Intent:

Action

对要执行的操作命名的字符串。通常是平台定义的值之一,比如 ACTION_SENDACTION_VIEW
使用 <action> 元素在您的 intent 过滤器中指定此内容。您在此元素中指定的值必须是操作的完整字符串名称,而非 API 常量(请参阅以下示例)。

Intent中的Action必须能够和Activity过滤规则中的Action匹配. 如果一个Intent-filter过滤规则中有多个action,那么只要Intent中的action能够和Intent-filter过滤规则中的任何一个action相同即可匹配成功。

DATA

使用 <data> 元素在您的 intent 过滤器中指定此内容。使用此元素中的一个或多个属性,您可以只指定 MIME 类型、URI 前缀、URI 架构,也可以指定这些内容的组合以及其他指示所接受数据类型的项。

注意:如果您无需声明关于数据 Uri 的具体信息(比如在您的 activity 处理的是其他类型的“额外”数据而不是 URI 的情况下),您应只指定 android:mimeType 属性,以声明您的 activity 处理的数据类型,比如 text/plain 或 image/jpeg。

data 由两部分组成
mineType 和 URI
mineType: 指媒体类型 例如: image/jpeg vided/* …

category

提供另外一种表征处理 intent 的 activity 的方式,通常与用户手势或 activity 启动的位置有关。系统支持多种不同的类别,但大多数都很少使用。不过,所有隐式 intent 默认使用 CATEGORY_DEFAULT 进行定义。
使用 <category> 元素在您的 intent 过滤器中指定此内容。

如果Intent中的存在category,那么所有的category都必须在Intent-filter过滤规则中的category出现过,才能和这个Activity匹配。Intent中的category数量可能少于Activity中配置的category数量,但是Intent中的这category必须和Activity中配置的category相同才能匹配。
我们来看个例子:
我们在Activity中配置category匹配规则:

<activity
 android:name=".AActivity"
 android:label="@string/app_name"
 android:theme="@style/AppTheme.NoActionBar">
 <intent-filter>
 <category android:name = "android.intent.category.DEFAULT" />
 <category android:name="aaa.bb.cc"/>
 <action android:name="com.axe.mg.what" />
 </intent-filter>
</activity>

运行以下代码可以匹配到AActivity:

public void match(){
 Intent intent = new Intent();
 intent.addCategory("aaa.bb.cc");
 intent.setAction("com.axe.mg.what");
 startActivity(intent);
}

注意:只通过category匹配是无法匹配到AActivity的。因为category属性是一个执行Action的附加信息。必须靠action来同时匹配。

案例

在您的 intent 过滤器中,您可以通过声明嵌套在 元素中的具有相应 XML 元素的各项,来声明您的 activity 接受的条件。

例如,以下就是一个 Activity,它具有在数据类型为文本或图像时处理 ACTION_SEND Intent 的 Intent 过滤器:

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

提示:如果您希望选择器对话框中的图标与 activity 的默认图标不同,请在 <intent-filter> 元素中添加 android:icon。

每个入站 intent 仅指定一项操作和一个数据类型,但可以在每个 中声明 <action>、<category> 和 <data> 元素的多个实例。

如果任何两对操作和数据的行为相斥,您应创建单独的 intent 过滤器,以指定与哪种数据类型配对时哪些操作可以接受。

例如,假定您的 activity 同时处理 ACTION_SENDACTION_SENDTO intent 的文本及图像。在这种情况下,您必须为两个操作定义两种不同的 intent 过滤器,因为 ACTION_SENDTO intent 必须使用数据 Uri 指定使用 send 或 sendto URI 架构的收件人地址。例如:

<activity android:name="ShareActivity">
    <!-- filter for sending text; accepts SENDTO action with sms URI schemes -->
    <intent-filter>
        <action android:name="android.intent.action.SENDTO"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:scheme="sms" />
        <data android:scheme="smsto" />
    </intent-filter>
    <!-- filter for sending text or images; accepts SEND action and text or image data -->
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="image/*"/>
        <data android:mimeType="text/plain"/>
    </intent-filter>
</activity>

注意:为了接收隐式 intent,您必须在 intent 过滤器中添加 CATEGORY_DEFAULT 类别。startActivity() 和 startActivityForResult() 方法将所有 intent 当作声明了 CATEGORY_DEFAULT 类别一样对待。如果您没有在 intent 过滤器中声明该类别,任何隐式 intent 都不会解析为您的 activity。

总结:

以上重点描述了如何在其中的应用中启动另一个应用,另一个应用如何配置才能隐式的接收一个应用的请求。以及如何通过intent的方式进行参数处理,并传给另一个应用的相关的零散的知识,下一节将继续描述,如何从其他应用接收简单的数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

senda66

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值