Android成长之路(7)——关于隐式Intent的用法

Android其中最重要的特性之一,就是一个应用可以基于“action”来切换到另一个应用。比如,你的应用想要查找地方,在地图上显示。但是不一定要创建一个activity来显示地图,可以使用Intent发起一个请求来查看地址,然后Android系统会启动一个可以显示地图的应用。

之前,会使用到显式的Intent来让一个activity跳转到另一个activity。但是,当想要跳转到一个独立的应用时,比如查看地图,这时候就一定要使用隐式Intent。

创建一个隐式Intent

隐式Intent不用像显式Intent那样传入一个目标activity的类名,只需要传入操作的响应,也就是action。这个action表明了你想要做什么。Intents通常会包含与action相关的数据,比如想要查看的地址,或者想要发送的邮件信息等。这取决于你想要做什么,数据可能是Uri类型,也可能是其他类型,或者根本就不需要数据。

如果数据是Uri类型的,下面一个简单的Intent()构造函数例子,可以用来定义action和数据。

比如说,怎么样创建一个Intent初始化一个电话拨号,用Uri类型来作为电话号码的类型。

Uri number = Uri.parse("tel:5551234");
Intent callIntent = new Intent(Intent.ACTION_DIAL, number);

查看地图:

Uri location = Uri.parse("geo:38.899533,-77.036476");
Intent mapIntent = new Intent(Intent.ACTION_VIEW, location);

打开网页:

Uri website = Uri.parse("http://xxxxx.com");
Intent webIntent = new Intent(Intent.ACTION_VIEW, website);

发送邮件:

Uri emailUri = Uri.parse("mailto:[email protected]");
intent emailIntent = new Intent(Intent.ACTION_SENDTO, emailUri); 

想要传递其他不同类型的数据,可以调用不同的putExtra()方法,把数据添加进去隐式的Intent中。

默认的情况下,系统基于所包含的 Uri 数据确定Intent需要的相应 MIME 类型。如果Intent中不包含Uri,应该使用 setType()来指定与Intent相关的数据。设置MIME类型指定那一种类型的activity可以接收这个Intent。

发送一个邮件附件:

Intent emailIntent = new Intent(Intent.ACTION_SEND);
emailIntent.setType(HTTP.PLAIN_TEXT_TYPE);
emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[] {"[email protected]"});
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"));

这个Intent没有Uri,所以声明MIME类型为 “text/plain”

