美文网首页
Intent的简单介绍

Intent的简单介绍

作者: ErolC鱼 | 来源:发表于2019-04-14 12:42 被阅读0次

    Intent

    Intent 在Android中算是比较重要的一个类,用它可以去打开activity,发送broadCaseReceiver,开启service,下面就简单的介绍一下它。

    不同的Intent

    Intent可以分为隐式Intent和显式Intent,显式Intent比较简单,看一下例子就好:

        Intent intent = new Intent(this,Main2Activity.class);
    

    这样就得到了一个显式Intent,所谓显式Intent就是直接通过指定需要开启的类的class而得到的Intent,上面的例子就是我想打开Main2Activity这个Activity。
    而隐式Intent就像这样:

    隐式Intent

        Intent intent = new Intent();
    

    不需要指定特定的class也可以开启,但是单单这样是不够的,就算是隐式的Intent也需要指定某些东西,不然系统也不会知道你到底想要打开什么。所以在Intent中就存在这么三样东西:action、category、Data。就像这样

        //隐式Intent
        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_VIEW);
    

    然而现在指定了一个action,但是系统是怎么知道这个Intent是要打开哪个组件的呢?答案就是Android也给组件提供了一个用于匹配的类,这个类也存在这三样东西,这个类就是IntentFilter。众所周知,所有的组件都需要在项目中的一个名为AndroidManifest.xml文件中进行注册。其中就有MainActivity:

    MainActivity

    可以看到intent-filter标签对应的就是IntentFilter这个类了。如果想要用上面的隐式Intent打开某个Activity,那么这个Activity如果是下面这样子的话,那么它就能打开:

    Main2Activity

    一目了然是不是。可能你会问为什么会除了<action>标签之外还有一个<category>标签,看下去就明白了。

    三个匹配项

    通过指定Intent的Action、Category、Data而将真正需要打开的组件名称隐去的Intent,就是隐式Intent。现在分别说说这三个东西:


    三个匹配项
    名称 类型 说明
    Action String Action是一个字符串,有系统默认的(上面的Intent.ACTION_VIEW就是android.Intent.action.VIEW这个字符串)也可以自定义,如果是一个隐式Intent,那么Action是必须要指定的。
    Category String Category也是一个字符串,但是这个不是必须要在设置Intent的指定的,因为如果不指定,Intent会为其准备默认的值,这个值就是android.intent.category.DEFAULT,所以在使用<intent-filter>标签是就必须指定<category>android:name属性的值,无论Intent是否指定。

    到这里估计大家会用隐式Intent和IntentFilter了吧。

    热心的看客跳了出来:“Data你还没说呢”。

    我一拍后脑勺,赔笑道:“马上马上”。

    Intent的Data

    Intent的Data总的来说包含两部分,一部分是Uri,一部分是mimeType。还是首先简单的介绍一下uri吧。uri,名叫统一资源标识符。也就是说,正常情况下,一个根据一个Uri就可以找到唯一的一个资源(这个资源可以是图片,文本等等),在互联网中除了Uri,还有一个Url,但是与本文无关,这里就不多做讨论。有兴趣的可以自行百度(Google)。而一个Uri又由多个部分组成:

    scheme:[//host[:port]][/path][?query][#fragment]

    给个例子:http://example.com/item/intent.html

    data标签属性

    这图就是<intent-filter>的子标签<data>的有关属性,下面就详细的说一说这个图:

    属性说明
    属性名 描述
    android:mimeType 表示这个Uri标识的资源的类型,比如以.txt为结尾的文件的mimeTypetext/plain
    android:path 该属性的值对应Uri中的path部分,即例子中“/item/intent.html”部分
    android:host 该属性的值对应Uri中host部分,即例子中“example.com”部分
    android:scheme 该属性的值对应Uri中scheme部分,即例子中“http”部分
    android:pathPattern 该属性的值作为表达式,匹配整个Uri,*.为匹配符,*代表匹配前者零次或多次,比如:“q*”可以匹配“q”,“qq”等,.则匹配任意字符。而\为转义字符,如果想表达一个*,则需要\\*这样表示,如果想要表达一个\,则需要\\\\这样表示
    android:pathPrefix 该属性的值为路径前缀,即例子中“/item”部分
    android:port 该属性的值对应Uri中port部分
    android:ssp ssp为“scheme-specific part”缩写,意为除了scheme之外的一切。以http://example.com/item/inten为例,https为scheme,那么ssp就是://example.com/item/inten.
    android:sspPattern 该属性的值与android:pathPattern相仿
    android:sspPrefix 该属性的值与android:pathPrefix相仿

    需要注意的是:在配置<intent-filter>是,<data>不是必须的,<data>的属性所书写的Uri指向的资源也非必须存在,在本质上来说,Uri依旧是一个字符串,在Intent和IntentFilter匹配的层面来说,只要两者对的上就完全可以了,至于Uri指向的资源是否存在,这个不归Intent管。所以,在Data里与资源有关的android:mimeType属性也不是必须的。

    好了,也说完Data了,至此,在简单总结一下Intent和IntentFilter之间的匹配规则:
    首先,action是必须要有的,也是必须完全一致的。然后Intent方的category也是必须的,但是并非一定要写,因为Intent会提供默认值,而IntentFilter方是必须要写的。最后一个是Data,这个对于双方来说都不是必须的,但是IntentFilter写了,如果要下一个访问到该组件的Intent,那么就必须写Data部分,而且与IntentFilter的Data部分完全匹配上。这里更要注意的是,当IntentFilter定义了Uri和mimeType,那么在设置Intent的Uri和mimeType就必须使用setDataAndType()这个方法一次性设置,而不能通过setData方法和setType方法分开设置。原因很简单,看了这三个方法的源码就知道了,这三个方法的源码并不难,也就几行而已。

    Intent的剩下的设置

    剩下的设置有两个:第一个是putExtra,也就是给这个Intent加入额外的数据,这个是很常用的,主要的两个组件通过Intent的数据交流方式。通过putExtra方法加入,通过get~~()方法取出,get类方法根据数据类型分多种,使用的时候,你便可得知。
    第二个就是flag,也就是标志,这个倒是不经常用,但是对于特定场景来说确实很有用。我就简单说一个吧:退出登录。

    一个退出登录操作就是,点击退出按钮,界面将重新回到登录界面,如果在登录界面点击后退,那么将回到主界面。
    这里就会有一个问题,如果一个退出按钮在多层activity之后,也就是在ActivityA跳转到ActivityB,然后再跳转到ActivityC中,现在假设退出按钮就在ActivityC中。现在点击退出按钮,回到了登录界面(ActivityLogin),如果此时后退,按照正常情况就是会回退到ActivityC中,就算在跳转之后,ActivityC使用finish方法结束掉自己,那么在ActivityLogin回退后依旧会回到ActivityB中。这不符合业务。在这个场景中,Intent的filg中的某些个就可以起到奇效:Intent.FLAG_ACTIVITY_CLEAR_TASK|Intent.FLAG_ACTIVITY_NEW_TASK,清空栈并新建栈以存放新创建的ActivityLogin界面。这样ActivityLogin就会变成唯一的一个Activity,再后退,自然回到主界面。

    至于Intent的更多flag用法就不在这多说了,有一个小点就是,Intent可以设置一个flag也可以设置多个。

    Intent整个就已经简单的介绍完了。下面再说说Intent的一些好玩的特殊的常用的用法吧。

    Intent实际使用

    打电话

        //跳转到拨号页面
        Intent intent = new Intent(Intent.ACTION_DIAL,Uri.parse("tel:"+phone));
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent)
    
        //跳过拨号界面直接拨打电话
        Intent intent=new Intent(Intent.ACTION_CALL,Uri.parse("tel:"+phone));
        startActivity(intent);
    
    

    打开一个选择面板

    用特定Intent可以直接打开一个选择面板,这个面板是为多选准备的,需要一次使用多个Intent的时候,就会用到它。

       Intent chooserIntent = Intent.createChooser(targetedShareIntents.remove(0), "title");
       chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedIntents.toArray(new Parcelable[]{}));
       startActivity(chooserIntent);
    

    原生分享

    所谓原生分享,就是不需要第三方的sdk使用Android本身提供的api进行的分享。

    Intent share_intent = new Intent();//首先创建一个空意图
    
            share_intent.setAction(Intent.ACTION_SEND);//给这个意图设置action
    
            share_intent.settype("text/plain");//设置分享的数据的类型
    
            share_intent.putextra(intent.extra_subject, "分享");//设置分享的主题
    
            share_intent.putextra(intent.extra_text, "hi 推荐您使用一款软件:" );//设置分享的主体
    
            share_intent = Intent.createChooser(share_intent, "分享");//这个是创建分享弹框面板,事实上这一步可以省略,功能上对整个分享没有影响,Intent.createChooser这个方法只是让我们自己决定分享弹框面板的title而已。
    
            startActivity(share_intent);//打开分享面板或者打开对应的应用对应的activity
    
            //上面是最简单的原生分享的写法,有个缺点就是会调出手机上所有存在分享模块的应用,有些时候我们只想分享到其中的某些,所以可以按照一下写法,对分享的对象进行筛选:
            //接着:就是可以筛选掉不需要分享的应用了,
            //创建分享意图
            Intent targeted = new Intent(Intent.ACTION_SEND);//创建一个分享意图
            targeted.setType("text/plain");//定义要分享的数据类型
    
            List<ResolveInfo> resInfo = context.getPackageManager().queryIntentActivities(targeted,
                    PackageManager.MATCH_DEFAULT_ONLY);//根据意图可以获取到所有支持该意图的activity的数据。
            //判空,判断是否存在这样的activity,如果没有就不用白忙活了。
            if (!resInfo.isEmpty()) {
                List<Intent> targetedShareIntents = new ArrayList<>();//这里用来存放所有你想要分享的应用的意图
                for (ResolveInfo info : resInfo) {
                  
                    ActivityInfo activityInfo = info.activityInfo; //获取activity的信息,这里包括包名,activity名称等等
                    PackageManager pm = context.getApplicationContext().getPackageManager(); //获取包的管理类,以便获取应用名
    
                    Log.e("name", "share:---- " + info.activityInfo.applicationInfo.loadLabel(pm).toString());
                    //筛选需要分享的应用中的分享模块(有些应用会有多个分享模块)
    
                    if (shouldShare(info.activityInfo.name)) {//在shouldShare()这个方法中做判断,如果该activity是你需要的就返回true,不是则返回false,如果是,则做下面的步骤,填充这个意图,
                        // 分享出去的内容
                        targeted.putExtra(Intent.EXTRA_TEXT, content);
                        // 分享出去的标题
                        targeted.putExtra(Intent.EXTRA_SUBJECT, title);
                        //设置分享的activity所在的包
                        targeted.setPackage(activityInfo.packageName);
                        //设置需要分享的activity类名
                        targeted.setClassName(activityInfo.packageName, info.activityInfo.name);
                        //将生成的分享意图放入集合中
                        targetedShareIntents.add(targeted);
                    }
                }
                try {
                    // 选择分享时的标题,创建分享面板
                    Intent chooserIntent = Intent.createChooser(targetedShareIntents.remove(0), "选择分享的应用");
                    if (chooserIntent == null) {
                        return;
                    }
                    //将分享的意图集中在选择分享的意图中
                    chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedShareIntents.toArray(new Parcelable[]{}));
                    //打开分享面板
                    context.startActivity(chooserIntent);
                } catch (Exception ex) {
                    Log.e(TAG, "share: " + ex);
                }
            }
    

    打开一些第三方地图,进行路线导航。

    这里展示的是打开腾讯、高德、百度的地图

        //腾讯地图使用的Uri,其中lat和lng是终点的经纬度,addressName是终点的名称
        String uriString = "qqmap://map/routeplan?type=drive&to=" + addressName + "&tocoord=" + lat + "," + lng + "&policy=2&referer=myapp";
        //百度地图使用的Uri
        String uriString = "baidumap://map/direction?" +
                    "destination=name:" + addressName + "|latlng:" + lat + "," + lng+
                    "&mode=transit&sy=3&index=0&target=1";
    
        //高德地图使用的Uri,高德多提供了一个初始地址,如果不填,将由当前地址作为起始点
         String uriString = "androidamap://route?sourceApplication=amap&slat=" + currentLat + "&slon=" + currentLng+"&dlat=" + lat + "&dlon=" + lng + "&dname=" + addressName + "&dev=0&t=1";
    
        //高德地图浏览器版Uri
        String uriString = "http://uri.amap.com/navigation?to=" + lng + "," + lat + "," +addressName + "&mode=car&policy=1&src=mypage&coordinate=gaode&callnative=0";
    
    
        Uri uri = Uri.parse(uriString);
        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_VIEW);
        intent.addCategory(Intent.CATEGORY_DEFAULT);
        intent.setData(uri);
        startActivity(intent);
    

    从以上实际使用可以看出隐式Intent的一些便利性,而从分享的实际使用中,我们可以看到,其实在Data里包含的mimeType的意义不单单表示Uri是标识的资源的类型,更为广泛的可以是认为希望让Intent开启的组件知道,我给你的是什么类型的数据,无论是Uri标识的还是通过putExtra传递过去的,
    下面是分享的部分代码:

        share_intent.settype("text/plain");//设置分享的数据的类型
    
        share_intent.putextra(intent.extra_subject, "分享");//设置分享的主题
    
        share_intent.putextra(intent.extra_text, "hi 推荐您使用一款软件:" );//设置分享的主体
    

    好了,文章到这里就该结束了,希望这篇文章会对大家有所帮助,而且小弟不才,有错误,望指出。谢谢。

    相关文章

      网友评论

          本文标题:Intent的简单介绍

          本文链接:https://www.haomeiwen.com/subject/rebjwqtx.html