js与app的通信

原文(搜藏备用):

http://blog.csdn.net/sky_monkey/article/details/22938125
http://blog.csdn.net/jerryvon/article/details/7489573
http://www.cnblogs.com/yexiaochai/p/3439179.html
  1. </pre>最近在做一个项目,该项目的前身是为mobile browser量身打造的一个网站。现在有这样一个需求:<p></p><p>当用户在用mobile browser浏览该网站的时候会点击一个按钮/超链接,通过这个按钮的点击事情需要打开安装在本机的应用程序,或者如果本机没有安装该应用程序则打开应用商店并打开该程序在商店中的搜索结果页面。</p><p></p><p>刚开始的时候iPhone team的人给出一个solution,下面是实施跳转的HTML + javascript源代码。</p><p></p><pre name=“code” class=“html”><html>
  2.     <head>
  3.         <meta name=“viewport” content=“width=device-width” />
  4.     </head>
  5.     <body>
  6.         <h2><a id=“applink1” href=“mtcmtc://profile/116201417”>Open scheme(mtcmtc) defined in iPhone with parameters </a></h2>
  7.         <h2><a id=“applink2” href=“unknown://nowhere”>open unknown with fallback to appstore</a></h2>
  8.         <p><i>Only works on iPhone!</i></p>
  9.         <script type=“text/javascript”>
  10.             // To avoid the “protocol not supported” alert, fail must open another app.
  11.             var appstore = “itms://itunes.apple.com/us/app/facebook/id284882215?mt=8&uo=6”;
  12.             function applink(fail){
  13.                 return function(){
  14.                     var clickedAt = +new Date;
  15.                     // During tests on 3g/3gs this timeout fires immediately if less than 500ms.
  16.                     setTimeout(function(){
  17.                               // To avoid failing on return to MobileSafari, ensure freshness!
  18.                               if (+new Date – clickedAt < 2000){
  19.                               window.location = fail;
  20.                               }
  21.                               }, 500);
  22.                 };
  23.             }
  24.             document.getElementById(“applink1”).onclick = applink(appstore);
  25.             document.getElementById(“applink2”).onclick = applink(appstore);
  26.             </script>
  27.     </body>
  28. </html>

其原理就是为HTML页面中的超链接点击事件增加一个setTimeout方法.

如果在iPhone上面500ms内,本机有应用程序能解析这个协议并打开程序,则这个回调方法失效;如果本机没有应用程序能解析该协议或者500ms内没有打开个程序,则执行setTimeout里面的function,就是跳转到apple的itunes。
我用同样的原理来处理android的javascript跳转,发现如果本机没有程序注册intent-filter for 这个协议,那么android内置的browser就会处理这个协议并且立即给出反应(404,你懂的),不会像iPhone一样去执行setTimeout里面的function,即便你把500ms改成0ms也不管用。

我就开始了我的Google search之旅,最终在stackoverflow一个不起眼的地方找到solution。

不解释,先给出源代码

android里面androidManifest.xml文件对activity的配置,如何配置就不表述了,表达能力有限,请参考developer.android.com

  1. <activity android:name=“.ui.UploadActivity” android:screenOrientation=“portrait”>
  2.             <intent-filter>
  3.                 <data android:scheme=“http” android:host=“192.168.167.33” android:port=“8088” android:path=“/mi-tracker-web/download.html”/>
  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.             </intent-filter>
  8.         </activity>

HTML页面中指向该应用程序的hyperlink

  1. <a id=“applink1” href=“http://192.168.167.33:8088/mi-tracker-web/download.html”>
  2.             Open Application</a>

不难发现,在androidManifest.xml中配置的filter中data的属性表述,在下面的HTML.href中全部看到了。请注意,这两个路径要全部一致,不能有差别,否则android系统就不会拦截这个hyperlink。

好了,为什么我说这个solution能解决我们当初提出来的需求呢,答案在这里:

如果说本机安装了这个应用程序

