文章目录

  • 背景
  • 现状
  • 结论
  • URL Scheme(通用)
    • 效果
    • 说明
    • 场景
    • Intent语法(安卓原生谷歌浏览器)
    • 如何让ios APP 支持自定义URL Scheme?
    • 如何让Android APP 支持自定义URL Scheme?
  • App Links(安卓6.0+)
    • 效果
    • 说明
    • 如何让Android APP 支持App Links?
  • Universal Link(IOS 9+)
    • 效果
    • 说明
    • 如何让ios APP 支持 Universal Link?
  • 微信的开放标签
    • 效果
    • 遇到的坑
  • 微信/浏览器中唤起 app store
    • 效果
    • 说明
  • h5端调起app
  • h5传参给app,app端如何接收
    • ios
      • 微信开放标签参数
      • URL scheme参数
    • android
      • 微信开放标签参数
      • URL scheme参数
  • 参考文档

背景

调研 h5 唤起 App 方案

目的:引导已下载用户打开 APP,引导未下载用户下载 APP。

现状

IOS:绝大多数用户使用 IOS 10 +
Android:跨度比较大,4以上均有一定的人使用,各个浏览器对 app links处理方式不同

结论


如上图,我们一般业务逻辑是有两个页面,一个详情页,一个下载页。上图逻辑中描述的就是详情页“打开app”按钮的逻辑。

其中涉及到的技术:

  • URL Scheme(android/ios都支持)
  • Universal Link(ios9 及以上支持的)
  • App Links(android 6+ 及以上支持)【这个可以不用,因为这个只在短信打开app可以用到】
  • 微信开放标签SDK(android/ios都支持)
  • 微信/浏览器中唤起 app store

接下来一一说明以上技术

URL Scheme(通用)

效果

只能在浏览器中使用,微信环境触发不了。
下图是 ios Safari 截图,android 也是差不多的一个弹窗。

说明

有点像 web 中我们通过域名定位到一个网站,app 同样是通过类似的这个东西(URL Scheme)来定位到 app

