摘要:通过对前面 记录:Intent、Intent过滤器和通用Intent 摘抄学习中,在启用第三方程序中的 Uri,是两眼一抹黑。不知道具体是什么东西。这里还是摘抄一遍去强迫学习吧! 由于摘抄中之多名博主,其地址为下面的参考链接
URI
1.概念
URI(Uniform Resource Identifier,统一资源标识符)以字符串来表示某种资源的统一资源标识。
格式为:[scheme:] scheme-specific-part[#fragment]
[scheme:]组件
,URI的名称空间标识
scheme-specific-part组件
,用于标识资源,内部格式由具体的scheme
来决定
[#fragment]
,井号(#)作为fragment组件的其实字符,fragment组件则用于聚焦到资源的某个部分
2.绝对URI和相对URI
绝对URI:以 scheme 组件起始的完整格式,如 http://fsjohnhuang.cnblogs.com。表示以对标识出现的环境无依赖的方式引用资源。
相对URI:不以 scheme组件的起始的非完整格式,如 fsjohnhuang.cnblogs.com。表以对依赖标识出现的环境有依赖的方式引用资源。
实例:当前页面的地址为 http://fsjohnuang.cnblogs.com
// html snippet
<a id="test" href="test.com">test.com</a>
// js snippet
<script>
var href = document.getElementById('test').href
console.log(href) // 显示 http://test.com
</script>
3.不透明URI和分成URI
不透明URI:scheme-specific-part组件不是以正斜杠/
起始,如 mailto:fsjohnhuang@xxx.com。由于不透明URI无需要进行分解操作,因此不会对scheme-specificpart组件进行有效性验证
分层URI:scheme-spcific-part组件是以/
起始的,如 http://fsjohnhuang.com。
[//authority] | 表示授权机构组件,以一对正斜杠(//)起始,可以基于主机(server-based)或注册(registry-based),基于注册相对于基于主机额数目较少。并以正斜杠、问号或无后续字符作为 authority 组件的结果。 而 authority 组件的具体格式 “[userinfo@]host[:port]“ “[userinfo@]“ ,用户账号 “host“,主机IP或域名 “[:port]“,通信端口号,若省略则使用相应的 scheme 组件的默认端口号 示例:“http://fsjohnhuang@github.com:80/ “ |
[path] | path组件表示根据 authority 组件识别资源的位置。path组件有一系列的路径片段(path segment)构成,路径片段以正斜杠(/)作为分隔符。若第一路径片段以正斜杠(/)起始则为绝对路径,否则称为相对路径 |
[?query] | query组件用于识别要传递给资源的数据,用于影响资源额响应的行为 |
4.标准化(Normalization)、解析化(Resolution)和(Relativization)
标准化(Normalization):其实就是去除 path 组件中当前层(.)和上一层(..)这些冗余字符。如 z/../y 标准化为 y
解析化(Resolution):以URI A作为基本 URI来和另一个 URI一同解析为一个新的标准 URI。如 http://fsjohnhuang.com 作为基本URI和 z/../y 一同解析成 http://fsjohnhuang.com/y
相对化(Relativization):相对比其实就是解析化的相反操作。如 http://fsjohnhuang.com作为基本URI和 http://fsjohnhuang.com/z 来做相对化操作得到 /z
到这里我们可能会认为这不就跟平常的网站地址一样吗?为啥大家叫网站地址为URL,而不是URI呢?
互联网之父 Tim Berners-Lee引入用于识别、定位和命名互联网资源的途径 —— URI、URL和URN。三者彼此关联URI的范畴位于体系的顶层,URL和URN的范畴位于体系的底层。
- URI:Uniform Resource Identifier,统一资源标识符
- URL:Uniform Resource Locator,统一资源定位符
- URN:Uniform Resource Name,统一资源名称
上图可知 URL和URN必须是URI,但URI不一定是URL或URN。
URI仅仅是资源名称而已,知道了URI最多就是知道有这么一个名称的资源罢了,至于如何获取(与资源做交互)则是毫无头绪(不能定位或读取/写入资源),而这个资源名称是永久持有还是暂时持有也没有相应的规定,于是就有了 URL和URN两个子集。
首先URL和URN均继承了URL格式中各组件。然后在这个基础上进行了各自的扩展?
URL
URL = URI(scheme组件为部件已知的网络协议的URI子集)+与scheme组件标识的网络协议匹配处理器(URL Protocol Handler)
- URI的scheme组件在URL中称为protocol组件,一般 http、https、ftps、data、jar 等
- URL Protocol Handler 则是一种资源定位器和根据协议建立的约束规则与资源通信的读写机制,用于定位、读写资源
如:安装迅雷后点击ed2k的迅雷种子时则会自动打开迅雷下载界面,这为什么了
迅雷种子就是资源,而ed2k就是资源URL的protocol组件,而迅雷就是URL Protocol Handler。而protocol组件与URL Protocol Handler间的映射关系在windows下则存放在注册表中,而Ubuntu中存放在/usr/share/applications/.desktop中。
URN
URN = URI(scheme组件为部分已知的网络协议的URI子集) + 与scheme组件标识的网络协议匹配的协议处理器(URL Protocol Handler)+ 持久性/地址无关性
URN用于持久化地标识 Internet资源,即使资源已经不存在或不可用时依然保持不变,通过实际的持久性策略实现资源发生变化也不用修改URI(地址无关性)。然而通过持久性策略还可以实现一条URN对应N条URI,如 BT中的磁力链接(Magnet URI scheme)
以上说了这么多,是时候总结一下URI、URL和URN的关联和区别!!。(感叹一句,羡慕英语认真学的,详情介绍放在类上面。可惜看不懂! ixi,不过还好感谢各位大大的无私。可以直接看中文摘抄)
- 首先URI是基础,URL和URN均属于URI
- URL = URI(scheme组件为部分已知的网络协议的URI子集)+ 与scheme组件标识的网络协议匹配的协议处理器(URL Protocol Handler)
- URN突出的是持久化,通过具体的持久化策略实现地址无关性。 URN = URI(scheme组件为部分已知的网络协议的URI子集) + 与scheme组件标识的网络协议匹配的协议处理器(URL Protocol Handler) + 持久性/地址无关性
5.java.net.URI类和java.net.URL类
Java当中对URI和URL单独提供 java.net.URI和java.net.URL两个操作类
java.net.URI中主要提供以下功能:
1.验证URI格式
构造函数URI(String str),若格式不正确则抛出URISyntaxException URI.create(String str),若格式不正确则抛出unchecked的IllegalArgumentException
2.提取URI各组件
Component | Example value | |
---|---|---|
getScheme() | http | |
getSchemeSpecificPart() | //username:password@host:8080/directory/file?query#fragment | |
getAuthority() | username:password@host:8080 | |
getUserInfo() | username:password | |
getHost() | host | |
getPort() | 8080 | |
getPath() | /directory/file | |
getQuery() | query | |
getFragment() | fragment |
3.标准化、解析化和相对化
normalize(),,返回符合标准的URI新对象。如`x/y/../z/./q`->`x/z/q`
resolve(String/URI uri),进行反向解析,以入参作为相对URI,以resolve方法所属对象作为基本URI来得到一个新的标准的URI对象
relativize(URI uri),相对化操作,就是获取URI中的相对URI
//实例
URI absolute = new URI("http://android.com/");
URI relative = new URI("robots.txt");
URI resolved = new URI("http://android.com/robots.txt");
// print "http://android.com/robots.txt"
System.out.println(absolute.resolve(relative));
// print "robots.txt"
System.out.println(absolute.relativize(resolved));
4.将URI转成URL
URI#toURL(),将URI转换为URL。
注意:不含任何搜索和读写资源的操作
java.net.URL中主要提供以下功能: URL类是依据URL Protocol Handler 类处理URL字符串,若没有相应的协议处理器则抛 MalformedURLException
内置提供了 http、https、ftp、file和jar 协议的URL Protocol Handler。而其它协议的处理器则需要开发者自行继承 URLStreamHandler来实现了,处理流程如下:
- 查看处理器缓存HashTable handlers,若存在缓存项则直接返回
- 若缓存中没有则查看是否有URLStreamHandlerFactory实例,若存在则调用其createURLStreamHandler(String protocol)。默认情况下URLStreamHandlerFactory实例为null
- 若2中返回null,则通过系统属性java.protocol.handler.pkgs获取以|分隔的包名列表,然后逐一检查是否存在继承了URLStreamHandler的<package>.<protocol>.Handler类,有则返回,无则继续遍历
- 若3中遍历失败,则检查是否存在继承了URLStreamHandler的<system default package>.<protocol>.Handler的内置类
- 上述均失败则抛出MalformedURLException。
class Main{
static void main(String[] args) throws IOException, MalformedURLException{
String path1 = "d:\\t.txt"
, path2 = "file:/d:/t.txt";
Main main = new Main();
main.readByFr(path1);
main.readByUrl(path2);
}
// 通过FileInputStream的写法
void readByFr(String path) throws IOException{
FileReader fr = new FileReader(path);
try{
int buf;
while (-1 != buf){
buf = fr.read();
System.out.print((char)buf);
}
}finally{
fr.close();
}
}
// 通过URL的写法
void readByURL(String path) throws MalformedURLException, IOException{
URL url = new URL(path);
InputStreamReader reader = new InputStreamReader(url.openStream());
try{
int buf;
while (-1 != buf){
buf = reader.read();
System.out.print((char)buf);
}
}
finally{
reader.close();
}
}
}
Uri
一、URI和Uri
上面介绍了 Java 的URI。那么,Android中Uri有和它有什么异同了 ?
- 所属的包不同。URI 位置在 java.net.URI,是一个java提供的类。而Uri位置在 android.net.Uri 。是由Android提供的一个类,所以初步可以判断,Uri是URI的“扩展”以适应Android系统的需要
- 作用不同。URI 类代表了一个 URL(这个URI不是类,而是其本来的意义:通用资源标识符——Uniform Resource Identifier)实例。Uri类是一个不可以改变的URI引用,包括一个URI和一些碎片,URI跟在 “#”后面。建立并且转换URI引用。而且Uri类对无效的行为不敏感,对于无效的输入没有定义相应的行为,如果没有另外制定,它将返回垃圾而不是跑出异常
二、Uri结构
(1)、基本形式:
[scheme:]scheme-specific-part[#fragment]
这里分为三个部分
scheme、scheme-specific-part、fragment
(2)、进一步划分
如果进一步划分是这样的
[scheme:][//authority][path][?query][#fragment]
其中有下面几个规则:
- path可以有对个,每个用“/”连接,比如
scheme://authority/path1/path2/path3?query#fragment - query参数可以带有对应的值,也可以不带,如果带对应的值用 = 表示,如:
scheme://authority/path1/path2/path3?id = 1&name = mingming&old#fragment
这里有三个参数:- 参数1:id,其值是 1
- 参数2:name,其值是:mingming
- 参数3:old,没有对它赋值,所以它的值是null
- 在Android中,除了scheme、authority是必须要有的,其他的几个 path、query、fragment,它们每一个可以选择性的要或者不要,但顺序不能变,比如:
- 其中“paht”课不要:scheme://authority?query#fragment
- 其中“path”和“query”可都不要:scheme://authority#fragment
- 其中“query”和“fragment”可都不要:scheme://authority/path
- “path”,”query”,”fragment”都不要:scheme://authority
- 等等…..
(3)、终极划分
其中authority又可以分为 host:port的形式,机再次划分后是这样的:
[scheme:][//host:port][path][?query][#fragment]
所以这是划分最细的形式,其中host:port用冒号分隔,冒号前面是 host,冒号后面是port
三、示例
经过上面的讲解,想必大家的Uri的结构有所了解,下面我们就实例看看各部分的识别方式
[scheme:]scheme-specific-part[#fragment]
[scheme:][//authority][path][?query][#fragment]
[scheme:][//host:port][path][?query][#fragment]
先列出这三种Uri形式,好让大家对比;
先针对下一个Uri字符串来匹配一下各个部分:
http://www.java2s.com:8080/yourpath/fileName.htm?stove=10&path=32&id=4#harvic
- scheme:匹配上面的两个Uri标准形式后,很容易看出,在前面部分是scheme。所以这个Uri字符串的的scheme是:http
- scheme-specific-part:很容易看出scheme-specific-part是包含在scheme和fragment之间的部分,也就是包括第二部分的[//authority][path][?query]这几个小部分,所在这个Uri字符串的scheme-specific-part是://www.java2s.com:8080/yourpath/fileName.htm?stove=10&path=32&id=4 ,注意要带上//,因为除了[scheme:]和[#fragment]部分全部都是scheme-specific-part,当然包括最前面的//
- fragment:这个是更容易看出的,因为在最后用#分隔的部分就是fragment,所以这个Uri的fragment是:harvic
下面就是对scheme-specific-part进行拆分了
在scheme-specific-part中,最前端的部分就是authority,?后面的部分是query,中间的部分就是path - authority:很容易看出scheme-specific-part最新端的部分是:www.java2s.com:8080
- query:在scheme-specific-part中,?后的部分为:stove=10&path=32&id=4
- path:在query:在scheme-specific-part中,除了authority和query其余都是path的部分:/yourpath/fileName.htm
又由于authority又一步可以划分为host:port形式,其中host:port用冒号分隔,冒号前的是host,冒号后的是port,所以 - host:www.java2s.com
- port:8080
四、代码提取
上面我们通过实例讲解了肉眼识别Uri更部分的方式,但在代码中又要怎样提取呢。下面就看看Uri中提取各部分的接口,依然以上面的Uri字符串为例:
http://www.java2s.com:8080/yourpath/fileName.htm?stove=10&path=32&id=4#harvic
- getScheme() :获取Uri中的scheme字符串部分,在这里即,http
- getSchemeSpecificPart():获取Uri中的scheme-specific-part:部分,这里是://www.java2s.com:8080/yourpath/fileName.htm?
- getFragment():获取Uri中的Fragment部分,即harvic
- getAuthority():获取Uri中Authority部分,即www.java2s.com:8080
- getPath():获取Uri中path部分,即/yourpath/fileName.htm
- getQuery():获取Uri中的query部分,即stove=10&path=32&id=4
- getHost():获取Authority中的Host字符串,即www.java2s.com
- getPost():获取Authority中的Port字符串,即8080
另外还有两个常用的:getPathSegments()、getQueryParameter(String key)
- List<String> getPathSegments():上面我们的getPath()是把path部分整个获取下来:/yourpath/fileName.htm,getPathSegments()的作用就是依次提取出Path的各个部分的字符串,以字符串数组的形式输出。以上面的Uri为例:
String mUriStr = "http://www.java2s.com:8080/yourpath/fileName.htm?stove=10&path=32&id=4#harvic";
Uri mUri = Uri.parse(mUriStr);
List<String> pathSegList = mUri.getPathSegments();
for (String pathItem:pathSegList){
Log.d("qijian","pathSegItem:"+pathItem);
}
打出来的列表为:
- getQueryParameter(String key):在上面我们通过getQuery()获取整个query字段:stove=10&path=32&id=4,getQueryParameter(String key)作用就是通过传进去path中某个Key的字符串,返回他对应的值。
-
注意注意,我稍微更改了下字符串,把query中id的值去掉了!!!!!然后看看通过getQueryParameter(“id”)获取它的值会得到什么!String mUriStr = "http://www.java2s.com:8080/yourpath/fileName.htm?stove=10&path=32&id#harvic"; mUri = Uri.parse(mUriStr); Log.d(tag,"getQueryParameter(\"stove\"):"+mUri.getQueryParameter("stove")); Log.d(tag,"getQueryParameter(\"id\"):"+mUri.getQueryParameter("id"));
结果如下:可以看到,在path中,即使针对某一个KEY不对它赋值是允许的,但在利用getQueryParameter()获取该KEY对应的值时,获取到的是null;
通过自定义Uri外部启动APP与Notification启动
一、自定义Uri与外部启动
1、概述
前面我们讲了Uri的结构,在这里,我们将看看如何利用自定义的URI来启动我的的应用。 有时,我们要通过外部Uri链接来启动我们的应用,主要是通过Uri隐式Intent匹配的方式:
这里通过隐式Intent匹配来启动应用,在这里我们自定义了一个Uri结构:qijian://test.uri.activity?action=1 我们的应用在隐式匹配Intent时,使用的语法为:Uri uri = Uri.parse("qijian://test.uri.activity?action=1"); Intent intent = new Intent("android.qijian.schemeurl.activity"); intent.setData(uri); startActivity(intent);
<activity android:name=".SecondActivity" android:label="@string/title_activity_second"> <intent-filter> <action android:name="android.qijian.schemeurl.activity" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="qijian" android:host="test.uri.activity" /> </intent-filter> </activity>
我们这里在匹配Intent时,使用指定scheme和host来精确匹配过来的Uri,以防止同名scheme就能启动我们的activity,即本来可能要启人家应用,确我们也横插一脚,用户体验很不好,一定要做到精确匹配,以防大家URI一样出现多个应用让用户选择的情况。
这样,第三方就能通过这个Uri来匿名启动我们的Activity了。
2、实例
(1)、新建用于外部启动的Activity
首先,我们先建一个应用,命名为:SchemeURL,在这个应用中我们新建一个Activity命名为:secondActivity,其XML代码如下 (这个Activity是为了在外部启动,为了标识这个Activity是这个应用的,把背景色改成了黄色,文字改上了“SchemeURL 的Activity”):
(2)、在AndroidManifest.xml中添加Intent-filter过滤代码: 在SecondAcitivity中添加上Intent-filter用于隐式启动Intent,由于我们定义的Uri格式为:qijian://test.uri.activity?action=1,所以我们固定schemeurl和host,通过query来传递参数即可; 除了Uri匹配,我这里还添加上了Action:“android.qijian.schemeurl.activity”,所以我们在第三方隐式匹配时要同时通过Uri和action来同时匹配才能通过这里的Intent-filter<RelativeLayout 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:background="#ffff00" tools:context="com.harvic.com.schemeurl.SecondActivity"> <TextView android:text="SchemeURL 的Activity" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </RelativeLayout>
(3)新建应用:UseSchemeURL,通过自定义的Uri来从外部调起SecondActivity 这个应用的外观是这个样子的:当点击按钮时,调起SchemeURL的SecondActivity:<activity android:name=".SecondActivity" android:label="@string/title_activity_second"> <intent-filter> <action android:name="android.qijian.schemeurl.activity" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="qijian" android:host="test.uri.activity" /> </intent-filter> </activity>
代码为:
(4)、进阶:通过Uri来传递参数,并处理 在上面,我们已经能够通过Uri来进入我们的应用,我们上面只是固定了Uri的scheme部分和host部分,对于其它部分并没有固定,所以我们可以通过其它部分来传递参数,进而完成指定的功能:比较进入指定的页面或做出指定的操作,等 比如,我们在SchemeURL中,对Uri进行接收,并将结果显示出来:Button btn = (Button) findViewById(R.id.btn_try); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Uri uri = Uri.parse("qijian://test.uri.activity?action=1"); Intent intent = new Intent("android.qijian.schemeurl.activity"); intent.setData(uri); startActivity(intent); } });
结果截图如下:public class SecondActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); Intent intent = getIntent(); if (null != intent) { Uri uri = intent.getData(); if (uri == null) { return; } String acionData = uri.getQueryParameter("action"); TextView tv = (TextView)findViewById(R.id.qijian_test_tv); tv.append("\n传过来的action值为:" + acionData); } } }
二、特殊应用:Notification与应用启动
有关通过Uri启动APP的经典应用,应当数通过推送消息启动我们应用的指定页面或做出特定的操作了。这部分,我们就看看如何通过推送的通知栏消息来进入我们的应用。 效果如下:- 首先:SchemeURL工程代码都不变,我们依然通过隐式匹配Intent来启动SecondActivity.
- 然后:在UseSchemeURL工程中新加一个按钮,当点击时,发送Notification通知,点击跳转到SchemeURL工程的SecondActivity
其中发送Notification代码如下:
private void pushNotify() { NotificationManager notifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); NotificationCompat.Builder builder; builder = new NotificationCompat.Builder(this); builder.setSmallIcon(R.drawable.ic_launcher) .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher)) .setContentTitle("harvic") .setContentText("test schemeURL") .setTicker("有新消息") .setOngoing(false) .setWhen(System.currentTimeMillis()) .setPriority(Notification.PRIORITY_DEFAULT) .setAutoCancel(true); Uri uri = Uri.parse("qijian://test.uri.activity?action=1"); Intent intent = new Intent("android.qijian.schemeurl.activity"); intent.setData(uri); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); builder.setContentIntent(pendingIntent); Notification notification = builder.build(); notifyManager.notify(1111, notification); }
最重要的是PendingIntent的封装!
即如下代码:Uri uri = Uri.parse("qijian://test.uri.activity?action=1"); Intent intent = new Intent("android.qijian.schemeurl.activity"); intent.setData(uri); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
在这里大家也可以看到,这是利用隐式Intent匹配的方式来启动我们的Activity的!所有的APP在通知栏进入到自己的应用,都是通过这个方式来的!>
三、有关path、pathPrefix、pathPattern 之间的区别
细心的同学可能在AndroidManifest.xml中已经发现,Intent-filter的data域除了scheme、host、port这些已知的还有三个参数我们没用过——path、pathPrefix、pathPattern;它们也是用来隐式匹配Intent的,即:<activity android:name=".SecondActivity" android:label="@string/title_activity_second"> <intent-filter> <action android:name="android.qijian.schemeurl.activity" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="qijian" android:host="test.uri.activity" android:path="" android:pathPrefix="" android:pathPattern=""/> </intent-filter> </activity>
这里主要说的区别是 path、pathPrefix、pathPattern 之间的区别:
- path: 用来匹配完整的路径,如:http://example.com/blog/abc.html,这里将 path 设置为 /blog/abc.html 才能够进行匹配;
- pathPrefix: 用来匹配路径的开头部分,拿上面的 Uri 来说,这里将 pathPrefix 设置为 /blog 就能进行匹配了;
- pathPattern: 用表达式来匹配整个路径,这里需要说下匹配符号与转义。
匹配符号:
- “*” 用来匹配0次或更多,如:“a” 可以匹配“a”、“aa”、“aaa”…
- “.” 用来匹配任意字符,如:“.” 可以匹配“a”、“b”,“c”…
- 因此 “.*” 就是用来匹配任意字符0次或更多,如:“.*html” 可以匹配 “abchtml”、“chtml”,“html”,“sdf.html”…
转义:
因为当读取 Xml 的时候,“\” 是被当作转义字符的(当它被用作 pathPattern 转义之前),因此这里需要两次转义,读取 Xml 是一次,在 pathPattern 中使用又是一次。如:“” 这个字符就应该写成 “\”,“\” 这个字符就应该写成 “\”。
样例:匹配 http 以 “.pdf” 结尾的路径
如果我们想要匹配 http 以 “.pdf” 结尾的路径,使得别的程序想要打开网络 pdf 时,用户能够可以选择我们的程序进行下载查看
我们可以将 scheme 设置为 “http”,pathPattern 设置为 “.*.pdf”,整个 intent-filter 设置为:<action android:name="android.intent.action.VIEW"></action> <category android:name="android.intent.category.DEFAULT"></category> <data android:scheme="http" android:pathPattern=".*\\.pdf"></data> </intent-filter>
如果你只想处理某个站点的 pdf,那么在 data 标签里增加 android:host=”yoursite.com” 则只会匹配 http://yoursite.com/xxx/xxx.pdf,但这不会匹配 www.yoursite.com,如果你也想匹配这个站点的话,你就需要再添加一个 data 标签,除了 android:host 改为 “www.yoursite.com” 其他都一样。
四、特殊:如何从网页中通过Uri启动我们的应用
如果想要从网页中点击一个链接跳转到我们的应用,那除了Intent-filter中的各种匹配工作,还应该加上一个属性:<category android:name="android.intent.category.BROWSABLE"/>
即,以我们的SecondActivity为例,它完整的AndroidManifest.xml的配置方式为:
<activity android:name=".SecondActivity" android:label="@string/title_activity_second"> <intent-filter> <action android:name="android.qijian.schemeurl.activity" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE"/> <data android:scheme="qijian" android:host="test.uri.activity"/> </intent-filter> </activity>
Android中Uri和Path之间的转换
1.原因
调用系统拍照应用,拍照后要保存图片,那么我们需要指定一个存储图片路径的Uri。这就涉及到如何将file path转换为Uri。有时候我们还需要根据照片的路径得到照片的media Uri,那么又该如何转换呢?Android Uri to Path
现在遇到的常规Uri有两种:- 媒体文件的Uri是content://, 表示这是一个数据库数据。去数据库查询正常返回。
- 其他的文件Uri是file://, 表示这个是一个文件。这个uri是通过Uri.fromFile(File file)方法生成。
File Uri To Path
这个转换相对比较简单,我们可以直接利用Android SDK提供的Uri.getPath()方法来获取相应的路径,然后利用Java IO来获取输入流,创建Bitmap。如果想直接通过File Uri获取输入流,我们可以通过调用ContentResolves.openInputStream(Uri uri)返回得到输入流。bitmap = BitmapFactory.decodeStream( getContentResolver().openInputStream( GetImageUri.getImageStreamFromExternal("Screenshots/Screenshot.png")) );
这里的GetImageUri.getImageStreamFromExternal是我自己写的一个工具类:
public static Uri getImageStreamFromExternal(String imageName) { File externalPubPath = Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_PICTURES ); File picPath = new File(externalPubPath, imageName); Uri uri = null; if(picPath.exists()) { uri = Uri.fromFile(picPath); } return uri; }
Android Path To Uri | File Path To File Uri
public static Uri getImageStreamFromExternal(String imageName) { File externalPubPath = Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_PICTURES ); File picPath = new File(externalPubPath, imageName); Uri uri = null; if(picPath.exists()) { uri = Uri.fromFile(picPath); } return uri; }
这里我们看到,最核心的部分就是利用Uri.fromFile()方法获取到指定路径的File Uri。
File Path To Media Uriublic static Uri getMediaUriFromPath(Context context, String path) { Uri mediaUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; Cursor cursor = context.getContentResolver().query(mediaUri, null, MediaStore.Images.Media.DISPLAY_NAME + "= ?", new String[] {path.substring(path.lastIndexOf("/") + 1)}, null); Uri uri = null; if(cursor.moveToFirst()) { uri = ContentUris.withAppendedId(mediaUri, cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media._ID))); } cursor.close(); return uri; }
代码分析:首先我们获取到相册数据库表的Uri,mediaUri。然后我们利用ContentResolver.query()方法,并且selectionArgs传入根据指定路径获得的文件名来得到一个cursor对象。然后通过这个cursor对象我们获取到指定文件的ID。最后通过ContentUri组合mediaUri和图片的Id,得到最终的Media Uri。
参考资料