在android browser中点击HTML中的applink1,browser会重定向到指定的链接,但是由于我们的应用程序在android OS中配置了一个intent-filter,也是针对这个制定的链接。就是说现在android系统有两个程序能处理这个链接:一个是系统的browser,一个是配置了intent-filter的activity。现在点击这个链接,系统就会弹出一个选择:是用browser还是你指定的activity打开。如果你选择你的activity,系统就会打开你的应用程序,如果你继续选择用browser,就没有然后了。

如果说本机木有安装这个应用程序

那么这个HTML里面的这个超链接就起很重要的左右了,这个download.html里面可以forward到android的应用商店

download.jsp源代码如下。具体为什么请求的是download.html这个地址却访问到了download.jsp,就不解释了,struts2的东西。

  1. <%@ page language=“java” contentType=“text/html; charset=ISO-8859-1”
  2.     pageEncoding=“ISO-8859-1”%>
  3. <!DOCTYPE html PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN” “http://www.w3.org/TR/html4/loose.dtd”>
  4. <html>
  5. <head>
  6. <meta http-equiv=“Content-Type” content=“text/html; charset=ISO-8859-1”>
  7. <title>Insert title here</title>
  8. </head>
  9. <body>
  10.     <script type=“text/javascript”>
  11. <span style=“white-space:pre”>    </span>window.location=“market://search?q=com.singtel.travelbuddy.android”;</script>
  1. </body>
  2. </html>

唉,文笔不行,估计我写出来自己都不怎么看得懂。就再把跳转的关键点说一下:

在androidManifest.xml中定义intent-filter的时候定义的scheme,host,port,path拼凑起来是一个有用的HTTP路径,这样就算本机没有activity定义了intent-filter来捕获这个链接,那这个链接也会重定向到打开android market place的页面,继而打开应用商店。因为每个android手机都会捕获到market这个协议(如果android手机里面没有market商店,不怪我哈),系统就会自动打开market place应用商店并根据参数进入搜索页面并显示结果。

扯淡

这个月比较倒霉,我送了女朋友一台笔记本电脑作为生日礼物,结果15天一过电脑就坏了,悲剧的我还把电脑盒子给扔了!淘宝不给换更不给退

于是被女朋友臭骂了一过星期后,今天本来在公司有任务的,但是去修了个电脑(换主板啊……),一蹉跎就快五点了,现在反正干劲也不行,就写篇博客吧

需求

上周五的时候,确切说是周四晚上,老板终于又递上了一个神奇的需求

浏览器检测手机是否安装app

尼玛,反正在我看来,这个需求够奇葩的,当时我还一致认为不能完成,但是最后也居然想出了(抄出了)一个办法,于是这里拿出来与各位分享下

在此之前,我们继续聊下上次遇到的tap点透问题

tap“点透”再探索

http://www.cnblogs.com/yexiaochai/p/3377900.html

http://www.cnblogs.com/yexiaochai/p/3391015.html

看过上面两篇博客的的朋友应该知道我们为什么要使用tap事件替换click事件,然后大概知道tap会带来哪些问题,以及我是如何解决这些问题的

虽然完整解决方案因为公司财产问题未拿出来,但是基本思路是有了,其核心就是蒙版遮盖!

话说还头,这个蒙版其实还是不太能让人接受,这不我又请教了我一个同事,我们同事提出了一个属性:

pointer-events属性值详解

  • auto——效果和没有定义pointer-events属性相同,鼠标不会穿透当前层。在SVG中,该值和visiblePainted的效果相同。
  • none——元素不再是鼠标事件的目标,鼠标不再监听当前层而去监听下面的层中的元素。但是如果它的子元素设置了pointer-events为其它值,比如auto,鼠标还是会监听这个子元素的。
  • 其它属性值为SVG专用,这里不再多介绍了。

这个属性,通过前端观察/和张鑫旭的博客再深入了解一番过后发现,这个家伙可以消除一个元素的鼠标事件呢!!!自然也包括touchstart了,于是老夫感兴趣了,便有了今天这个插曲

是否继承

我们首先来看看这个属性是否会被继承:

http://sandbox.runjs.cn/show/teegz43u