# authority包括host和port两部分
[scheme:][//authority][path][?query][#fragment]

常用APP的 URL Scheme

weixin:// | alipay:// | taobao:// | sinaweibo:// | mqq:// | zhihu:// | sms:// |

其中scheme既可以是Android中常见的协议,也可以是我们自定义的协议。Android中常见的协议包括content协议、http协议、file协议等,自定义协议可以使用自定义的字符串,当我们启动第三方的应用时候,多是使用自定义协议。

场景

  • 服务器下发跳转路径,客户端根据服务器下发跳转路径跳转相应的页面
  • H5页面点击锚点,根据锚点具体跳转路径APP端跳转具体的页面
  • APP端收到服务器端下发的PUSH通知栏消息,根据消息的点击跳转路径跳转相关页面
  • APP根据URL跳转到另外一个APP指定页面

Intent语法(安卓原生谷歌浏览器)

安卓的原生谷歌浏览器自从 chrome25 版本开始对于唤端功能做了一些变化,URL Scheme 无法再启动Android应用。 例如,通过 iframe 指向 weixin://,即使用户安装了微信也无法打开。所以,APP需要实现谷歌官方提供的 intent: 语法

如果用户未安装 APP,则会跳转到系统默认商店。当然,如果你想要指定一个唤起失败的跳转地址,添加下面的字符串在 end; 前就可以了:

intent://scan/#Intent; package=com.google.zxing.client.android; scheme=zxing; end;
<a href="intent://scan/#Intent;scheme=zxing;package=com.google.zxing.client.android;S.browser_fallback_url=http%3A%2F%2Fzxing.org;end"> Take a QR code </a>

如何让ios APP 支持自定义URL Scheme?

支持之后,在 safari 中输入 rf://,你就会看到

这里每次在 Safari 浏览器测试要打开一个新的页面

如何让Android APP 支持自定义URL Scheme?

AndroidManifest

<activity android:name=".MainActivity"><intent-filter> <!--正常启动--><action android:name="android.intent.action.MAIN"/><category android:name="android.intent.category.LAUNCHER"/></intent-filter><intent-filter> <!--URL Scheme启动--><!--必有项--><action android:name="android.intent.action.VIEW"/><!--如果希望该应用可以通过浏览器的连接启动,则添加该项--><category android:name="android.intent.category.BROWSABLE"/><!--表示该页面可以被隐式调用,必须加上该项--><category android:name="android.intent.category.DEFAULT"/><!--协议部分--><data android:scheme="urlscheme"android:host="auth_activity"></intent-filter><intent-filter><action   android:name="emms.intent.action.check_authorization"/><category android:name="android.intent.category.DEFAULT"/><category android:name="emms.intent.category.authorization"/></intent-filter>
</activity>

intent-filter的标签在指定path的值时,可以在里面使用通配符*,起到部分匹配的效果。

上面的设置中可以看到,MainActivity包含多个设置,第一个是正常的启动,也就是在应用列表中启动;第二个是通过URL Scheme方式启动,其本身也是隐式Intent调用的一种,不同在于添加了属性,定义了其接受URL Scheme协议格式为urlschemel://auth_activity

Activity

Intent intent = getIntent();
String scheme = intent.getScheme();
String dataString = intent.getDataString();
Uri uri = intent.getData();
if (uri != null) {//完整的url信息String url = uri.toString();//scheme部分String schemes = uri.getScheme();//host部分String host = uri.getHost();//port部分int port = uri.getPort();//访问路径String path = uri.getPath();//编码路径String path1 = uri.getEncodedPath();//query部分String queryString = uri.getQuery();//获取参数值String systemInfo = uri.getQueryParameter("tool_id");
}

接受参数


App Links(安卓6.0+)

效果

比如在短信、记事本等等中有一个链接,点击直接唤起 app,没有多余的弹窗之类的。这个目前在 h5 打开 app 的这个场景没有用到。

说明

Android App Links是一种特殊的Deep Links,它使Android系统能够直接通过网站地址打开应用程序对应的内容页面,而不需要用户选择使用哪个应用来处理网站地址。

Deep Link 即我们通常说的url scheme跳转

这个主要的场景就是你自己的网站,自己的app。

如何让Android APP 支持App Links?

<activity ...><intent-filter android:autoVerify="true"><action android:name="android.intent.action.VIEW" /><category android:name="android.intent.category.DEFAULT" /><category android:name="android.intent.category.BROWSABLE" /><data android:scheme="http" android:host="www.example.com" /><data android:scheme="https" /><data android:scheme="https" android:host="*.example.com" /></intent-filter></activity>

android:autoVerify="true" 出现在你任意一个 intent filter 里,在Android 6.0 及以上的系统上安装应用的时候,会触发系统对APP里和URL有关的每一个域名的验证。验证过程设计以下步骤:
1、系统会检查所有包含以下特征的 intent filter:

  • Action 为 android.intent.action.VIEW
  • Category 为 android.intent.category.BROWSABLEandroid.intent.category.DEFAULT
  • Data scheme 为 http 或 https

2、对于在上述intent filter里找到的每一个唯一的域名,Android系统会到对应的域名下查找数字资产文件,地址是:https://域名/.well-known/assetlinks.json
比如知乎的网站:https://www.zhihu.com/.well-known/assetlinks.json

如何生成数字资产文件?
这是知乎的文件举例:

[{"relation": ["delegate_permission/common.handle_all_urls"],"target": {"namespace": "android_app","package_name": "com.zhihu.android","sha256_cert_fingerprints": ["BD:84:50:55:7C:B3:96:5C:05:1F:16:11:D4:28:6A:5F:02:9B:90:9C:AE:3D:E7:57:EC:15:2D:05:63:C3:F7:FA"]}}
]

需要改的就是 package_name 和 sha256_cert_fingerprints,别的不用动

  • package_name:在build.gradle里定义的application ID
  • sha256_cert_fingerprints:应用签名的SHA256指纹信息。你可以用下面的命令,通过Java keytool来生成指纹信息:
keytool -list -v -keystore my-release-key.keystore

这个字段支持多个指纹信息,可以用来支持不同的应用版本,如开发版本和发布版本。

当你用这个脚本生成的没有 sha256 的时候,将你的 java 版本升级一下,我当时是 java8 不行,升级到 11 就可以了。

注意:

  • assetlinks.json文件的content-type必须为application/json
  • 不管你的应用的intent filter是否定义https作为data的scheme,assetlinks.json必须能通过HTTPS链接访问
  • assetlinks.json必须能不经过任何重定向被访问到(即没有301、302跳转),同时可以被爬虫访问到(你的robot.txt必须允许抓取/.well-known/assetlinks.json)
  • 如果你的应用支持多种域名,你需要把assetlinks.json发布在这几个域名的服务器上。
  • 不要在你的AndroidManifest文件里发布无法公开访问的开发/测试URL(比如那些只能通过VPN进行访问的URL)。一种可行的做法是配置构建类型,来为开发构建生成不同的清单。

测试
https://developers.google.com/digital-asset-links/tools/generator
场景
大多是短信链接直接唤起app的场景

Universal Link(IOS 9+)

效果

在短信/记事本/微信 等等环境中,点击链接可以直接唤起 app,没有弹框等多余的提示。

说明

Universal Link 是苹果在 WWDC2015 上为 iOS9 引入的新功能,通过传统的 HTTP 链接即可打开 APP。如果用户未安装 APP,则会跳转到该链接所对应的页面。

为什么要用Universal Link?
之前只能用URL Scheme 的时候,如果在微信里打开h5,需要引导用户点击右上角用 safari 上,现在直接点击一个按钮就可以跳转到对应的app。

如何让ios APP 支持 Universal Link?

官方文档
1、在 开发者中心 ,Certificates, Identifiers & Profiles --> Identifiers 下 AppIDs 找到自己的 App ID,编辑打开 Associated Domains 服务。

2、打开工程配置中的 Associated Domains ,在其中的 Domains 中填入你想支持的域名,必须以 applinks: 为前缀

3、配置 apple-app-site-association 文件,文件名必须为 apple-app-site-association ,不带任何后缀。上传该文件到你的 HTTPS 服务器的根目录或者 .well-known 目录下。

例如知乎链接:https://www.zhihu.com/apple-app-site-association

{"applinks": {"apps": [],"details": [{"appID": "9JA89QQLNQ.com.apple.wwdc","paths": [ "/wwdc/news/", "/videos/wwdc/2015/*"]},{"appID": "ABCD1234.com.apple.wwdc","paths": [ "*" ]},"8J52SRPW6X.com.zhihu.ios": {"paths": ["/universal-links-callback/*","/qq_conn/100490701/*","NOT /question/*/log","/question/*","/people/*","/topic/*","/p/*"]},]}
}
  • appID:组成方式是 teamId.yourapp’s bundle identifier。如上面的 9JA89QQLNQ就是teamId。登陆开发者中心,在Account -> Membership里面可以找到Team ID。
  • paths:设定你的app支持的路径列表,只有这些指定的路径的链接,才能被app所处理。星号的写法代表了可识 别域名下所有链接。

微信的开放标签

如果微信打开的 h5 想要跳转 app 时

  • h5 端需要用微信的开放标签 wx-open-launch-app
  • App 必须接入微信 OpenSDK。

这里解读以下接入的规则:

  • 多个公众号可以绑定同一个应用。
  • 公众号可以有多个域名白名单。
  • 公众号对应绑定的应用只能有一个域名。

比如你有一个域名e.test.com,一个公众号,一个开放平台的应用,那么你需要:
1、公众号后台绑定域名白名单 e.test.com
2、开放平台找到公众号的关联设置,选择该公众号,并绑定域名 e.test.com。
所以这三个是一一对应的,如果你有另一个域名想要接入唤起同一个应用,那么你需要找一个新的公众号和新的域名,按上面的步骤绑定。

这里注意一点:其实在 ios 中,只要你接入了 Universal Link,那么在微信环境下你是可以直接唤起 app 的而不需要接入微信sdk触发这个弹框,但是如果你想和 android 保持一致的用户体验和代码逻辑,那么就干脆都接入吧。

效果

ios、android 效果相同。

遇到的坑

1、按照微信的文档,各方面都接好之后,发现只是偶尔能唤起这个弹框,大多数的时候是唤不起的。最终发现是该页面导致的:

也就是说,你的测试链接不要放在微信的聊天记录点开,会先到这个页面,微信可能会做一些处理,导致我们的 sdk 被影响。我在这个帖子有详细的回答。
https://developers.weixin.qq.com/community/develop/doc/0004c06e12479887f00d42be85b800
所以需要你把这个链接用微信分享,或者生成二维码,总之不要触发这个页面,就可以正常调起 h5 唤起 APP 的弹框。

2、Android冷启动无法唤起
WXEntryActivity.java

package com.cmvalue.wisederma.wxapi;import android.app.Activity;
import android.os.Bundle;
import com.theweflex.react.WeChatModule;import com.tencent.mm.opensdk.constants.ConstantsAPI;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.WritableMap;
import com.tencent.mm.opensdk.modelmsg.ShowMessageFromWX;
import com.tencent.mm.opensdk.openapi.IWXAPI;
import com.tencent.mm.opensdk.openapi.WXAPIFactory;
import com.tencent.mm.opensdk.modelbase.BaseReq;
import com.tencent.mm.opensdk.modelbase.BaseResp;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import android.util.Log;
import android.content.Intent;
import com.tencent.mm.opensdk.openapi.IWXAPIEventHandler;
import java.lang.ref.WeakReference;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import org.json.JSONObject;
import org.json.JSONException;
import com.facebook.react.ReactPackage;
import java.util.List;
import com.facebook.react.PackageList;
import com.theweflex.react.WeChatPackage;
import com.cmvalue.wisederma.MainActivity;
import java.lang.Thread;
import android.os.Handler;
import com.facebook.react.ReactActivity;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.bridge.ReactContext;
import com.cmvalue.wisederma.generated.BasePackageList;
import com.facebook.react.common.build.ReactBuildConfig;public class WXEntryActivity extends ReactActivity implements IWXAPIEventHandler {private IWXAPI api;private static final String APP_ID = "xxxxx";private static String TAG = "MicroMsg.WXEntryActivity";public void trigger () {api.handleIntent(getIntent(), this);}public void loopCall () {Handler handler = new Handler();handler.postDelayed(new Runnable(){public void run() {if(WeChatModule.getModules().isEmpty()){Log.e("WXEntryActivity", "11111");loopCall();} else {Log.e("WXEntryActivity", "222222");trigger();}}}, 3000);}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);api = WXAPIFactory.createWXAPI(this, APP_ID, false);WeChatModule.handleIntent(getIntent());// 关键:如果没有,说明冷启动,手动启动,否则就正常启动// 这个getModules函数是手动改的npm包,加了一个if(WeChatModule.getModules().isEmpty()){Intent intent = new Intent(WXEntryActivity.this, MainActivity.class);startActivity(intent);loopCall();}else {trigger();}finish();}@Overridepublic void onNewIntent(Intent intent) {super.onNewIntent(intent);setIntent(intent);api.handleIntent(intent, this);}@Overridepublic void onReq(BaseReq baseReq) {WritableMap map = Arguments.createMap();map.putString("openId", baseReq.openId);map.putString("transaction", baseReq.transaction);if (baseReq.getType() == ConstantsAPI.COMMAND_SHOWMESSAGE_FROM_WX) {ShowMessageFromWX.Req req = (ShowMessageFromWX.Req) baseReq;// 对应JsApi navigateBackApplication中的extraData字段数据map.putString("type", "SendMessageToWX.Resp");map.putString("lang", req.lang);map.putString("extMsg", req.message.messageExt);}ReactInstanceManager mReactInstanceManager = this.getReactNativeHost().getReactInstanceManager();ReactContext currentReactContext = mReactInstanceManager.getCurrentReactContext();currentReactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("WeChat_Resp", map);finish();}@Overridepublic void onResp(BaseResp resp) {Log.e("WXEntryActivity", "测试测试测试测试测试");}
}

