前言
知乎在手机浏览器打开,会有个 App 内打开的按钮,点击直接打开且跳转到该详情页,是不是有点神奇,是如何做到的呢?
效果预览

Uri Scheme
配置 intent-filter
AndroidManifest.xml
1<activity android:name=".MainActivity"> 2 <!-- 需要添加下面的intent-filter配置 --> 3 <intent-filter> 4 <action android:name="android.intent.action.VIEW" /> 5 <category android:name="android.intent.category.DEFAULT" /> 6 <category android:name="android.intent.category.BROWSABLE" /> 7 <data 8 android:host="myhost" 9 android:path="/main" 10 android:port="1024" 11 android:scheme="myscheme" /> 12 </intent-filter> 13</activity>
测试网页
main 下新建 assets 文件,写了简单的 Html 网页用于 WebView 展示,来进行测试。
index.html:
1<html> 2<head> 3 <meta charset="UTF-8"> 4</head> 5<body> 6<h1>这是一个 WebView</h1> 7 8<a href="market://details?id=com.tencent.mm">open app with market</a> 9<br/> 10<br/> 11 12<a href="myscheme://myhost:1024/main?key1=value1&key2=value2">open app with Uri Scheme</a> 13 14<br/> 15<br/> 16 17 18</body> 19</html>
Web View 加载:
webView.loadUrl("file:///android_asset/index.html");
目标页面
接受参数,做相应的处理。
1Intent intent = getIntent(); 2if (null != intent && null != intent.getData()) { 3 // uri 就相当于 web 页面中的链接 4 Uri uri = intent.getData(); 5 Log.e(TAG, "uri=" +uri); 6 String scheme = uri.getScheme(); 7 String host = uri.getHost(); 8 int port = uri.getPort(); 9 String path = uri.getPath(); 10 String key1 = uri.getQueryParameter("key1"); 11 String key2 = uri.getQueryParameter("key2"); 12 Log.e(TAG, "scheme=" + scheme + ",host=" + host 13 + ",port=" + port + ",path=" + path 14 + ",query=" + uri.getQuery() 15 + ",key1=" + key1 + ",key2=" + key2); 16}
打印消息如下:
1uri=myscheme://myhost:1024/main?key1=value1&key2=value2 2scheme=myscheme,host=myhost,port=1024,path=/main,query=key1=value1&key2=value2,key1=value1,key2=value2
原理
myscheme://myhost:1024/main?key1=value1&key2=value2,通过一个链接,为什么能启动相应的 APP 呢?Web 唤起 Android app 的实现及原理,一文说到关键代码在 Android 6.0 的原生浏览器的 shouldOverrideUrlLoading 方法,核心实现在 UrlHandler 这个类中。代码如下:
1final static String SCHEME_WTAI = "wtai://wp/"; 2final static String SCHEME_WTAI_MC = "wtai://wp/mc;"; 3boolean shouldOverrideUrlLoading(Tab tab, WebView view, String url) { 4 if (view.isPrivateBrowsingEnabled()) { 5 // Don't allow urls to leave the browser app when in 6 // private browsing mode 7 return false; 8 } 9 if (url.startsWith(SCHEME_WTAI)) { 10 // wtai://wp/mc;number 11 // number=string(phone-number) 12 if (url.startsWith(SCHEME_WTAI_MC)) { 13 Intent intent = new Intent(Intent.ACTION_VIEW, 14 Uri.parse(WebView.SCHEME_TEL + 15 url.substring(SCHEME_WTAI_MC.length()))); 16 mActivity.startActivity(intent); 17 // before leaving BrowserActivity, close the empty child tab. 18 // If a new tab is created through JavaScript open to load this 19 // url, we would like to close it as we will load this url in a 20 // different Activity. 21 mController.closeEmptyTab(); 22 return true; 23 } 24 //…… 25 }
源码
公众号「吴小龙同学」回复「SchemeSample」,获取这次练习的完整示例。
Deep Links

如图,在 Android M 之前,如果点击一个链接有多个 APP 符合,会弹出一个对话框,询问用户使用哪个应用打开 - 包括浏览器应用。谷歌在Android M 上实现了一个自动认证(auto-verify)机制,让开发者可以避开这个弹出框,使用户不必去选择一个列表,直接跳转到他们的 APP。
创建
Android M App Links: 实现, 缺陷以及解决办法
我没有验证,因为我玩不起来,有条件更新下 Deep Links 这块内容,可以自己搭个本地服务器。
弊端
需要 Android M
需要 Android 6.0(minSdkVersion 级别23)及更高版本上的才能使用。
.well-known/assetlinks.json
开发者必须维护一个与app相关联的网站,通过在以下位置托管数字资产链接 JSON 文件来声明您的网站与您的意图过滤器之间的关系:
https://domain.name/.well-known/assetlinks.json
参考
Android移动开发者必须知道的Deep Linking技术
公众号
我的公众号:吴小龙同学,欢迎交流~