Android中URI的理解(与Intent的联系)

在Android中,几乎所有数据都由URI来表示,如查询ContenProvider中的数据需要URI,Intent启动一个新Activity有时候也需要为Intent设置URI以来表示需要操作的对象,

(比如以下代码:

       一、打开一个网页,类别是Intent.ACTION_VIEW
  Uri uri = Uri.parse(“http://blog.3gstdy.com/”);
  Intent intent = new Intent(Intent.ACTION_VIEW, uri);
  二、打开地图并定位到一个点
  Uri uri = Uri.parse(“geo:52.76,-79.0342″);
  Intent intent = new Intent(Intent.ACTION_VIEW, uri);

貌似Intent和URI一组合能做好多事情,但是这个Intent在Android中为何能够做这么多事情呢?我猜测可能是由于Android中有一个注册机制,然后通过Intent和URI就可以去通过注册机构找到想要操作的对象和数据,类似于中间有一个代理机构一样!看一下URI的结构吧。

URI有以下几种类型,(不全)

http://  表示网址

file://   表示文件

tel://    表示电话

mailto:.... 表示发送邮件

geo: 表示地理位置

content:// 表示ContentProvider

(注,更全的类型在这个博客: Intent 绑定 URI)


我们再看看IntentFilter中data的结构(URI表示数据,data表示数据类型,应该有关系)、

直接引用一片文章,文章地址为:http://www.cnblogs.com/newcj/archive/2011/08/11/2135094.html

intent-filter 之 data 「scheme, host, port, mimeType, path, pathPrefix, pathPattern」

之前一直搞不很明白 AndroidManifest.xml 中 activity 标签下的 intent-filter 中 data 标签的属性含义,今天认真看了 Dev Guide,又在网上查询了大量相关资料,现把 data 标签中的属性含义做一个总结。
 

一、定义
 

scheme, host, port, path, pathPrefix, pathPattern 是用来匹配 Intent 中的 Data Uri 的。具体规则如下:
 

  scheme://host:port/path or pathPrefix or pathPattern
 

这里需要注意的是这里的 scheme 不是 schema,也许你记得 xmlns:android="http://schemas.android.com/apk/res/android" 这段声明,你就会想起其中的 schema (至少我是这样想到了...- -!),但这里的 scheme 不是 schema。虽然在写 AndroidManifest.xml 的时候,有智能提示,但是希望大家还是能注意到。

上面那句最后的 “path or pathPrefix or pathPattern” 是指后面的 path 验证可以使用 data 属性中的 android:path、android:pathPrefix 或 pathPattern,你可以添加任意个 data 标签,由于是 “or” ,因此,只要其中任意一个 data 匹配,系统就会选择你的 Activity 启动,当然,如果别的 Activity 也有相同的 data 标签,系统就会给用户弹出一个 Chooser Dialog。


mimeType 也是是用来匹配 Intent 的。比如,当你使用 Intent.setType("text/plain") ,那么系统将会匹配到所有注册 android:mimeType="text/plain" 的 Activity,想获取更多有关 mimeType 的知识请参考:【转】备份:Android 常用 mimeType 表

这里需要十分注意的是 Intent.setType(), Intent.setData,Intent.setDataAndType() 这三个方法!
 

  • setType 调用后设置 mimeType,然后将 data 置为 null;
  • setData 调用后设置 data,然后将 mimeType 置为 null;
  • setDataAndType 调用后才会同时设置 data 与 mimeType。
     
另外需要注意的是,如果你在 data 标签,既设置了 mimeType 又设置了 scheme 之内的,那么你的 Intent 需要同时设置匹配的 data 与 mimeType 即调用 setDataAndType ,系统才能匹配到这个 Activity(即便你 mimeType 设置为 "*/*" 也是如此)。当然,如果你没有设置 mimeType,那么调用 setData 进行匹配,如果你设置了任何的 mimeType 将不会匹配到该 Activity。

二、区别

  这里主要说的区别是  pathpathPrefixpathPattern 之间的区别
 
  • path 用来匹配完整的路径,如:http://example.com/blog/abc.html,这里将 path 设置为 /blog/abc.html 才能够进行匹配;
  • pathPrefix 用来匹配路径的开头部分,拿上面的 Uri 来说,这里将 pathPrefix 设置为 /blog 就能进行匹配了;
  • pathPattern 用表达式来匹配整个路径,这里需要说下匹配符号与转义。
匹配符号:
  1. “*” 用来匹配0次或更多,如:“a*” 可以匹配“a”、“aa”、“aaa”...
  2. “.” 用来匹配任意字符,如:“.” 可以匹配“a”、“b”,“c”...
  3. 因此 “.*” 就是用来匹配任意字符0次或更多,如:“.*html” 可以匹配 “abchtml”、“chtml”,“html”,“sdf.html”...
转义:
因为当读取 Xml 的时候,“\” 是被当作转义字符的(当它被用作 pathPattern 转义之前),因此这里需要两次转义,读取 Xml 是一次,在 pathPattern 中使用又是一次。如:“*” 这个字符就应该写成 “\\*”,“\” 这个字符就应该写成 “\\\\”。


三、一些例子

   例子1:如果我们想要匹配 http 以 “.pdf” 结尾的路径,使得别的程序想要打开网络 pdf 时,用户能够可以选择我们的程序进行下载查看。

我们可以将 scheme 设置为 “http”,pathPattern 设置为 “.*\\.pdf”,整个 intent-filter 设置为:
View Code
1 <intent-filter>
2 <action android:name="android.intent.action.VIEW"></action>
3 <category android:name="android.intent.category.DEFAULT"></category>
4 <data android:scheme="http" android:pathPattern=".*\\.pdf"></data>
5 </intent-filter>

如果你只想处理某个站点的 pdf,那么在 data 标签里增加  android:host="yoursite.com" 则只会匹配 http://yoursite.com/xxx/xxx.pdf,但这不会匹配 www.yoursite.com,如果你也想匹配这个站点的话,你就需要再添加一个 data 标签,除了 android:host 改为 “www.yoursite.com” 其他都一样。
 

例子2:如果我们做的是一个IM应用,或是其他类似于微博之类的应用,如何让别人通过 Intent 进行调用分享出现在选择框里呢?
 
我们只用注册 android.intent.action.SEND 与 mimeType 为 “text/plain” 或 “*/*” 就可以了,整个 intent-filter 设置为:
View Code
1 <intent-filter>
2 <action android:name="android.intent.action.SEND" />
3 <category android:name="android.intent.category.DEFAULT" />
4 <data mimeType="*/*" />
5 </intent-filter>

这里设置 category 的原因是,创建的 Intent 的实例默认 category 就包含了 Intent.CATEGORY_DEFAULT ,google 这样做的原因是为了让这个 Intent 始终有一个 category。


例子3:如果我们做的是一个音乐播放软件,当文件浏览器打开某音乐文件的时候,使我们的应用能够出现在选择框里?
 
这类似于文件关联了,其实做起来跟上面一样,也很简单,我们只用注册 android.intent.action.VIEW 与 mimeType 为 “audio/*” 就可以了,整个 intent-filter 设置为:
View Code
1 <intent-filter>
2 <action android:name="android.intent.action.VIEW" />
3 <category android:name="android.intent.category.DEFAULT" />
4 <data android:mimeType="audio/*" />
5 </intent-filter>


参考:
【2】备份:Android 常用 mimeType 表  http://www.cnblogs.com/newcj/archive/2011/08/10/2134305.html

结束


现在看看URI和IntentFilter的data标签之间的联系,

对以上文章进行总结,可以看到,以ContentProvider的URI为例说名,

<data>标签中的scheme对应于 ContentProvider的URI中的content://

<data>标签中的mimeType和ContentProvider也是有联系的,ContentProvider要求查询的时候返回一个MIME类型。

<data>标签中的 pathpathPrefixpathPattern相当于ContentProvider的URI的中间路径!


也就是说URI中的数据会在IntentFilter中进行一遍过滤!所以你可以看到的相似的貌似又有联系的操作:

Intent intent = new Intent();

intent.setData(Uri.parse(“http://blog.3gstdy.com/));

intent.setAction("android.intent.action.VIEW");

startActivity(intent);


如果是隐式Intent,应该是判断Action类型,知道大概要执行什么样的动作,然后通过URI的头字段找,找到服务类型,找到数据路径,然后在注册了的

注册表上进行寻找,如果在Intent中有符合相应要求的,那么几找到了自己想找的对象!至于源码和Android具体怎么做的,我们先不管,先猜测就是这样工作的,以后看framework和源码的时候再回来补!


其他参考文章:

编写可接收浏览Intent的Activity

参考文章二:

I am writing my android application, which I want to define a custom URI scheme, so that user can go to my app by typing a URI in browser, like: myapps://cate=1&id=3

I successfully implemented this in my apps, but I discover that for some device, the browser treat the link differently.

In my HTC Flyer, it opens my app correctly, but in Samsung Galaxy Ace, the browser translates the link to myapps%3A%2F%2Fcate=1%26id=3, which is encoded, and it just google the "myapps://cate=1&id=3" for me instead of open the app.

I define the intent filter in the manifest like this:

<intent-filter >
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.BROWSABLE" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:scheme="myapps"/>
</intent-filter>

Any help on this issue? thanks

EDITED

I just looked at the source code of android browser, it defined what scheme it accepts:

protected static final Pattern ACCEPTED_URI_SCHEMA = Pattern.compile(
    "(?i)" + // switch on case insensitive matching
    "(" +    // begin group for schema
    "(?:http|https|file):\\/\\/" +
    "|(?:inline|data|about|content|javascript):" +
    ")" +
    "(.*)" );

Now I understand why custom scheme won't work! Any apps should only capture schemes: http,https,file,inline,data,about,content,javascript.


自己的的实验代码:

代码说明:

两个Activity,MainActivity和activity_test,第一个Activity有一个按钮,可以进行跳转到第二个Activity,

这里的Intent的data我们设置的是一个自定义的URI,Android没有定义过的,为

myapp://com.tianyuan.datas,然后在Manifest文件中,对第二个Activity设置的IntentFilter为

      <intent-filter >
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <data android:scheme="myapp"/>
       </intent-filter>

这里只是设置了android:cheme的类型为一个Android从来没有定义过的类型,为"myapp"

代码:

public class MainActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        Button slip = (Button)findViewById(R.id.slip);
        slip.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
// TODO Auto-generated method stub

Uri uri = Uri.parse("myapp://com.tianyuan.datas");
Intent intent = new Intent();
intent.setData(uri);
intent.setAction(Intent.ACTION_VIEW);
startActivity(intent);
}
});
    }
}


activity_test:

public class activity_test extends Activity{


@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.testlayout);
}


}

Manifest文件:

<application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/title_activity_main" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />


                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity 
            android:name=".activity_test">
            <intent-filter >
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <data android:scheme="myapp"/>
            </intent-filter>
        </activity>
    </application>


结论:

上面的代码能过正确执行,说明了URI和Intent的确有一定联系,并且和Android内部的注册机制有关系。

Intent携带能够选择执行程序的URI,通过URI去注册机构寻找要执行的App,IntentFilter的信息放在了

注册机构里面,然后通过对URI和IntentFilter的信息进行比对,选择适当的App去执行URI和Intent中的数据。


以上是我自己猜测的Android的执行机构,没有用代码进行验证,不过这么想也没什么问题。


望 各位大牛指点批注,指引学习!


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值