WeChatModule.java

// add
public static ArrayList<WeChatModule> getModules() {return modules;
}

微信/浏览器中唤起 app store

效果

说明

浏览器中唤起 app store,以下任意一种都可以

window.top.location.href = 'https://itunes.apple.com/cn/app/id432274380'
window.location.href = 'https://itunes.apple.com/cn/app/id432274380'
window.top.location.href = 'itms-apps://itunes.apple.com/app/id432274380'
window.location.href = 'itms-apps://itunes.apple.com/app/id432274380'

微信中唤起 appstore,需要接入微信的 sdk,也就是上面所说的开放标签。跳转链接还是这个。(这个微信官方文档也没有说过,完全是自己试出来的)

h5端调起app

以上都是 app 端要做的事情,那么 h5 端要做什么调起 app 呢?
有好几种,比如下面的 a 标签,iframe 之类的,npm 上 callapp-lib 包兼容了这些,我们可以直接用。

// a标签调起举例
<a href="intent://scan/#Intent;scheme=zxing;package=com.google.zxing.client.android;end"">扫一扫</a>
// 或
window.location.href = 'sinaweibo://qrcode';

callapp-lib 包调起 app

<template><div @click="goDownload"><img class="bgImg" src="~/assets/img/bg.png" alt="" /><img v-if="userAgent.isWechat" class="chickImg" src="~/assets/img/chick.png" alt="" /></div>
</template>
<script>
import CallApp from 'callapp-lib'
export default {computed: {userAgent() {return this.$store.getters.userAgent}},mounted() {if (!this.userAgent.isWechat) {this.openApp(this.$route.query)}},methods: {openApp(url) {const options = {scheme: {protocol: 'kccatalog'},intent: {package: '',scheme: ''},appstore: '填写appstore的下载地址',yingyongbao: '填写应用宝的下载地址',fallback: '填写唤端失败后跳转的地址。'}const callLib = new CallApp(options)callLib.open({param: url.param,path: url.path})},goDownload() {window.location.href = '没有自动唤端的话,证明手机里面没有app, 点击页面上任意一个地方直接跳应用宝下载链接, 微信不会拦截支付宝的链接'}}
}
</script>

