title: iOS/Android 微信及浏览器中唤起本地APP
date: 2017-05-10 10:19:20
tags:

需求概述

分享应用活动链接已经成为手机应用一个非常重要的推广传播形式。为了提高转化率,就需要让用户不管是在微信或者是浏览器中,都能在点击链接后, 唤起本地的 app 后 , 跳转到指定页面 。

虽然这个功能从用户体验方面来说是自然而然的,但是由于 iOS/Android 平台差异性,在实现过程中还是有些问题。

  1. 未安装 app 时,如何做好引导页,引导用户下载后打开 app 后,是否可以打开之前唤醒前指定的页面或内容

  2. 如何绕过微信的  scheme  屏蔽,在微信中唤醒 app 中,并打开指定页面

  3. iOS 专用的  universal link ,Android 专用的  App Links  的集成要求,有哪些局限性。

现实情况

在实施过程中,还是有两个地方是没办法忽略的:

scheme 被微信屏蔽了

除非一些和微信有合作的 app 可以进入到白名单,其他的应用在微信内都没办法通过自定义 scheme 协议直接唤起 app,前端页面需要对唤起场景进行判断。

浏览器无法明确地判断本地是否已经安装 app

目前的取巧方案就是通过  setTimeOut  设置超时时间,在超时时间内唤起 app,然后获得成功失败回调,如果获得的是失败的回调,则说明本地没有安装 app,需要跳转到商店下载页面。

实现方案

踩坑方案

鉴于在打开 url scheme 的方法中,iOS9 和 iOS8/iOS7 区别很大,Android 不同厂商的是适配也不同,这里介绍的踩坑方案都是前人实践总结出来的。

两种打开方式:

一、 直接跳转:点击链接或者修改  window.location 。
点击链接:

<a href="schemeUrl">唤醒你的APP</a>

修改 window.location:

window.location.href = schemeUrl;

这种情况,如果APP唤醒失败,或者APP未安装的话,很多时候都会跳到错误页,这很影响用户体验,而我们的要求可能是跳转到其他页面或者下载APP。

二、 iframe  跳转:在 body 上添加 iframe,设置 src 属性为跳转的 URL scheme

<a href="APP下载地址">下载或打开APP</a>
<script>$('a').click(function() {var ifr = document.createElement('iframe');ifr.src = '自定义 URL scheme';ifr.style.display = 'none';document.body.appendChild(ifr);setTimeout(function(){document.body.removeChild(ifr);}, 3000);});
</script>

这一种方法不会引起页面内容可见的变化,不会导致浏览器历史记录的变化,
实现过程是:点击 a 标签时,打开 自定义 scheme。若成功,就唤起 app,若失败,就到 href 属性,去到下载地址。

相应的,在 Android 客户端这边,需要在 manifest 文件里面配置 intent-filter,如下:

<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="自定义 URL scheme" />
</intent-filter>

注意这里的  intent-filter  的这几个配置不能再和 action.MAIN 放在一起。如果是在同一个 activity 中配置,那么可以配置两个 intent-filter ,比如

<!-- 第一个filter -->
<intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" />
</intent-filter><!-- 第二个filter -->
<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="mls" />
</intent-filter>

理论上,在 Android 上这两种方式应该都是可以顺利实现的。但是 Android 的 chrome 内核从 chrome 25 以后就弃用了 iframe,不再支持通过 js 触发(非人为点击)或者通过设置  iframe src  地址来触发 scheme 跳转。所以,后一种方法在适配上存在比较多的问题。故一般选择前面一种做法,即 href 的点击或者 window.location 跳转。

使用第一种方案,setTimeOut 派上用场。

$('a').click(function() {location.href = '自定义 URL scheme';t = Date.now();setTimeout(function(){if (Date.now() - t < 1200) {location.href = 'Android 下载地址';}}, 1000);return false;}
}

