有可能说到URL Scheme,很多还不太了解,甚至根本就不知道是什么东西。下面我们来好好认识一下它。
URL Scheme 是Android中一种页面内跳转的协议; 也可以被称为 URLRouter。它可以通过匹配 URI 去打开对应的Activity。就类似网页中,我们打开网页的方式去打开一个Activity。当然这是一种通过Intent隐式打开Activity的方式。现在模块化开发时,遇到特别复杂的模块组合,也有可能会用的它。
我们通过Scheme启动Activity时,是通过Intent.setData(Uri uri)启动Activity的。它需要的参数是一个URI。所以URL Scheme的协议的格式其实就是 URI的格式。
一个完整的Scheme的协议格式由 scheme、userInfo、host、port、path、query和fragment 组成。结构如下:
scheme://userInfo@host:port/path?query#fragment
其中 scheme://
是固定的格式。userInfo@
可以省略,host
是必须的。port 、query 和 fragment
也是可以省略的。
其中scheme既可以是Android已经定义好的协议,也可使用我们自定义的。Android 常见的scheme 协议有:content 、file、http 等。如果我们自定义协议就可以随意使用一些字符串来限定协议。当然最好是有一定含义的字符串。如下面的协议:
xieyi://[email protected]:8080/userDetails?param1=参数1¶m2=参数2#片段"
#
后的所有的内容都看成一个片段。首先我们可以确定的是,通过 scheme启动Activity 是通过Intent 隐式启动的;隐式调用需要在 AndroidManifest 里的标签下添加 来设置;使用scheme的时候需要在 标签里面设置 我们需要的 URL Scheme 信息。
接下来我们介绍一下 里的关于scheme的各个标签的含义:
常用的几个
//对应的就是我们上面提到 host
android:host="me.ricky.sample"
//路径,必须以 / 开头
android:path="/path"
//端口
android:port="8080"
//协议
android:scheme="xieyi"
不太常用的几个
//路径的前缀,必须以 / 开头
android:pathPrefix="/path"
//匹配路径的正则
android:pathPattern=".*path"
了解上面的这些,那scheme到底应该怎么用呢?
首先我们新建一个Activity,命名为 SchemeActivity;然后在AndroidManifest里作如下配置:
<activity android:name=".SchemeActivity">
<intent-filter>
<action android:name="me.ricky.sample.action"/>
<category android:name="android.intent.category.DEFAULT"/>
<data
android:host="me.ricky.sample"
android:scheme="xieyi"
/>
intent-filter>
activity>
这里我们设置最简单的Scheme设置。如果我们要启动该Activity,应该怎么办呢?
intent.setAction("me.ricky.sample.action");
startActivity(intent);
上面的代码是通过隐式的Action去启动Activity,如果你只是这样简单去启动Activity,一定是不行的,因为我们设置标签,系统无法匹配到特定的Activity,那它就会报出以下的异常:
android.content.ActivityNotFoundException: No Activity found to handle Intent { act=me.ricky.sample.action }
如果我们想完全匹配那么就需要给设置该action的Activity,指定data数据:
Uri uri = Uri.parse("xieyi://me.ricky.sample");
intent.setAction("me.ricky.sample.action");
intent.setData(uri);
startActivity(intent);
xieyi://me.ricky.sample
其中的scheme就是 xieyi
对应的是我们在 AndroidManifest中设定的android:scheme="xieyi";
;me.ricky.sample
对应的是我们在 AndroidManifest中设定的 android:host="me.ricky.sample"
这样就能匹配我们设定Activity了。
当然这是最简单的Scheme的使用。没有什么难度。我们启动Activity时,完全匹配了我们在AndroidManifest里设置的scheme信息。
xieyi://[email protected]:8080/userDetails?param1=参数1¶m2=参数2#片段"
这URI 几乎包含了URI的所有的组成。其中包含了一些参数。那么我们如何在被启动的Activity中获取这些参数呢?它的用法跟通过Intent获取额外的参数是一样的。只不过我们是通过解析URI 来获取。
如果我们将这个URI作为参数启动Activity,
Intent intent = new Intent();
Uri uri = Uri.parse("xieyi://[email protected]:8080/userDetails?param1=参数1¶m2=参数2#片段");
intent.setAction("me.ricky.sample.action");
intent.setData(uri);
startActivity(intent);
那我们可以在被启动的SchemeActivity的onCreate()方法中通过下面的方法获取URI的各个部分。
Uri uri = getIntent().getData();
String host = uri.getHost();
String authority = uri.getAuthority();//authority包括host和port两部分
String scheme = uri.getScheme();
String query = uri.getQuery();//获取的是 “param1=参数1¶m2=参数2”
String path = uri.getPath();
int port = uri.getPort();
String frag = uri.getFragment();
String userInfo = uri.getUserInfo();
String param1 = uri.getQueryParameter("param1");
String param2 = uri.getQueryParameter("param2");
这样我们就可以通过scheme 来进行Activity之间的 信息传递了。
除了上提到的 AndroidManifest中的设置数据和我们在Intent.setData(Uri uri);设定的一一对应之外。还有几个简单的依赖规则。
当我们通过Intent将URI参数与中的标签中指定的URI对比时,系统会只对比标签指定的部分。
需要注意,这里path设定时还可以通过 下面的属性设置
//路径的前缀,必须以 / 开头
android:pathPrefix="/path"
//匹配路径的正则
android:pathPattern=".*path"
pathPrefix指定是前缀,只要我们设定的path的前缀pathPrefix设置一致就可以通过path打开特定的Activity;同理下面的pathPattern;是通过正则来匹配path,只要能匹配通过就可以通过path打开特定的Activity。
如果要通过scheme跨应用启动Activity,其实也很简单:
Intent intent = new Intent();
Uri uri = Uri.parse("xieyi://[email protected]:8080/userDetails?param1=参数1¶m2=参数2#片段");
intent.setAction("me.ricky.sample.action");
intent.setData(uri);
startActivity(intent);
跟上面同一个应用中启动Activity时一样的,这里需要注意的是:如果不希望启动的Activity跟当前的Activity在同一个栈了,需要加上下面的代码:
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
<a href="xieyi://[email protected]:8080/userDetails">查看用户详情a>
注意: 在网页中启动Activity 未经本人验证,只是从其它地方看到的。
另:一般隐式启动Activity,需要我们去验证一下我们的Intent指定的Activity是否存在。如果存在就可以放心的去启动相应的Activity了。
private boolean checkIntent(Intent intent) {
PackageManager manager = getPackageManager();
List resolveInfos = manager.queryIntentActivities(intent, PackageManager.MATCH_ALL);
System.out.println(resolveInfos);
return !resolveInfos.isEmpty();
}