h5传参给app,app端如何接收

ios

微信开放标签参数

按照 https://github.com/little-snow-fox/react-native-wechat-lib 中的配置好了之后,可以用这个接收参数

import { DeviceEventEmitter } from 'react-native';
DeviceEventEmitter.addListener('WeChat_Req', (req) => {handleExtMsg(req.extMsg);
});

这时你会发现 ios 冷启动无法唤起,需要在 ios 的生命周期 didFinishLaunchingWithOptions 函数里注册微信:

// 这个是RN向ios注入全局变量用的
#import "ReactNativeConfig.h"
//向微信注册
NSString *APP_ID = [ReactNativeConfig envFor:@"APP_ID"];
NSString *UNIVERSAL_LINK = [ReactNativeConfig envFor:@"UNIVERSAL_LINK"];
[WXApi registerApp:APP_ID universalLink:UNIVERSAL_LINK];

URL scheme参数

接受到之后就可以解析参数并跳到对应的页面了。

import { Linking } from 'react-native';
// 热启动接受
Linking.addEventListener('url', (data) => {handleLinking(data.url);
});
// 冷启动接受
const initialUrl = await Linking.getInitialURL();
if (initialUrl) {setTimeout(() => {handleLinking(initialUrl);}, 3000);
}

android

微信开放标签参数