注意:定义Intent时要尽可能具体,这是非常重要的。比如,如果你使用ACTION_VIEW inten想要展示一张图片,那你就应该指定MIME类型为 image/*. 这就防止了你的app被Intent触发之后意外地查看到其它的数据类型(比如地图应用)

检验是否有适合的应用来接收Intent

尽管Android系统保证每一个确切的Intent都会对应apps中发起的一个请求响应(比如电话、邮件或者地图应用等),但是在调用一个Intent之前还是应该包含验证这一步骤的。如果调用了一个Intent,但是设备中并没有一个可以处理这个Intent请求响应的应用,这时候app就会崩溃了。

检验过程并不复杂,创建一个PackageManager的对象,调用queryIntentActivities() 来获取activity的List容器,这个可以用来处理Intent。如果这个List不为空的话,那就说明至少有一个应用可以接这个Intent,调用Intent是安全的。

PackageManager packageManager = getPackageManager();
List activities = packageManager.queryIntentActivities(intent,
        PackageManager.MATCH_DEFAULT_ONLY);
boolean isIntentSafe = activities.size() > 0;

使用Intent来启动一个活动

一旦已经创建了Intent,并且设置好了额外的信息,就可以调用startActivity()来发送给系统了。如果系统发现有多个应用都可以出来响应这个Intent,那么就会显示一个对话框出来,让你选择其中一个应用。如果你之前已经选择了默认的应用,那就会直接打开。

效果图,因为已经选择默认的浏览应用了,就直接打开

结合上面的检验步骤,代码如下:

Uri website = Uri.parse("http://www.baidu.com");
Intent webIntent = new Intent(Intent.ACTION_VIEW, website);

PackageManager packageManager = getPackageManager();
List activities = packageManager.queryIntentActivities(intent,
        PackageManager.MATCH_DEFAULT_ONLY);
boolean isIntentSafe = activities.size() > 0;

if(isIntentSafe){
      startActivity(webIntent);
}

用户可以勾选选择框来默认启动的应用,可能每一次用户都会选择用一个应用来打开网页或者拍照。但是呢,如果类似于分享这种,用户可能就会要选择几个不同的应用来分享了。以应该有一个选择对话框可以让用户选择。毕竟不能让用户只能勾选一个默认应用吧。

所以,需要使用 createChooser()来创建一个intent,然后再把它传进startActivity()中。这样,通过传入createChooser()方法返回的Intent,就会显示一个选择窗口,可以选择不同的应用。标题就是传入的title。

Intent intent = new Intent(Intent.ACTION_SEND);
...

// 最好在字符串资源中定义UI文本,这个文本显示的是"Share this photo with"
String title = getResources().getString(R.string.chooser_title);

// 创建一个Intent来显示选择窗口,传入intetn和标题
Intent chooser = Intent.createChooser(intent, title);

//验证
if (intent.resolveActivity(getPackageManager()) != null) {
    startActivity(chooser);
}

接收返回结果

启动另一个应用并不是只有startActivity()这个方法,也可以使用startActivityForResult()来代替startActivity(),这样启动另一应用的同时,还可以获得返回的结果。比如,启动照相机应用,然后会接收拍到的照片作为结果。当然,在activity中,需要一个方法来接收这个返回结果。activity在 onActivityResult()方法中接收。

注意:当调用 startActivityForResult(),可以使用显式Intent,也可以使用隐式Intent。当启动自己的Activity来接收结果时,应使用显式Intent确保可收到预期的结果。

接下来看看一个启动照相机应用的例子。

先添加权限,请求系统硬件的权限,还有保存照片写入文件的权限。

   <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
   <uses-feature android:name="android.hardware.camera2.CaptureRequest"/>

简单的布局,一个Button启动,一个ImageView显示图片

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.choicepictest.MainActivity">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/take_photo"
        android:text="Take Photo"/>

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/picture"
        android:layout_gravity="center_vertical"/>

</LinearLayout>

然后到MainActivity。首先,当然先初始化组件,在Oncreate()函数中调用startActivityForResult()方法启动照相机,这个方法需要传入两个参数,一个是Intent对象,一个是请求码,用来标识是哪一个发起的请求。

先定义两个请求码

 public static final int TAKE_PHOTO = 1;
 public static final int SHOW_PICTURE = 2;

onCreate方法中,先new一个File用来保存图片,使用隐式Intent发起请求android.media.action.IMAGE_CAPTURE

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        takePhoto = (Button) findViewById(R.id.take_photo);
        picture = (ImageView) findViewById(R.id.picture);

        takePhoto.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                File outImage = new File(Environment.getExternalStorageDirectory(),"my_image.jpg");

                try {
                    if(outImage.exists()){
                        outImage.delete();
                    }
                    outImage.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                imageUri = Uri.fromFile(outImage);
                Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
                intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);

                PackageManager packageManager = getPackageManager();
                List activities = packageManager.queryIntentActivities(intent,PackageManager.MATCH_DEFAULT_ONLY);
                boolean IsIntentSafe = activities.size() > 0;
                if(IsIntentSafe){
                    startActivityForResult(intent, TAKE_PHOTO);
                }

    }

接下来是另一方法onActivityResult,就是用来接收结果、处理结果的方法。

有三个参数

第一个是请求码,就是上面 startActivityForResult方法中传入的请求码。

第二个是结果码,RESULT_OK表示操作成功, RESULT_CANCELED表示用户退出或者因为某种原因。

第三个参数就是传送数据的Intent。

下面第一个处理是裁剪照片,使用startActivityForResult启动裁剪,Intent传入的是com.android.camera.action.CROP,请求码SHOW_PICTURE。操作成功后,第二个处理是显示照片

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode){
            case TAKE_PHOTO :
                if(resultCode==RESULT_OK){
                    Intent intent = new Intent("com.android.camera.action.CROP");
                    if(data !=null){
                        intent.setDataAndType(data.getData(),"image/*");
                    } else {
                        intent.setDataAndType(imageUri,"image/*");
                    }
                    intent.putExtra("scale", true);
                    intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri);
                    startActivityForResult(intent,SHOW_PICTURE);
                }
                break;
            case SHOW_PICTURE:
                if(resultCode==RESULT_OK){
                    try {
                        Bitmap bitmap = BitmapFactory.decodeStream(
                                getContentResolver().openInputStream(imageUri)
                        );
                        picture.setImageBitmap(bitmap);
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                    }
                }
                break;
            default:
                break;
        }
    }

添加Intent过滤器

前面讲的都是:从你的应用开始另一个应用的Activity。但如果你的应用可以执行对另一个应用有用的操作,应准备好响应来自其他应用的操作请求。 例如,如果构建一款可与用户的好友分享消息或照片的社交应用,最关注的是支持 ACTION_SEND,以便用户可以从另一应用发起 “共享”操作并且启动你的应用执行该操作。

要允许其他应用开始你的Activity,您需要 在相应元素的Manifest文件中添加一个 元素。

当应用安装在手机上时,系统会识别你的intent-filter并添加信息至所有已安装应用支持的Intent内部目录。当其他应用通过隐式Intent调用 startActivity() 或 startActivityForResult() 时,系统会找到可以响应该Intent的Activity。

为了确定哪一种Intent你的应用可以出来,每一个intent filter的添加应该尽可能地指定响应的类型和接收的数据类型。如果Activity具有满足以下 Intent 对象条件的intent filter,系统可能向Activity发送给定的 Intent:

Action

一个响应操作的字符串名称。例如ACTION_SEND 或者 ACTION_VIEW 。在intent filter中使用< action >来指定。指定的值一定是响应的完整字符串名称。

Data

跟intent相关的数据的描述。

在intent filter中使用< data >来指定。标签中,可以使用一个或多个属性,但是只能指定MIME类型,URI的前缀,URI的模式或者这些方法的组合和其他一些可接受的数据类型。如果你不需要声明关于这个数据的Uri(比如当你的activity处理其他的“extra”类型,而不是Uri),应该只指定这个android:mimeType属性来声明处理数据的类型。比如text/plain或者 image/jpeg。

1、android:scheme

用于指定数据的协议部分,如 http 部分。

2、 android:host

用于指定数据的主机名部分,如 www.baidu.com部分。

3、android:port

用于指定数据的端口部分,一般紧随在主机名之后。

4、 android:path

用于指定主机名和端口之后的部分,如一段网址中跟在域名之后的内容。

5、android:mimeType

用于指定可以处理的数据类型,允许使用通配符的方式进行指定。

Category

< category > 标签则包含了一些附加信息,更精确地指明了当前的活动能够响应的 Intent中还可能带有的 category。只有< action >和< category >中的内容同时能够匹配上 Intent 中指定的 action 和 category 时,这个活动才能响应该 Intent。但是category大部分很少用。所有的隐式intent默认定义为CATEGORY_DEFAULT。

每个Intent中只能指定一个action,但却能指定多个category。为了能够接收到其他应用发送过来的隐式intent,必须要在intent filter中包含 CATEGORY_DEFAULT类别 。方法 startActivity() 和 startActivityForResult() 就像声明 了CATEGORY_DEFAULT 类别那样处理所有的Intent。

比如,这里定义了一个activity,可以出来 ACTION_SEND intent,当数据类型是text或者image。

<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

为了决定在你的Activity执行哪种操作,先读取用于开始Activity的 Intent。

当你的Activity开始时,调用 getIntent()来获取一个启动你的Activity的 Intent。可以在Activity生命周期的任何时间执行此操作,但通常应该在 onCreate() 或 onStart()中执行。

例如:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    // 获取启动你的activity的Intent
    Intent intent = getIntent();
    Uri data = intent.getData();

    // 找出这个intent的类型
    if (intent.getType().indexOf("image/") != -1) {
        // 使用image数据处理
    } else if (intent.getType().equals("text/plain")) {
        // 使用text来处理
    }
}

返回结果

如果想要向调用你的应用的Activity返回一个结果,只需调用 setResult() 来指定结果码和结果的Intent。当操作完成且返回原来的Activity时,调用 finish() 关闭(和destroy)你的Activity。

例如:

Intent result = new Intent("com.example.RESULT_ACTION", Uri.parse("content://result_uri");
setResult(Activity.RESULT_OK, result);
finish();

必须要指定一个结果码作为结果。通常是RESULT_OK 或者 RESULT_CANCELED,如果需要的话,还可以提供一个装有数据的Intent。结果码默认设置为RESULT_CANCELED。所以,如果用户在完成操作之前和返回你设置的结果之前就点击返回键,那么原来的activity就会接收到RESULT_CANCELED作为结果。

如果只是简单地返回一个整数表示其中一个选择的结果,可以设置结果码为任何大于0的值。如果想用结果码传递一个整数又不需要包含一个Intent,可以调用setResult()紧紧传入结果码。

例如:

setResult(RESULT_COLOR_RED);
finish();

这里不需要检查activity是被startActivity() 还是startActivityForResult()启动的。如果启动你的activity的inten可能期待返回一个结果,那么就简单地调用setResult()。如果原来的activity已经调用了startActivityForResult(),那么系统会传送你提供给setResult()的结果。否则,结果会被忽略掉。

到这里,结合之前照相机的例子想一想,也明白了大概的流程了。

时间: 2024-11-06 16:09:26

Android成长之路(7)——关于隐式Intent的用法的相关文章

Android开发实践:实战演练隐式Intent的用法

本文通过完成一个实战任务,来掌握Android开发中隐式Intent的用法. 任务:假设我们已经实现了一个视频播放器(PlayerActivity),我们希望能把它注册到系统中,当用户点击本地视频或者在线视频时,能启动这个视频播放器. (假设该类的全路径为:com.jhuster.videoplayer.PlayerActivity) [注]:本文完整的示例代码请到我的Github下载,地址:VideoPlayer 1. 什么是隐式Intent? Intent是Android中比较重要的组件,常

Android中的显示Intent和隐式Intent

1.显示Intent 在onclick方法中 Intent intent=new Intent(FirstActivity.this,SecondActivity.class); startActivity(intent); 2.隐式Intent 隐式Intent不明确指出我们要启动哪一个活动,而是指定一系列更为抽象的action和category等信息,然后交由系统去分析这个intent,并帮我们找到合适的Intent去启动 通过标签下配置的内容,指定当前活动能够响应的action和categ

隐式Intent的使用——Android学习笔记3

隐式Intent的使用 一.为什么要用隐式Intent? 但如果想调用别的程序的组件时,且开发人员往往并不清楚别的应用程序的组件名称,这时我们只能用隐式Intent,隐式Intent恰恰相反,它不会用组件名称定义需要激活的目标组件,而是Android系统帮助应用程序寻找与Intent请求意图最匹配的组件. 二.Android系统怎么找? 主要是通过Intent Filter来寻找与隐式Intent相关的对象.具体的选择方法是:Android将Intent的请求内容<intent-filter>

在Android中Intent的概念及应用(一)——显示Intent和隐式Intent

Intent寻找目标组件的两种方式: 显式Intent:通过指定Intent组件名称来实现的,它一般用在知道目标组件名称的前提下,一般是在相同的应用程序内部实现的. 隐式Intent:通过Intent Filter来实现的,它一般用在没有明确指出目标组件名称的前提下,一般是用于在不同应用程序之间. 一.显示Intent: 创建一个Activity的完整过程: 1.手动创建一个类,让其继承自Activity: public class MyAty extends Activity 2.让其绑定一个

android隐式intent使用场景解析

Android 隐式intent相信大家都有用过,大部分场景我们用显式intent已经能满足我们的业务需求,隐式intent大部分都是用来启动系统自带的Activity或Service之类的组件.昨天有个业务场景就是,我在第三方依赖库 module A里面有个Activity A,现在需要在Activity A里面启动 module app里面的Activity B,我想了一下,可以用隐式intent来实现这个功能,而且实现代码最简洁,不用修改或添加其他代码. 隐式intent很简单,首先要在A

Android开发学习笔记:浅谈显示Intent和隐式Intent

原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://liangruijun.blog.51cto.com/3061169/655132 Intent寻找目标组件的两种方式: 显式Intent:通过指定Intent组件名称来实现的,它一般用在知道目标组件名称的前提下,一般是在相同的应用程序内部实现的. 隐式Intent:通过Intent Filter来实现的,它一般用在没有明确指出目标组件名称的前提下,一般是用于在不同应用程序之间.

[Android 成长之路]2-实现带图标的ListView

--by San[[email protected]] 在编写列表时,如果是普通的列表,使用ListView + ArrayAdapter 即可实现,但是如果要在每个列表项中文字前配一个图标了,或者在文字后面再配一个图标,这个就可以用 SimpleAdapter 实现了,不要被它的名字欺骗了,It is not simple, it is powerful. 我们以制作一个带图标的菜单列表为例,首先我们创建一个布局文件 menu_frame.xml,里面包含一个 ListView, 代码如下:

Android开发:显式/隐式Intent意图跳转Activity总结

显式跳转 在已知包名和类名的情况下常用的跳转方法: 是 nt mIntent = new Intent(); mIn Int etent.setClassName("com.android.settings","com.android.settings.Settings"); mContext.startActivity(mIntent); 我们也常这么用: y.class); startActivity(intent); 这是跳转到当前应用的某个Activity,

(Android review)显示意图激活与隐式意图激活

一.基本知识点 1.<activity android:label="第一个activity" android:name=".Main2Activity"/> label属性:某个Acivity的标题 2.R文件不要引错了,引成Android底层的了 3.intent.setClass(this, Main2Activity.class);第一个参数:上下文第二个参数:要激活的组件的字节码文件 4.显示意图激活(明确指定了要激活的组件)1)intent.