Android 中通过 URI 实现 Web 页面调用本地 App

HTML 5 和本地 App 各有所长,现在公司的项目中也大量采用 HTML 5 做活动页面,这样本地代码和 HTML 5 的交互就是必须的。

说到 Android 端 Java 代码和 Web 端 HTML / JS 代码的交互,你可能最先想到的就是 WebView 的这两个方法:

  • loadUrl(String url);

  • addJavascriptInterface(Object object, String name);

前者可以实现 native 代码直接执行 JS 代码,而后者可以将 native 代码实现的接口暴露给 Web 页面,这样 Web 页面可以像调用普通 JS function 一样调用这个 native 接口。

但其实还有一种更直接的方式:那就是 URL,准确的说是 URI。

因为 shouldOverrideUrlLoading(WebView view, String url) 等回调方法本身就可以看作 Web 页面通过 URL 和本地应用进行数据交互,因此只要 Web 页面按照 URI 规范将数据传递过来,本地 App 就能在相关回调方法中调用相关 URI 的 API 解析数据,实现数据交互。

事实上,Android 很多内部组件正是通过 URI 传递数据的,特别是在调用系统服务和使用 ContentProvider 时经常用到。例如:

//调用地图应用显示地理位置
Uri uri = Uri.parse("geo:38.899533,-77.036476");
Intent it = new Intent(Intent.Action_VIEW, uri);
startActivity(it);
//调用拨号程序
Uri uri = Uri.parse("tel:18616612345");
Intent it = new Intent(Intent.ACTION_DIAL, uri);   
startActivity(it);

假设 Web 页面采用一个超链接将数据以 URI 形式传递过来:

<a href="MY_SCHEME:\\MY_HOST:MY_PORT\MY_PATH\?arg0=0&arg1=1">Open App</a>

则 Android 端数据解析代码如下:

webView.setWebViewClient(new WebViewClient(){
	@Override
	public boolean shouldOverrideUrlLoading(WebView view, String url) {
		Uri uri=Uri.parse(url);
        	if(uri.getScheme().equals(MY_SCHEME)
        		&& uri.getHost().equals(MY_HOST)
        		&& uri.getPort() == MY_PORT
        		&& uri.getPath().equals(MY_PATH)){
        		String arg0 = uri.getQueryParameter("arg0");
        		String arg1 = uri.getQueryParameter("arg1");
        		//TODO
        	} else {
        		view.loadUrl(url);
        	}
		return true;
	}
});

以上是在 WebView 中加载 Web 页面的情况,如果要使外部浏览器中的 Web 页面也能够调用,则需要在 AndroidManifest.xml 中为指定 Activity 注册 intent-filter :

<activity
	android:name=".activity.UriActivity"
	android:exported="true">
	<intent-filter>
		<action android:name="android.intent.action.VIEW"/>
		<category android:name="android.intent.category.DEFAULT"/>
		<category android:name="android.intent.category.BROWSABLE"/>
		<data android:scheme="MY_SCHEME"
			android:host="MY_HOST"
			android:port="MY_PORT"
			android:path="MY_PATH"/>
	</intent-filter>
</activity>

对应的在处理 URI 的 Activity 中只需要调用 getIntent().getData() 方法即可读取 URI 数据。