按照 https://github.com/little-snow-fox/react-native-wechat-lib 中的配置好了之后,可以用这个接收参数

import { DeviceEventEmitter } from 'react-native';
DeviceEventEmitter.addListener('WeChat_Resp', (resp) => {handleExtMsg(resp.extMsg);
});

URL scheme参数

同 ios

参考文档

https://github.com/suanmei/callapp-lib
https://github.com/jawidx/web-launch-app
https://www.zhihu.com/question/270839820
示例
app links
Universal Link配置
https://github.com/little-snow-fox/react-native-wechat-lib

可能是最全的h5唤起App方案相关推荐

  1. H5 唤起 APP的解决方案

    H5 唤起 APP的解决方案 参考文章: (1)H5 唤起 APP的解决方案 (2)https://www.cnblogs.com/yzhihao/p/8989195.html 备忘一下.

  2. H5唤起APP进行分享的尝试

    H5唤起APP进行分享 最近很久没有写blog和note,倒是过家家的开发日志简单草草写了一点.这次记录下这个学习过程 由来 我们的 "通达有你",web h5页面的分享功能体验太 ...

  3. app能不能跳转外部h5_APP内部H5页面跳转 H5唤起APP 怎么做?

    H5唤起APP某页面 做的H5页面,在APP中放了入口,在APP中访问别的页面 场景 我们做的H5页面,希望从浏览器跳转至APP的某个特定页面,如在H5中设置购买按钮,希望点击跳转至APP内部的购买页 ...

  4. 如何用xinstall实现h5唤起app

    移动端主流的H5页面如何实现唤起APP的? 写过hybrid的同学,想必都会遇到这样的需求,如果用户安装了自己的APP,就打开APP或跳转到APP内某个页面,如果没安装则引导用户到对应页面或应用商店下 ...

  5. android h5 唤起app,h5唤起app解决方案

    最近遇到一个需求:点击一个按钮,如果本机装有则唤起app,没有的话则跳下载页. 刚一接到需求,觉得很熟悉,实际上这个功能也确实很常见,页能搜索到一大堆方案,但是实际应用中,却发现总是很难做到100%的 ...

  6. h5唤起app,清除timeout

    本文转载出处:https://www.cnblogs.com/chaoyuehedy/p/9118365.html 很多代码博主亲测有效!欢迎讨论! 第一种方法: <meta name=&quo ...

  7. uni-app h5唤起App

    安卓配置 'abcd'值可以自定义 H5页面代码 <a href="abcd://">打开APP</a><br/> 注意:如果运行了跳转不了,图 ...

  8. 百度浏览器Android6.0,有些安卓机型百度浏览器h5唤起app失效

    安卓用的ifarme方法 `var ifr = document.createElement("iframe"); var t1 = new Date().getTime();   ...

  9. H5 如何实现唤起 APP

    前言 写过hybrid的同学,想必都会遇到这样的需求,如果用户安装了自己的APP,就打开APP或跳转到APP内某个页面,如果没安装则引导用户到对应页面或应用商店下载.这里就涉及到了H5与Native之 ...

  10. android h5 判断应用,Android H5判断是否安装app和唤起APP

    H5中是无法直接判断应用是否安装的,但是可以间接判断. 第一种方式, if(...){ document.location = ''; setTimeout(function(){ //此处如果执行则 ...

最新文章

  1. android学习笔记34——ClipDrawable资源
  2. Devexpress报表开发(二):创建数据报表
  3. Adobe Reader栈溢出漏洞(CVE-2010-2883)分析
  4. NHibernate之旅(14):探索NHibernate中使用视图
  5. UVA 10588—— Queuing at the doctors
  6. Android之知识总结
  7. [MSP430DriverLib-2]使用延时让LED闪烁
  8. 使用pymongo需要手动关闭MongoDB Connection吗?
  9. 一天已不足24小时?一年不足365天?求每年元旦为周几的公式还能用吗?(标题党石锤了)
  10. cru使用教程_显示器刷新率超频教程
  11. matlab数值分析代码,数值分析matlab代码
  12. jsPlumb 学习笔记(1)(api部分翻译)
  13. SATA、mSATA 、PCIe和M.2——SSD硬盘的接口详解
  14. solr使用 备忘录 草稿
  15. 修改idea运行内存大小
  16. MPPT电源控制器设计
  17. c语言private用法,深入理解C++中public、protected及private用法
  18. RD会话主机服务器2012上没有可用的远程桌面许可证服务器
  19. hdu吃糖果解题报告
  20. php 日历排班的例子,基于ThinkPHP实现的日历功能实例详解

热门文章

  1. 网络攻防实验(连更)
  2. 【转】百度网盘高速下载-暴力油猴脚本
  3. 关于STC8H8K64U单片机IAP升级过程
  4. 前端特效-HTML+CSS - 图片悬浮效果
  5. JSZip 的简单介绍
  6. movielens1M数据处理
  7. 把 14 亿中国人都拉到一个微信群,在技术上能实现吗?
  8. 虚拟机怎么启动共享文件服务器,VMware虚拟机中ubuntu启用本地文件共享的设置方法...
  9. php获取ip所有方式,php获取用户(客户端)真实IP地址的三种方法
  10. 4款最好的Android设备HTML编辑器