码迷,mamicode.com
首页 > 移动开发 > 详细

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

时间:2016-05-20 16:03:16      阅读:256      评论:0      收藏:0      [点我收藏+]

标签:

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:xxxx@gmail.com"); 
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[] {"jon@example.com"}); 
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()的结果。否则,结果会被忽略掉。

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

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

标签:

原文地址:http://blog.csdn.net/qq_24850089/article/details/51435750

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!