复制代码
 1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 2 <html xmlns="http://www.w3.org/1999/xhtml">
 3 <head>
 4     <title></title>
 5     <meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
 6     <meta content="telephone=no" name="format-detection" />
 7     <meta name="apple-mobile-web-app-capable" content="yes" />
 8     <style>
 9         div { display: block; border: 1px solid black;  margin: 10px; padding: 10px; }
10     </style>
11     <script id="others_zepto_10rc1" type="text/javascript" class="library" src="/js/sandbox/other/zepto.min.js"></script>
12     
13 </head>
14 <body>
15 
16     <div id="p1">
17         p1
18         <div id="p1-1">
19             p1-1
20             <div id="p1-1-1">
21                 p1-1-1
22             </div>
23              <div id="p1-1-2">
24                 p1-1-2
25             </div>
26         </div>
27     </div>
28 
29      <div id="p2">
30         p2
31         <div id="p2-1">
32             2-1
33             <div id="p2-1-1">
34                 p2-1-1
35             </div>
36              <div id="p2-1-2">
37                 p2-1-2
38             </div>
39         </div>
40     </div>
41 
42 </body>
43 <script type="text/javascript">
44     $('div').click(function () {
45         alert($(this).attr('id'))
46 
47     })
48 </script>
49 </html>
复制代码

首先,现在鼠标点击会有一个冒泡的过程,所以会一次弹出提示框,现在我们将下面的p2的style加上我们的pointer-events属性试试点击是否有效果

结果表明,设置了pointer-events: none; 后,该元素以及下面的元素的click事件全部没有了

甚至是我们的input框都不会获取焦点了!!!,所以这个家伙确实够厉害,但是不会取消事件冒泡

有了这个结论,我们上我们的重量级代码吧,这个代码请各位用手机测试

属性解决点透

 

复制代码
 1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 2 <html xmlns="http://www.w3.org/1999/xhtml">
 3 <head>
 4     <title></title>
 5     <meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
 6     <meta content="telephone=no" name="format-detection" />
 7     <meta name="apple-mobile-web-app-capable" content="yes" />
 8     <style>
 9         #list { display: block; position: absolute; top: 100px; left: 10px; width: 200px; height: 100px; }
10         div { display: block; border: 1px solid black; height: 500px; width: 100%; }
11         #input { width: 80px; height: 200px; display: block; }
12     </style>
13     <script id="others_zepto_10rc1" type="text/javascript" class="library" src="/js/sandbox/other/zepto.min.js"></script>
14 </head>
15 <body>
16     <div id="list" style="background: gray;">
17     </div>
18     <div id="wrapper">
19         <div id="d">
20             <input type="text" id="input" />
21         </div>
22     </div>
23 </body>
24 <script type="text/javascript">
25     var list = $('#list');
26     var d = $('#d');
27     var input = $('#input');
28     input.tap(function (e) {
29         input.val(new Date().getTime());
30     });
31     list.tap(function (e) {
32         //        $('input').css("pointer-events", "none");
33         list.hide();
34         setTimeout(function () {
35             list.show();
36             //            $('input').css("pointer-events", "auto");
37         }, 1000);
38     });
39     d.tap(function () {
40         d.append($('<p>div tap</p>'));
41     });
42     
43 </script>
44 </html>
复制代码

 

http://sandbox.runjs.cn/show/wub3i7fr

这里各位请使用手机访问试试,现在这个网页有几个问题:

我点击灰色区域会将灰色区域隐藏,灰色区域消失,一秒后重现,于是可能发生两个问题:

① 某些浏览器中后面的div tap事件会触发

② input元素必定获得焦点

div事件有些时候我们可以通过阻止冒泡处理,但是input这个问题基本不可调和,因为其获得焦点弹出键盘十分恼火

元素我们是通过一个蒙版解决,先我们来看看是否可以给我们的容器wrapper加上一个css属性解决呢???

复制代码
 1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 2 <html xmlns="http://www.w3.org/1999/xhtml">
 3 <head>
 4     <title></title>
 5     <meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
 6     <meta content="telephone=no" name="format-detection" />
 7     <meta name="apple-mobile-web-app-capable" content="yes" />
 8     <style>
 9         #list { display: block; position: absolute; top: 100px; left: 10px; width: 200px; height: 100px; }