理想过程是这样:浏览器尝试打开 URL scheme,在 1 秒计时后,检查当前时间,如果实际时间已过 1200 毫秒,说明唤起 app 成功(唤起 app 会让浏览器的定时器变慢);如果没超过 1200 毫秒,很可能是没有安装应用,就跳到下载地址。

但是这么做也是有问题的,因为 Android 系统是多任务系统,setTimeOut基本就没那么精准,不能起到理想效果。换一种方式, setInterval ,如果设置比较小的运行间隔(<30ms),在浏览器或者 webview 中,应用切换到后台,setInterval 会被很明显的延迟执行,比如设置一个运行间隔 20ms,总计运行 100 次的定时器,如果页面一直处于前台,则 100 次跑完,总耗时与 100x20=2000ms 不会有太大差异,但页面在后台运行时,此时间会明显超过 2000ms。可以利用这一点来实现是否成功打开 app 检测及回调。

function openApp(openUrl, appUrl, action, callback) {// 检查 app 是否打开function checkOpen(cb){var _clickTime = +(new Date());function check(elsTime) {if ( elsTime > 3000 || document.hidden || document.webkitHidden) {cb(1);} else {cb(0);}}// 启动间隔 20ms 运行的定时器,并检测累计消耗时间是否超过 3000ms,超过则结束var _count = 0, intHandle;intHandle = setInterval(function(){_count++;        var elsTime = +(new Date()) - _clickTime;if (_count>=100 || elsTime > 3000 ) {clearInterval(intHandle);check(elsTime);}}, 20);}// 在 iframe 中打开 appvar ifr = document.createElement('iframe');ifr.src = openUrl;ifr.style.display = 'none';if (callback) {checkOpen(function(opened){callback && callback(opened);});}document.body.appendChild(ifr);      setTimeout(function() {document.body.removeChild(ifr);}, 2000);
}

iOS9 上 iframe 也不可用

在 iOS 9 上,iframe 方案变得不可用。在打开自定义 URL scheme 时,会弹出对话框,询问是否用 xx 应用来打开。往往用户还没来得及点击打开,定时器又触发了,导致跳到 App Store。
可以在尝试打开URL scheme 后,再加一个页面跳转,这样对话框会被覆盖,再刷新页面,就能无需确认唤起 app:

