{"_id":"@acs1899/tool-callapp","_rev":"2-b7e7852998db4f6971b24462ace44be0","name":"@acs1899/tool-callapp","dist-tags":{"latest":"0.1.1"},"versions":{"0.1.0":{"name":"@acs1899/tool-callapp","version":"0.1.0","description":"本文档用于介绍通过H5端唤起本地NN客户端的研究过程！","main":"index.js","directories":{"example":"examples"},"scripts":{"dev":"webpack --progress --watch --config=webpack.config.js","prod":"webpack --env.prod --config=webpack.config.js"},"repository":{"type":"git","url":"git+https://github.com/acs1899/H5CallUpNative.git"},"keywords":["h5","schema","native"],"author":{"name":"acs1899"},"license":"ISC","homepage":"https://github.com/acs1899/H5CallUpNative#readme","dependencies":{"eslint":"^5.6.1","extend":"^3.0.2","webpack":"^3.1.0","uglifyjs-webpack-plugin":"^1.0.1"},"gitHead":"8d31ee8f4892622b20f525b44b86bc7f44dbd2bc","bugs":{"url":"https://github.com/acs1899/H5CallUpNative/issues"},"_id":"@acs1899/tool-callapp@0.1.0","_npmVersion":"5.6.0","_nodeVersion":"8.11.3","_npmUser":{"name":"acs1899","email":"yrn19901005@gmail.com"},"dist":{"integrity":"sha512-6nL+D1+PcBkRtm1BrGhDXClkNIse9nRTvAcx/sbj2UpSsiuA3EVL6G7/C6gzmE7J/7YclRZ7wluwN/kozL4j0g==","shasum":"4181f245917d44c898499ed74187b8596bfa38be","tarball":"https://registry.npmjs.org/@acs1899/tool-callapp/-/tool-callapp-0.1.0.tgz","fileCount":9,"unpackedSize":41148,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcFwkACRA9TVsSAnZWagAAeagP/1lzRKI5pxX5P4mjPAIy\nxm0nmG1QB67rxNpEbJ2JO3IKan9mAvJnbqK4cqYx+J8+qNmW6/vUnflwQm4V\nY+/uw6ozah4SHNU16zE9KrnEdkoGAKh2YlZozlA+HW38As09t4NXAThWH9WW\nCK37QabBW8p47PAu3w7bAZ+j/O6BFuNpZ2gjzJZjkxa0uVH+u3t16zQEfZiN\nEhPyF2G4Kz1p2S762W3Qy05DABtPeJ8Ze/hZC0r1T9h4oMytVADtkcQ7ZP8C\nVGLfyK96eRoymRjgv97oE6isvEzKlQFz/UJ9R5mqv73+d4ZW3QDVXRAaD+LL\nfuaUe17YhFEBtJG6evgkq/SjbVylOng/6Ck3H5Rycg7WdVAmNe7TZSOBiEZ2\n5q+4SX2QKQj2+V3k+kaW3Fu6sS2PT+Lk6MSMXPBwcxSg8yc0KKg2uC9+VdYp\nvnhDHAgnq6P+bmRulVgBLQ6qDzBTfdLb9vLFzSJGNXD5irwS0iLwjVa1hQGF\nQiTSG7DnTIUvGgshg95miw8GkQmczKD6QTe1rO8jqHjufR7o3hV/KVsYqu8L\nTSrTseQ1bVi2R2Iv1yeNFDAsBRNiKvzkFgd73lZuie/8PUUKhFwpC2Tk7ksv\ndpQcu3M458618S4JzZiWQENwHQVyGt7Qqlt0GfwwXiCu/csJd9AHhWakSrT2\nI62q\r\n=v5bb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGhbiZ6jmHhZW8mVo/0KNyhsOo+lcA2sevW2H/TXypBzAiEApUcLgLbC0sa9duHGcSEol0YUGsb4c5zpGzmVInTuDVc="}]},"maintainers":[{"name":"acs1899","email":"yrn19901005@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/tool-callapp_0.1.0_1545013503940_0.9653260494822467"},"_hasShrinkwrap":false},"0.1.1":{"name":"@acs1899/tool-callapp","version":"0.1.1","description":"本文档用于介绍通过H5端唤起本地NN客户端的研究过程！","main":"index.js","directories":{"example":"examples"},"scripts":{"dev":"webpack --progress --watch --config=webpack.config.js","prod":"webpack --env.prod --config=webpack.config.js"},"repository":{"type":"git","url":"git+https://github.com/acs1899/H5CallUpNative.git"},"keywords":["h5","schema","native"],"author":{"name":"acs1899"},"license":"ISC","homepage":"https://github.com/acs1899/H5CallUpNative#readme","dependencies":{"eslint":"^5.6.1","extend":"^3.0.2","webpack":"^3.1.0","uglifyjs-webpack-plugin":"^1.0.1"},"gitHead":"3189b878999176ae6fbe7d6a9e5e832b63fd2ed0","bugs":{"url":"https://github.com/acs1899/H5CallUpNative/issues"},"_id":"@acs1899/tool-callapp@0.1.1","_npmVersion":"5.6.0","_nodeVersion":"8.11.3","_npmUser":{"name":"acs1899","email":"yrn19901005@gmail.com"},"dist":{"integrity":"sha512-7ikPYOMeICWE4yewASWLF/26QduTuHWxIpJypJ44rVxJpJOKu60Zi4sWtaHynROOfy29VfNPs/ZbKzOYW5ocKw==","shasum":"eef6827dd8dba1708806294a22c7477df37429af","tarball":"https://registry.npmjs.org/@acs1899/tool-callapp/-/tool-callapp-0.1.1.tgz","fileCount":9,"unpackedSize":41174,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcFw0fCRA9TVsSAnZWagAApBkQAI+9XSp6O6pHW4VCMTqs\nPkK3hsSjDfU9bt3iKPfmLP1ouI11YGDP9Jhr1VnDhG431oSgxxCZ4p68zQM4\nVRz84dsGDM1vxVW1mnnRHlTWikgQDW7Cn6rx6ridTUxvuhIs//zXBzmxtj/r\nZXj1cK6tzEZw+bmgh+kSdzcOYtEItQQ+u+KspTG5BJVc0Ohz8bJ4E7S0flae\ntayKd8bjH5Zo6467UJuRIWQ5MKD0FbumGcwEQY6U5jThe/+FrnNMYiD8Fu0H\njJqvp0G65pKn36eT1zbGN2HuGVPsnJ4j88F9hsuUgjBm4zO9TA4roYygHXzS\nY6W0YtzQs48EJuLFwdjX2gcA3/cWZZ4aAeVX+Qsy+sEj/12oH6ubq3mF0YIT\nRrHoxo+yLppKR75amo2j16VlBG70JLXpHo/vxC0jy7Zs3W/+Ez7+zzYFxcri\np49s8dFkTkyXtJrA+sEPVcYnmShJRZ5pEaZzheGQoBRlBm03ktDxZ0q28lVk\noqwSCr4Tz8Q5UBVxeR1gMbZvRlutr34kjNv1/RRhi2Sq5akhqtSnywluGOu8\nxbZLbHwlHb0cVL8kPkWCLTAUxGetTfjDqqeiaIPoD/ixkxhNUkTWacCWXL/r\nc+ZwfJkrqS7H7wcOfJK5O98bgMyimmnqr79D1BPf48IZo2+EWzX56SPS27zQ\nTOev\r\n=Bnxr\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICz4xzy9DeIKUGBevMfYzUUQpmHmy8PtrhOJGxHvTCUiAiBwfNDVHEJ7WFe0oJp2QHePic5vaWeVkyCZd/7lkrvjLw=="}]},"maintainers":[{"name":"acs1899","email":"yrn19901005@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/tool-callapp_0.1.1_1545014559003_0.9346239402833267"},"_hasShrinkwrap":false}},"time":{"created":"2018-12-17T02:25:03.767Z","0.1.0":"2018-12-17T02:25:04.090Z","modified":"2022-04-04T11:16:40.410Z","0.1.1":"2018-12-17T02:42:39.163Z"},"maintainers":[{"name":"acs1899","email":"yrn19901005@gmail.com"}],"description":"本文档用于介绍通过H5端唤起本地NN客户端的研究过程！","homepage":"https://github.com/acs1899/H5CallUpNative#readme","keywords":["h5","schema","native"],"repository":{"type":"git","url":"git+https://github.com/acs1899/H5CallUpNative.git"},"author":{"name":"acs1899"},"bugs":{"url":"https://github.com/acs1899/H5CallUpNative/issues"},"license":"ISC","readme":"# Fork 自用，如有问题请自行前往原仓储查看 [AlanZhang001/H5CallUpNative](https://github.com/AlanZhang001/H5CallUpNative)。\n\n---\n\n# 关于通过H5页面唤Native户端的介绍\n\n本文档用于介绍通过H5端唤起本地富途牛牛客户端的研究过程！起初是一些研究性质的文章，随着越来越多的人关注和提issue，所以这里做了一些更新。\n\n**快速使用见 5、相关代码**\n\n## change log\n\n#### 2018/12/17\n- 添加 Universal Link 支持\n\n#### 2018/12/08\n- readme 关于chrome25的启动方式说明做了修正，建议来自来自[VIGDXX](https://github.com/VIGDXX)\n\n#### 2018/10/03\n- 发布tool-callapp@1.0.1，修复readme\n\n#### 2018/10/02\n- 更新readme,规范化代码，可直接使用，已发布至npm，见[tool-callapp@1.0.0](https://www.npmjs.com/package/tool-callapp)\n- 更新微信和qq内的处理方式\n- 旧版的文章及代码写于2016年，见[support/v2016](https://github.com/AlanZhang001/H5CallUpNative/tree/support/v2016)\n\n## 1、背景\n\n- 目前通过H5页面唤起native APP的场景十分常见，比如常见的分享功能；一方面，对于用户而言，相同的内容在native APP上比H5体验更好，操作更加方便，另一方面，对于APP运营来说，可以增加app的用户粘性度。\n\n- 当前native客户端内置webview中，比较常用的是通过schema打开登陆页、配置分享入口的显示；而在外部浏览器或者webview中唤醒公司的客户端目前还没有太多尝试，有据此展开研究的必要性，以便日后在真实的需求中使用！\n\n## 2、唤醒native APP 的几种方式\n\n在Android端，常用的方式是Schame + Android Itent，在IOS端，常用的方式是Schema ＋　Universal links（IOS9+）；使用的前提都是客户端程序实现了Schema协议。\n\n下面对这３种方式做简要的介绍：\n\n#### 2.1 Schema\n\n在Android和IOS浏览器中（非微信浏览器），可以通过schema协议的方式唤醒本地app客户端；schema协议在App开发注册之后，与前端进行统一约定，通过H5页面访问某个具体的协议地址，即可打开对应的App客户端 页面；\n\n访问协议地址，目前有3种方式，以打开[富途牛牛](https://play.google.com/store/apps/details?id=cn.futu.trader)客户端登录页为例：\n\n1.**通过a标签打开**，点击标签时启动APP\n```html\n<a href=\"ftnn:login\">打开登录页</a>\n```\n2.**通过iframe打开**，设置iframe.src即会启动\n```html\n<iframe src=\"ftnn:login\"></iframe>\n```\n3.**直接通过window.location 进行跳转**\n```js\nwindow.location.href= \"ftnn:login\";\n```\n\nAndroid上实现注册schema协议，可以参考博文：[Android手机上实现WebApp直接调起NativeApp](https://www.baidufe.com/item/3444ee051f8edb361d12.html)\n\n>注：由于微信的白名单限制，无法通过schema来唤起本地app，只有白名单内的app才能通过微信浏览器唤醒，这个问题我目前没有找到合适的解决办法！\n\n#### 2.2 Android Intent\n\n<del>在Android Chrome浏览器中，版本号在chrome 25+的版本不再支持通过传统schema的方法唤醒APP，比如通过设置window.location = \"xxxx://login\"将无法唤醒本地客户端。需要通过Android Intent 来唤醒APP；</del>\n\n在Android Chrome浏览器中，版本号在chrome 25+的版本不再支持通过传统schema的方法唤醒APP，比如通过设置window.location = \"xxxx://login\"将无法唤醒本地客户端，传统方式需要通过设置用户手势，比如点击来唤起APP，或者，直接通过构造Android Intent 的方式来唤醒APP；\n[官方文档](https://developer.chrome.com/multidevice/android/intents)\n> you should implement a user gesture to launch the app via a custom scheme, or use the “intent:” syntax described in this article.\n\n使用方式如下：\n\n1.构件intent字符串：\n\n```html\nintent:\nlogin                                                // 特定的schema uri，例如login表示打开NN登陆页\n#Intent;\n    package=cn.xxxx.xxxxxx;                          // 富途牛牛apk信息\n    action=android.intent.action.VIEW;               // 富途牛牛apk信息\n    category=android.intent.category.DEFAULT;        // 富途牛牛apk信息\n    component=[string];                              // 富途牛牛apk信息,可选\n    scheme=xxxx;                                     // 协议类型\n    S.browser_fallback_url=[url]                     //可选，schema启动客户端失败时的跳转页，一般为下载页，需通过encodeURIComponent编码\nend;\n```\n\n2.构造一个a标签，将上面schame 字符串作为其href值，当点击a标签时，即为通过schema打开某客户端登陆页，如果未安装客户端，则会跳转到指定页，这里会跳转到下载页；\n\n```html\n<a href=\"intent://loin#Intent;scheme=ftnn;package=cn.futu.trader;category=android.intent.category.DEFAULT;action=android.intent.action.VIEW;S.browser_fallback_url=http%3A%2F%2Fa.app.qq.com%2Fo%2Fsimple.jsp%3Fpkgname%3Dcn.futu.trader%26g_f%3D991653;end\">打开登录页</a>\n```\n\n#### 2.3 Universal links\nUniversal links为 iOS 9 上一个所谓 通用链接 的深层链接特性，一种能够方便的通过传统 HTTP 链接来启动 APP, 使用相同的网址打开网站和 APP；通过唯一的网址, 就可以链接一个特定的视图到你的 APP 里面, 不需要特别的 schema；\n\n在IOS中，对比schema的方式，Universal links有以下优点：\n\n1. 通过schema启动app时，浏览器会有弹出确认框提示用户是否打开，而Universal links不会提示，体验更好；\n2. Universal link可在再微信浏览器中打开外部App；\n\n> 网易新闻客户端IOS 9上目前采用这种Universal links方式\n\n针对这部分内容可以参考博文：\n\n- [打通 iOS 9 的通用链接（Universal Links）](http://www.cocoachina.com/ios/20150902/13321.html)\n- [浏览器中唤起native app || 跳转到应用商城下载（二） 之universal links](http://gold.xitu.io/entry/57bd1e6179bc440063b3a029/view)\n\n>由于公司IOS客户端目前未实现这种协议，所以无法对这种唤醒方式做测试，日后明确支持此类协议，待测试功能后，再补充这部分详细内容！\n\n#### 2.4 微信JS SDK\n\n微信是一个特殊的存在；对于schema，微信会做出来拦截，导致通过 schema协议无法唤起APP;想要在微信中唤起APP，需要通过微信js sdk提供的接口进行唤起APP,目前微信并未在其开放平台上说明其接口，只有部分在微信白名单（其实就是和腾讯有合作关系的公司）中的应用程序可使用对应的接口进行唤起APP。\n\n目前富途牛牛已经接入该功能，接入方会收到对应的接口文档，按照文档进行接入接口，不确定是否涉及到需要保密的内容，故不作公开此处做法。如需接入，先联系微信相关部门。\n\n微信中，比较常用的做法是：\n\n1. 直接提示去下载页面\n2. 提示在外部浏览器中打开，在外部浏览器中通过schame等方式打开APP\n\n#### 2.5 QQ JS SDK\n\n和微信同属腾讯，qq确对外开放了唤起APP的相关接口权限。\n\n- 检测外部app是否安装,见[isAppInstalled](http://open.mobile.qq.com/api/common/index#api:isAppInstalled)\n\n![](https://cloudmain.futunn.com/test/business-qq/isinstalled.png?_=1538550953571)\n\n- 启动外部app,见[launch](http://open.mobile.qq.com/api/common/index#api:launchApp)\n\n![](https://cloudmain.futunn.com/test/business-qq/open.png?_=1538550953571)\n\n本来，心里想的很美的，使用QQ提供的api来打开APP,效果应该更好；想法是这样的：\n\n1. 使用isAppInstalled判断APP是否安装，如果已安装，则调用launchApp打开APP\n2. 如果没有判断没有安装外部APP，则可调整至下载页\n\n于是，写了个demo来测试，但是。。。。调用isAppInstalled，api给出的提示是“permission denied”，直接调用launchApp方法也不可行，网上找了很久也有相关说明，QQ的api平台也没有反馈入口，。。。只好放弃这种方式；**猜想只有 腾讯认可的域名中才能调用相关API。**\n\n**QQ webview本身也能通过schema的方式唤起APP,所以还是老实使用这种方式。**\n\n## 3、实现过程\n\n首先，通过**浏览器是无法判断是否安装了客户端程序的**，因此整体的思路就是：**尝试去通过上面的唤起方式来唤起本地客户端，如果唤起超时，则直接跳转到下载页**；整个实现过程围绕这一点展开。\n\n>2018年补充：QQ的jssdk目前已经支持判断外部APP是否安装\n\n在不考虑IOS9 Universal links唤醒方式的条件下，可以分为这几个步骤；\n\n#### 3.1 生成schema字符串\n\n首先判断浏览器UA，如果为Chrome for Android，则必须安装 Android Intent的方式来组织schema字符串；如果为其他浏览器，则按照普通的schema方式来返回即可；\n\n```js\n// 如果是安卓chrome浏览器，则通过intent方式打开\n// UC/QQ浏览器被识别为chrome，排除之\nif (browser.isChrome() && browser.isAndroid() && browser.isUC() === false && browser.isQQBrowser() === false) {\n    schemaStr = 'intent://' + schemaStr +'#Intent;'  +\n                'scheme='   + this.appConfig.PROTOCAL          + ';'+\n                'package='  + this.appConfig.APK_INFO.PKG      + ';'+\n                'category=' + this.appConfig.APK_INFO.CATEGORY + ';'+\n                'action='   + this.appConfig.APK_INFO.ACTION   + ';'+\n                'S.browser_fallback_url=' + encodeURIComponent(this.appConfig.FAILBACK.ANDROID) + ';' +\n                'end';\n} else {\n    schemaStr = this.appConfig.PROTOCAL + '://' + schemaStr;\n}\n```\n\n> 注意参数中包含的url地址需要进行encodeURIComponent编码\n\n#### 3.2 通过iframe或者a标签来加载schema\n\n由于无法确定是否安装了客户端，因此通过window.location =  schema的方式可能导致浏览器跳转到错误页；所以通过iframe.src或a.href载入schema是目前比较常见的方法；\n\n相比于iframe和a，通过设置其diaplay为none来进行隐藏，这样即便链接错误也不会对当前页构成影响，但是对于a标签，在未安装客户端的情况下，仍然会存在提示访问不存在的情况（比如opera），所以在选取上的优先级是：iframe>a>window.location,只有在iframe.href 无法调用schema的情况下，才采用a.href的方式。\n\n经过非全面测试：\n\n- Android系统上，Chrome for Android无法通过iframe.src 来调用schema，而通过a.href 的方式可以成功调用，而针对chrome内核的浏览器如猎豹，360，小米浏览器，\nopera对于iframe.src和a.href的方式都能支持，所以对chrome及先关的内核的浏览器采用a.href的方式来调用scheme；对于其他浏览器，如firefox,mobile QQ，sogou浏览器则采用iframe.src的方式调用schema。对于微信浏览器，则直接跳转到下载页。其他未经测试的浏览器，默认采用iframe.src来调用schema；\n- IOS 9+系统上，Safari浏览器无法通过iframe.src的方式调用schema，对于UC，Chrome，百度浏览器，mobileQQ只能通过a.href的方式进行调用schema；对于微信浏览器，默认跳转到下载页；\n\n代码如下：\n\n```js\n// 需要使用的schema\nvar schemaUrl = this.generateSchema(config.targetURI);\nvar body = document.body;\n\n// Android 微信不支持schema唤醒，必须提前加入腾讯的白名单\n// 百度浏览器会拦截schema，所以直接跳下载页\n// QQ,weobo 内也直接跳转下载页\nif (browser.isWx() || browser.isBaidu()\n    || (browser.isIOS() && browser.isMobileQQ())\n    || (browser.isIOS() && browser.isAlipay())\n    || browser.isWeibo()) {\n    if (browser.isAndroid()) {\n        window.location.href = this.appConfig.FAILBACK.ANDROID;\n    } else if(browser.isIOS()){\n        window.location.href = this.appConfig.FAILBACK.IOS;\n    }\n// Android chrome 不支持iframe 方式唤醒\n// 适用：chrome,leibao,mibrowser,opera,360，UC,qq浏览器\n} else if (browser.isChrome() && browser.isAndroid()\n    || browser.isUC()\n    || browser.isSafari()\n    || browser.isQQBrowser()) {\n    var aLink = util.createALink(schemaUrl);\n    body.appendChild(aLink);\n    aLink.click();\n// 其他浏览器\n// 适用：sogou,firefox,mobileQQ\n} else {\n    var iframe = util.createIfr(schemaUrl);\n    body.appendChild(iframe);\n}\n```\n\n#### 3.3 处理客户端未安装的情况\n\n前面提到无法确定客户端程序是否安装，所以在通过iframe和a调用schema时，会设置一个settimeout，超时，则跳转到下载页；\n\n>此处的超时时间设置也十分关键，如果超时时间小于app启动时间，则未待app启动，就是执行setimeout的方法，如果超时时间较长，则当客户端程序未安装时，需要较长时间才能执行settimeout方法进入下载页。因此，此处的延时时间需要考虑APP的启动时间。\n\n```\nvar start = new Date().getTime();\nvar that = this;\nvar loadTimer = setTimeout(function() {\n    if (util.isDocHidden()) {\n        return;\n    }\n    // 如果app启动，浏览器最小化进入后台，则计时器存在推迟或者变慢的问题\n    // 那么代码执行到此处时，时间间隔必然大于设置的定时时间\n    if (Date.now() - start > that.appConfig.LOAD_WAITING + 200) {\n        //console.log('come back from app')\n    // 如果浏览器未因为app启动进入后台，则定时器会准时执行，故应该跳转到下载页\n    } else {\n        if (fail) {\n            fail();\n        } else {\n            window.location.href = browser.isIOS() ? that.appConfig.FAILBACK.IOS : that.appConfig.FAILBACK.ANDROID;\n        }\n    }\n\n}, this.appConfig.LOAD_WAITING);\n```\n\n>代码中，进入到setimeout时，对跳转过程再次进行了限定；当浏览器因为启动app而切换到后台时，settimeout存在计时推迟或延迟的问题，此时，如果从app切换回浏览器端，则执行跳转代码时经历的时间应该大于setimeout所设置的时间；反之，如果本地客户端程序未安装，浏览器则不会进入后台程序，定时器则会准时执行，故应该跳转到下载页！\n\n在实际测试过程，当通过schema成功唤起客户端，再次返回浏览器时，发现页面已跳转至下载页面，因此对已设置的settimeout需要做一个清除处理；\n\n当本地app被唤起，app处于设备可视窗口最上层，则浏览器进入后台程序页面会隐藏掉，会触发pagehide与visibilitychange事件，此时应该清除setimeout事件,于此同时，document.hide属性为true，因此setimeout内也不做跳转动作，防止页面跳转至下载页面；\n\n此时，有几个事件比较关键：\n\n```html\npagehide: 页面隐藏时触发\nvisibilitychange： 页面隐藏没有在当前显示时触发，比如切换tab，也会触发该事件\ndocument.hidden 当页面隐藏时，该值为true，显示时为false\n```\n\n为了尽可能的兼容多的浏览器，所以讲这几个事件都进行绑定！\n代码如下。\n\n```\n// 当本地app被唤起，则页面会隐藏掉，就会触发pagehide与visibilitychange事件\n// 在部分浏览器中可行，网上提供方案，作hack处理\ndocument.addEventListener(util.visibilityChangeName(), function() {\n    if (util.isDocHidden) {\n        clearTimeout(loadTimer);\n    }\n}, false);\n\n// pagehide 必须绑定到window\nwindow.addEventListener('pagehide', function() {\n    clearTimeout(loadTimer);\n    //success && success();\n}, false);\n```\n\n\n---\n\n## 4、测试结果\n\n#### 4.1 Android平台（小米3/6 手机测试）\n\n- 经测试，可唤起chrome，Firefox，uc，360，mibrowser，sogou，liebao，mobileQQ浏览器；\n- 新版opera浏览器采用webkit内核，但是当客户端未安装时跳转下载页会会出错，提示页面不存在；\n- 微信不支持登陆，直接做了跳转到下载页处理；\n- Android上启动相对比较慢，导致很容易启动超时而跳转到下载页面；\n- 测试页面在本机，百度浏览器会上报检测url合法性，导致唤醒不成功,故百度浏览器上直接跳转至下载页\n\n![](http://i.imgur.com/917kCq1.png)\n\n#### 4.2 IOS平台（ipx）\n\n- os7上Safari可用，其他浏览器为测试，条件限制；\n- Safari，UC浏览器，Chrome 浏览器能唤起nn客户端，但是Safari会有 是否打开的提示；\n- QQ webviwe上能打开，偶尔会失败；2016年的时候测过还可以，现在不可以打开了；\n- IOS上启动速度相对较快\n- IOS上，在不能唤起APP的情况下，跳转至APP Store的下载链接可通过APP store间接唤起APP\n\n## 5、相关代码\n对代码进行简单的封装，代码如下，在使用时需要针对当前的app做必要设置，采用UMD的写法：\n\n代码见[index.js](https://github.com/AlanZhang001/H5CallUpNative/blob/master/src/index.js)\n\n安装方式：\n\n```\nnpm install tool-callapp --save\n```\n\n调用方式：\n\n```\nvar CallUp = require('tool-callapp');\n\n// 直接引入\n// <script type=\"text/javascript\" src=\"/yourpath/tool-callapp/index.js\"></script>\n\nvar callup = new Callup({\n    // 协议头\n    PROTOCAL:'ftnn',\n\n    // 主页\n    HOME: 'quote',\n\n    // 唤起失败时的跳转链接\n    FAILBACK: {\n        ANDROID: 'http://a.app.qq.com/o/simple.jsp?pkgname=cn.futu.trader&g_f=991653',\n        IOS:'http://a.app.qq.com/o/simple.jsp?pkgname=cn.futu.trader&g_f=991653'\n    },\n\n    // Android apk 相关信息\n    APK_INFO: {\n        PKG: 'cn.futu.trader',\n        CATEGORY: 'android.intent.category.DEFAULT',\n        ACTION: 'android.intent.action.VIEW'\n    },\n\n    // 唤起超时时间，超时则跳转到下载页面\n    LOAD_WAITING: 3000\n});\n\ncallup.loadSchema({\n    // 通过NN打开某个链接,注意不包括协议部分，比如打开行情主页的完整schema是ftnn://quote,这里传的值只有uri部分，即quote\n    targetURI: 'quote'\n});\n\n```\n\n>本来这里提供了success和fail的回调，但是还未做完整测试，故暂时不开放\n\n代码测试结果:\n\n|平台|Android|iOS|\n|:--:|:--:|:--:|\n|weixin|X|X|\n|qq|✔️|X|\n|chrome|✔️|✔️|\n|mi browser|✔️|-|\n|uc|✔️|✔️|\n|qq browser|✔️|✔️|\n|360 browser|✔️|✔️|\n|猎豹|✔️|✔️|\n|sogou|✔️|✔️|\n|baidu browser|X|X|\n|firefox|✔️|✔️|\n|safari|-|✔️|\n|微博国际|X|X|\n|微博|X|X|\n|支付宝|✔️|X|\n\n>✔️表示能唤起APP，X表示不能，-表示品台上没有该浏览器\n\n- android支护宝中唤起时，会先跳转至支付宝提供的中间页，提示会进行跳转。点击\"点击跳转\"之后能唤起APP\n\n<img src=\"https://cloudmain.futunn.com/test/H5CallUpNative/images/WechatIMG7-c3f1e6630de9197f1e08a5f9f9204cc7.png?_=1538561195372\" style=\"width: 40%;\" />\n\n## 6、研究意义\n**便于通过相关H5页面进入Native客户端，提升用户体验，提升App用户粘度；** 对于未安装客户端的用户，可引导进入下载通道，如下场景图：\n\n![](http://i.imgur.com/ymKiJJM.png)\n\n## 7、存在的问题\n\n1. 在没有安装客户端程序的时候，opera无法跳转到指定页的失败页；\n2. 通过微信唤醒客户端目前不可行，Android上需要微信设置白名单；IOS上，需要微信设置白名单或者通过Universal links（IOS9+）协议；\n3. 尚未对IOS9的 Universal links协议进行功能测试。\n4. 代码中使用的各种时间如：settimeout定时时间均根据本机测试进行的调整，普遍性需要进一步验证\n\n## 还没解决的问题\n\n1. 在询问是否打开APP的时候，如果选择了“取消”，则再唤起APP的时候会不起作用。目前并没有什么解决方案，在chrome Android,UC Android上会复现问题。需再次刷新页面才行。\n\n## 最后\n\n1. 经过自行测试及网上查阅资料，目前尚未找到完美的解决方案；\n2. 对于文中的不足和错误，欢迎指出。\n3. 转载请说明出处，以方便追本溯源修正文中错误\n\n## 相关阅读链接\n\n- <https://developer.chrome.com/multidevice/android/intents>\n- <https://segmentfault.com/a/1190000005848133?_ea=938555>\n- <http://www.w3ctech.com/topic/287?utm_source=tuicool&utm_medium=referral>\n- <http://echozq.github.io/echo-blog/2015/11/13/callapp.html>\n\n","readmeFilename":"readme.md"}