10         div { display: block; border: 1px solid black; height: 500px; width: 100%; }
11         #input { width: 80px; height: 200px; display: block; }
12     </style>
13     <script id="others_zepto_10rc1" type="text/javascript" class="library" src="/js/sandbox/other/zepto.min.js"></script>
14 </head>
15 <body>
16     <div id="list" style="background: gray;">
17     </div>
18     <div id="wrapper">
19         <div id="d">
20             <input type="text" id="input" />
21         </div>
22     </div>
23 </body>
24 <script type="text/javascript">
25     var list = $('#list');
26     var d = $('#d');
27     var input = $('#input');
28     input.tap(function (e) {
29         input.val(new Date().getTime());
30     });
31     list.tap(function (e) {
32         $('#wrapper').css("pointer-events", "none");
33         list.hide();
34         setTimeout(function () {
35             $('#wrapper').css("pointer-events", "auto");
36         }, 350)
37         setTimeout(function () {
38             list.show();
39         }, 1000);
40     });
41     d.tap(function () {
42         d.append($('<p>div tap</p>'));
43     });
44     
45 </script>
46 </html>
复制代码

http://sandbox.runjs.cn/show/1wldtigt

可以看到,我们解决了点透的问题,所以有我有这些厉害的同事是很幸运的,有了这个方案,我们就可以在zepto中封装我们的代码了

在tap点击后为容器标签设置该属性,350ms后取消即可,这样可以最大程度的包装代码不会修改

但是这个方案不是没有问题:

最重要的是他依然有个时间阀值,经过我测试时350ms,意思是我使用tap事件后会有350ms的事件某些区域完全无法点击

那么容器所占区域小的话还可接受,如果所占区域大的话就是噩梦,因为用户的点击明显会出现阻力

然后,毕竟会点透的情况不是多数,所以此方案仍有缺陷,最后还是得动一些其它力气

后续

这次tap的研究先到这里,我们我们有机会再继续,所以回到我们今天的主题吧!!!

浏览器检测手机是否安装app

这个需求初次提出其实让人感觉很为难,至少我是很为难的,因为压根就找不到办法嘛,经过周四晚上的纠结,基本就放弃了

第二天也给老大说搞不定,老大基本也仍为搞不定,但是这个需求是最大的老大提出的,所以跟进力度很强,这不是,过了没多久就来了个vip(代表级别高……)

哥来了就寄出了法宝,先是一个国外的网站,然后就是我们传说中的淘宝了

PS:说实话,虽然电脑的事情与淘宝无关,但是老夫现在对淘宝还是比较怨念的!!!!

淘宝不愧是业内技术领先的技术团队,我们来看看他的网站:

其它不必关注,我们就看这个“立即打开”!!!各位知道手机上这个立即打开干了什么吗?

这个家伙不得了,如果按照了app 的话点击立即打开就会打开app,如果没有按照的话居然跳向了市场连接

在某些时候这个对等与他有一个a标签会根据需求而获得不同的值!!!

但是,我们知道打开app是安装了app才会打开的,不然就是个死链接,死链接必然打不开!于是带着好奇带着敬畏,我们打开了淘宝的js库

PS:淘宝压缩混淆后js确实很小,而且没有使用类库哦

 

各位看官直接找到这个目录吧,于是进去我们一步步跟进去:

① 点击点

虽然小生本领不行,但是读代码还是入门的,所以国内一旦出了什么心技术,基本很快就能普及,这就是国内的技术,不是做不出来,就是不知道做出了是什么样子

一旦你做出了,那么我也能做出了,并且做的更好,所以我们缺乏创新啊……

我们找到了入口,于是进入install方法

② install

这群代码一目了然,读到这里,其实可以很轻易的猜测实现方案了!

③ 猜测实现方案

于是我就开始猜测,猜测的结果就是:

如果安装app 的情况下,打开链接会让window失去焦点,于是清除了计时器
如果没有安装app计时器里面的代码会执行,所以跳向了app市场

当然,最后发现一个问题:手机上网页无法失去焦点,这只是我自己的判断(无法失去焦点),所以最后也放弃了这个猜测

④ 陷入僵局

于是思路再次陷入僵局,任务不能实现,但是将淘宝代码搞下来,也无法实现,最后就开始以各种漫无目的,垃圾的办法搞,终于不注意成功了一次!!!

无意义的成功是因为将定时器设置的很大……

⑤ 解决方案

最后发现了方案,安装app 的情况下,网页会进入后台打开app!!!

网页进入后台后会挂起js 的执行,但是这个期间有600-1000ms的时间js仍然会执行

淘宝执行的阀值是600,我们大概是900,所以一直在原地踏步了很久

这个网页进入后台却成了解决问题的关键,于是新鲜代码出来了:

复制代码
 1 var log = function (msg) {
 2     $('body').before('<div class="log">' + msg + '</div>');
 3 };
 4 var timeout, t = 1000, hasApp = true;
 5 setTimeout(function () {
 6     if (hasApp) {
 7         log('安装了app');
 8         $('#dl_app').hide();
 9 
10     } else {
11         log('未安装app');
12         $('#dl_app').show();
13         log('开始强制下载');
14         forceDownload();
15     }
16 }, 2000)
17 function testApp() {
18     var t1 = Date.now();
19     var ifr = $('<iframe id="ifr"></iframe>')
20     ifr.attr('src', '您们app的协议');
21     $('body').append(ifr);
22     timeout = setTimeout(function () {
23         try_to_open_app(t1);
24     }, t);
25 }
26 function try_to_open_app(t1) {
27     var t2 = Date.now();
28     if (!t1 || t2 - t1 < t + 200) {
29         hasApp = false;
30     }
31 }
32 testApp();
复制代码

将这段代码加入网站首页1-2秒后hasApp就会告诉我们是否安装了app,当然问题也很明显:

缺陷

① 经测试,如果未安装app的情况下,safari会给出一个alert类似的提示,老夫将它去不掉!!!

PS:如果各位知道怎么去掉,请赐教

② 进入H5站点,如果安装了app便会打开app,这个是无法避免的

除了上面两个较明显的缺陷,其它还好了……

结语

我们今天的学习暂时到此,也不知道对各位有没有用

少扯淡,直接上代码:

  1. <span style=“font-size:18px;”><script>
  2. function startAPP(){
  3. window.location = “**://**.**/**”;
  4. }
  5. function downloadAPP(){
  6. window.location = “http://IP/**/**.apk”;
  7. }
  8. </script>
  9. <style type=“text/css”>
  10.         body{background:#D2D460;text-align:center;margin-top:10%}
  11.         div{width:100%;margin:0 auto;background:#fff;text-align:left;}
  12. </style>
  13. </head>
  14. <body>
  15. <?php
  16. $var_name =  $_GET[“isappinstalled”];
  17. if ($var_name==“1”){
  18.     echo ‘<img src=images/start.png alt=打开APP onclick=startAPP() ></img>’;
  19. }else if ($var_name==“0”){
  20.     echo ‘<img src=images/download.png alt=打开APP onclick=downloadAPP() ></img>’;
  21. }else{
  22.     echo ‘<img src=images/welcome.png alt=打开APP onclick=startAPP() ></img>’;
  23. }
  24. ?>
  25. </body></span>

在app里面需要做以下设置(一般是在manifest.xml文件的activity的 intent filter里面)

  1. <span style=“font-size:18px;”><intent-filter>
  2.                <action android:name=“android.intent.action.VIEW” />
  3.                <category android:name=“android.intent.category.DEFAULT” />
  4.                <category android:name=“android.intent.category.BROWSABLE” />
  5.                <data
  6.             android:scheme=“**”
  7.             android:host=“**.**”
  8.             android:pathPrefix=“/**”>
  9.          </data>
  10. </intent-filter></span>

发表评论

电子邮件地址不会被公开。 必填项已用*标注