$('a').click(function() {location.href = '自定义 URL scheme';location.href = '下载页';location.reload();
}

APP已安装这是没问题的,但如果APP未安装,跳 App Store 的请求会失败。 这时可以使用两个定时器:

$('a').click(function() {location.href = '自定义 URL scheme';setTimeout(function() {location.href = '下载页';}, 250);setTimeout(function() {location.reload();}, 1000);
}

universal link 和 App Links

这里的链接指的是深度链接 (deep learning) ,这是一种能够方便地通过传统的 http 链接来启动 app 或网站。通过唯一的网址,就可以链接一个特定的视图到你的 app 里面不需要特别的 scheme。

iOS 的 universal link

使用要求:

使用步骤:

  1. 添加域名到  Capabilities
    首先, 你必须在 Xcode 的 capabilities 里 添加你的 APP 域名, 必须用 applinks: 前置它:还添加一些你可能拥有的子域和扩展(www.domain.com, news.domain.com 等等)。这将使你的 APP 从你的域名请求一个特殊的 JSON 文件 apple-app-site-association。当你第一次启动 APP,它会从 https://domain.com/apple-app-site-association 下载这个文件。

  2. 构建  apple-app-site-association  文件
    该文件必须存在且为了安全原因可使用 SSL 通过 GET 请求访问到。你可以打开一个文本编辑器然后写一个这样的简单 JSON 格式:

    根据 paths 键设定一个允许的路径列表(你希望APP 作出反应的路径), 设置 * 号则只是打开 app 而已。
    TEBEJCSf9DF 这一串是具有团队标示的 bundle id。
    文件构建成功后,上传这个文件到你的域名根目录。

  3. 在 app 里处理通用链接
    为了在 app 里支持通用链接, 需要在 AppDelegate 里实现  [application(_:continueUserActivity:restorationHandler:)]  。 尽管这种方法可以用于许多不同的目的(比如 [Handoff]和 [搜索 API]), 我们将只关注如何处理接收到的通用链接。
    为了确保 app 可以翻译 URL 成实际的内容, 需要做下面几步:

  • 使用 [NSURLComponents]简单解析 webpageURL 到 host(如domain。com), 路径组成同理(如 ["/"]、“path”、“to"以及"thezoo”)。

  • 确保能识别 host。

  • 尝试将 pathComponents 匹配到 APP 的已知内容里。

  • 验证该内容实际上可以被呈现并呈现内容给用户。

Android 的 App Links

Android 手机上,打开链接通常会跳出选择框选择用什么浏览器打开,而使用 app links,当点击了链接,安卓系统会检查是否有一个 app 可以处理 url(比如 twitter.com),然后跟核对哪个app(s)可以处理该域名的链接,直接在应用内处理,这样我们就能避免弹框影响用户。

和 iOS 一样,Android 的深度链接也需要相关的域名来配合,有以下的条件:

想要让你的 app 处理链接,需要在 manifest 文件中使用 intent filter 声明 app 需要处理的 uri 模式。下面的例子,声明了一个  intent filter  能够处理 http://www.android.com 和 https://www.android.com:

<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" /><data android:scheme="https" /><data android:host="www.android.com" /></intent-filter>
</activity>

配置  autoVerify 来触发系统自动校验。

如何验证成功?这就需要网站所有者必须声明和app的联系。网站所有者通过持有一个名为  assetlinks.json  的 Digital Asset Links JSON 文件来声明与一个 app 的联系,它在 domain 的well-known 位置:https://domain[:optional_port]/.well-known/assetlinks.json。
注意:系统通过加密的 HTTPS 协议来验证 json 文件,所以不管 intent filter 中是否声明了 https,请确保 json 文件能够通过 HTTPS 连接来获取。
Digital Asset Links JSON 文件声明了与此网站关联的 app,下面的示例 assetlinks.json 文件允许包名为 com.example 的app打开链接:

[{"relation": ["delegate_permission/common.handle_all_urls"],"target": {"namespace": "android_app","package_name": "com.example","sha256_cert_fingerprints":["14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"]}
}]

sha256 可以通过 keytool 来获取。

预想可操作方案

  1. 链接页面里面有两个按钮:直接打开和下载。直接打开的话,判断本地是否存在 app 后直接启动 app,下载的话去到下载页面。前端需要设计两个页面(分享的页面,下载的页面)。

  2. 使用<a>标签的方式,来启动客户端。

  3. 区分渠道,判断浏览器内核区分是 Android 还是 iOS,在 Android 6.0 以上,可使用 app links,在 iOS9.0 以上,可使用 universal link.但是这两个都需要提供应用网站域名来绑定。其他系统版本通过自定义 scheme 来截取。

  4. 补充下 Android 的调用流程:

    由于好多初始化工作都在 SplashActivity 里面做了,所以开启的流程也是从上到下一步一步开页面,数据也需要传递过去。遇到需要登录的时候,先跳登录页,再跳详情页,把登录页从栈中清掉,返回时,就能从详情页 DetailActivity 回到 MainActivity 了。

关注全栈公众号,遇见更好的自己。

iOS/Android 微信及浏览器中唤起本地APP相关推荐

  1. iOS/Android 微信及浏览器中唤起本地 APP

    需求概述 分享应用活动链接已经成为手机应用一个非常重要的推广传播形式.为了提高转化率,就需要让用户不管是在微信或者是浏览器中,都能在点击链接后, 唤起本地的 app 后 , 跳转到指定页面 . 虽然这 ...

  2. 浏览器中唤起native app || 跳转到应用商城下载(二) 之universal links

    上一篇文章 在ios9出来以后,我们发现越来越多的应用能够直接绕过微信的屏蔽,从其内置浏览器中直接唤起app.相比于通过弹窗提示让用户到浏览器中操作的方式,这无疑是极大的提高了用户体验与流量导入.因此 ...

  3. 浏览器中唤起native app || 跳转到应用商城下载

    前段时间遇到一个小需求:要求在分享出来的h5页面中,有一个立即打开的按钮,如果本地安装了我们的app,那么点击就直接唤起本地app,如果没有安装,则跳转到下载. 因为从来没有做过这个需求,因此这注定是 ...

  4. 浏览器中唤起Native App

    前段时间遇到一个小需求:要求在分享出来的h5页面中,有一个立即打开的按钮,如果本地安装了我们的app,那么点击就直接唤起本地app,如果没有安装,则跳转到下载. 因为从来没有做过这个需求,因此这注定是 ...

  5. 微信小程序中的本地存储

    1. 微信小程序中的本地存储:wx.setStorageSync("key","value") - 读取时的key必须与存储时的key相同,才能取出需要的数据. ...

  6. htm文件在C语言中如何打开,如何在Microsoft Edge浏览器中打开本地HTML文件?

    自远古以来,如果您运行Web浏览器可执行文件,大多数Web浏览器都能打开本地文件,例如只需执行iexplore.exe file:/c:/temp/file或通过IShellDocView接口.我试图 ...

  7. 解决ios系统在safari浏览器中添加web应用程序到主屏幕,打开子链接会跳转到safari的问题

    解决ios系统在safari浏览器中添加web应用程序到主屏幕,打开子链接会跳转到safari的问题 前言 ios系统的safari浏览器有一个功能可以将web应用程序添加到主程序,使得web程序像a ...

  8. IE浏览器中选择本地文件

    IE浏览器中选择本地文件源码如下: <!DOCTYPE html> <html> <head> <meta http-equiv="content- ...

  9. html5页面中打开本地app,如果没有跳转下载页面的解决方案

    需求效果 在推广网页上用户点击产品的详细信息时,判断出这个用户手机上是否安装自己的app如果安装了直接自动打开手机内的app应用,若没有则跳转app的下载页 技术实现 直接用window.locati ...

最新文章

  1. 剑指offer:面试题07. 重建二叉树
  2. 设计模式入门之原型模式Prototype
  3. linux receive函数,Linux网络 - 数据包的接收过程
  4. 解决Loadrunner报not writing pre_cci.ci问题
  5. 从exe4j生成的exe中抽取jar文件
  6. 我的docker随笔10:docker客户端使用其它主机的docker服务器
  7. 《Windows 网络操作系统》集中实训任务书详解
  8. CTF---Web入门第十六题 天下武功唯快不破
  9. 微信小游戏制作学习笔记
  10. PHP+AJAX高性能聊天室(群聊+私聊)
  11. oracle 11g rac 恢复,11G RAC 异机恢复至单实例测试
  12. Android 架构设计与挑选
  13. 7-108 奇数偶数-zzuli
  14. 为什么这些年都不快乐
  15. RD client、远程桌面、smb等远程相关的使用与注意
  16. 【综述】A Comprehensive Survey on Graph NeuralNetworks(1)
  17. Revit轴网绘制中怎么生成轴网?
  18. 【MySQL】MyCat分库分表分片规则配置详解与实战(MySQL专栏启动)
  19. Caffe 代码解读之全连接层 inner product layer
  20. 60个英文阅读网站推荐

热门文章

  1. API接口防止参数被篡改和重放攻击
  2. 驭势“AI司机”融入城市生活,服贸会抢“鲜”体验
  3. 微型计算机技术上机报告心得,微机培训心得体会3篇
  4. 比较两个数或者三个数的大小,输出较大数的值 Java
  5. 刚性仿射变换算法_一种视觉定位中的点集仿射变换算法的制作方法
  6. 机器学习苹果识别——python+opencv实现物体特征提取
  7. PB控件属性之Tab
  8. angular+bootstrap+spring boot实现分页
  9. Ngork内网穿透简单实现
  10. 根据地理位置获取经纬度