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:
可以看到intent-filter
标签对应的就是IntentFilter
这个类了。如果想要用上面的隐式Intent打开某个Activity,那么这个Activity如果是下面这样子的话,那么它就能打开:
一目了然是不是。可能你会问为什么会除了<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 为结尾的文件的mimeType 为text/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 推荐您使用一款软件:" );//设置分享的主体
好了,文章到这里就该结束了,希望这篇文章会对大家有所帮助,而且小弟不才,有错误,望指出。谢谢。
网友评论