o2team / H5Skills

移动端开发技巧集合
831 stars 80 forks source link

手Q分享接口 - 填坑篇 #56

Open leeenx opened 7 years ago

leeenx commented 7 years ago

引子

为什么需要了解手Q的分享接口的坑呢?

理由如下:

  1. 制作手Q的活动页面相对比较少,如果遇到分享再来研究会影响到开发进度
  2. 当前(2015.05.24)手Q页面的公共分享方法已经不可以分享出去了,目前需要自己手动写分享方法
  3. 了解了分享接口的bug,可能对以后其它bug的借鉴作用

如果读者还不知道手Q分享是如果实现的,请先阅读手Q分享接口总结 - 基础篇

手Q取分享信息的优先级

mqq.ui.shareMessage < mqq.data.setShateInfo < meta信息 < title标签和body内容

这里不得不吐槽一下,MobileQQ API 提供给开发者的分享接口或与之相关的接口在ios下百分之九十九都存在bug。

1. mqq.data.setShareInfo有同域污染的bug

产生污染的原因解析

使用mqq.data.setShareInfo在ios下会有同域污染的bug,安卓目前没有发现bug。所以以下描述都是针对ios的

以下是两个DEMO页面:


- shareinfo.html

```html
<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no" name="viewport">
    <meta content="yes" name="apple-mobile-web-app-capable">
    <meta content="black" name="apple-mobile-web-app-status-bar-style">
    <meta content="telephone=no" name="format-detection">
    <meta content="email=no" name="format-detection">
    <title>shareinfo</title>
    <style type="text/css">
    html,body{margin: 0; padding: 0; font-size: .48rem;}
    </style>
</head>
<body ontouchstart>
<a href="meta_share.html">meta_share.html</a>
</body>
<script type="text/javascript" src="http://pub.idqqimg.com/qqmobile/qqapi.js?_bid=152"></script>
<script type="text/javascript">
if(mqq&&mqq.data){
    mqq.data.setShareInfo(
        {
            share_url:location.href,
            title:"shareinfo抬头",
            desc:"shareinfo描述",
            image_url:'http://wqs.jd.com/promote/2015/childrensday/images/share.png'
        }
    );
}
</script>
</html>

请使用iphone扫描二维码分别查看两个页面的分享信息:

meta_share.html:

meta_share二维码

shareinfo.html:

meta_share二维码

确认完这两个页面各自的分享信息后。再次扫描进入shareinfo.html后,点进链接进入页面meta_share.html。你会发现,原来meta_share.html页面的分享信息被shareinfo.html的分享信息取代了!!!

可以推测在ios的手Q中使用mqq.data.setShareInfo后分享信息是被存放在手Q内置webview中以类似localStorage的形式存在。手Q在分享前会先从mqq.data中寻找当前域下的分享信息,如果存在就直接取这个分享信息,如果不存在再去找title和meta信息,如果meta无分享信息直接取body内容。这就是setShareInfo存在bug的内在原因。

解决办法

解决这个bug的办法就是放弃使用非接口方式给页面添加分享信息,而改用setShareInfo或shareMessage来配置分享信息。

2. shareMessage有同域污染的bug

产生污染的原因解析

使用mqq.ui.shareMessage分享时,会产生与setShareInfo相同的bug。不过由于mqq.ui.shareMessage是个调用方法,只有当页面调用了mqq.ui.shareMessage方法之后并且在分享之后(或者是取消分享之后)回到原页面才会产生bug,如果页面未被调用mqq.ui.shareMessage就不会有污染bug。

以下是一个demo页面的代码:

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no" name="viewport">
    <meta content="yes" name="apple-mobile-web-app-capable">
    <meta content="black" name="apple-mobile-web-app-status-bar-style">
    <meta content="telephone=no" name="format-detection">
    <meta content="email=no" name="format-detection">
    <title>sharemessage</title>
    <style type="text/css">
    html,body{margin: 0; padding: 0; font-size: .48rem;}
    </style>
</head>
<body ontouchstart>
<br /><br /><br />
<a href="meta_share.html">meta_share.html</a>
</body>
<script type="text/javascript" src="http://pub.idqqimg.com/qqmobile/qqapi.js?_bid=152"></script>
<script type="text/javascript">
var siv=setInterval(function(){
    if(typeof(mqq)!="undefined"&&typeof(mqq.ui)!="undefined"){
        mqq.ui.setOnShareHandler(function(type){
            if(typeof(type)=="undefined")mqq.ui.showShareMenu();
            else{
                mqq.ui.shareMessage(
                    {
                        title:"shareMessage的title",
                        desc:"shareMessage的desc",
                        share_type:type,
                        image_url:"http://wqs.jd.com/promote/2015/childrensday/images/share.png",
                        puin:"12345",
                        sourceName:"JDC测试"
                    },
                    function(ret){
                        alert(ret.retCode);
                    }
                );
            }
        });
        clearInterval(siv);
    }else{
        arguments.callee.call(this);
    }
},500);
</script>
</html>

扫一扫,体验一下:

sharemessage DEMO

验证过程:

  1. 扫描进去后点右上角点分享给好友;
  2. 在弹出分享框后,点取消返回当前页面
  3. 点击meta_share.html链接,进入meta_share.html页面
  4. 在meta_share.html页面点分享
  5. 验证弹出来的分享框

可以做个假设,在ios的手Q调用mqq.ui.shareMessage的过程中,在后台调用了setShareInfo方法将分享信息存入手Q的缓存中。

解决办法

如果上面的猜测:“在ios的手Q调用mqq.ui.shareMessage的过程中,在后台调用了setShareInfo方法将分享信息存入手Q的缓存中”,正确的话,那么被污染的页面可以使用setShareInfo来解决污染bug。

将sharemessage.html中的链接改指向shareinfo.html 页面。修改如下:

sharemessage2.html

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no" name="viewport">
    <meta content="yes" name="apple-mobile-web-app-capable">
    <meta content="black" name="apple-mobile-web-app-status-bar-style">
    <meta content="telephone=no" name="format-detection">
    <meta content="email=no" name="format-detection">
    <title>sharemessage</title>
    <style type="text/css">
    html,body{margin: 0; padding: 0; font-size: .48rem;}
    </style>
</head>
<body ontouchstart>
<br /><br /><br />
<a href="shareinfo.html">shareinfo.html</a>
</body>
<script type="text/javascript" src="http://pub.idqqimg.com/qqmobile/qqapi.js?_bid=152"></script>
<script type="text/javascript">
var siv=setInterval(function(){
    if(typeof(mqq)!="undefined"&&typeof(mqq.ui)!="undefined"){
        mqq.ui.setOnShareHandler(function(type){
            if(typeof(type)=="undefined")mqq.ui.showShareMenu();
            else{
                mqq.ui.shareMessage(
                    {
                        title:"shareMessage的title",
                        desc:"shareMessage的desc",
                        share_type:type,
                        image_url:"http://wqs.jd.com/promote/2015/childrensday/images/share.png",
                        puin:"12345",
                        sourceName:"JDC测试"
                    },
                    function(ret){
                        alert(ret.retCode);
                    }
                );
            }
        });
        clearInterval(siv);
    }else{
        arguments.callee.call(this);
    }
},500);
</script>
</html>

扫一扫,体验一下:

sharemessage DEMO

验证过程:

  1. 扫描进去后点右上角点分享给好友;
  2. 在弹出分享框后,点取消返回当前页面
  3. 点击shareinfo.html链接,进入shareinfo.html页面
  4. 在shareinfo.html点击分享
  5. 验证弹出来的分享框

通过上面的页面,成功验证了:“在ios的手Q调用mqq.ui.shareMessage的过程中,在后台调用了setShareInfo方法将分享信息存入手Q的缓存中”。

解决shareMessage污染总结:

3. mqq.ui.setTitleButtons会造成全局污染

如mqq.ui.setTitleButtons会造成全局性污染,它的影响不止于同域了,不同域的页面也会被污染。

污染的原因分析

以下是测试DEMO:

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no" name="viewport">
    <meta content="yes" name="apple-mobile-web-app-capable">
    <meta content="black" name="apple-mobile-web-app-status-bar-style">
    <meta content="telephone=no" name="format-detection">
    <meta content="email=no" name="format-detection">
    <title>setTitleButtons</title>
    <style type="text/css">
    html,body{margin: 0; padding: 0; font-size: 24px;}
    </style>
</head>
<body ontouchstart>
<br /><br /><br />
<a href="http://m.baidu.com/">非同域链接</a><br /><br />
<a href="meta_share.html">同域meta_share.html</a>
</body>
<script type="text/javascript" src="http://pub.idqqimg.com/qqmobile/qqapi.js?_bid=152"></script>
<script type="text/javascript">
var siv=setInterval(function(){
    if(typeof(mqq)!="undefined"&&typeof(mqq.ui)!="undefined"){
        mqq.ui.setTitleButtons({
            left:{
                title:"测试",
                callback:function(){
                    alert("你返回不了的");
                }
            },
            right:{
                iconID:3,
                callback:function(){
                    mqq.ui.showShareMenu();
                }
            }
        });
        clearInterval(siv);
    }else{
        arguments.callee.call(this);
    }
},500);
</script>
</html>

扫一扫,体验:

sharemessage DEMO

体验过程:

  1. 扫描二维码进入页面后,点击左上角按钮与右上角按钮确定setTitleButtons已经生效
  2. 进入“非同域链接”
  3. 点击左上角与右上角观察有没有受污染
  4. 退出页面,重新扫二维码进入页面
  5. 进入“同域meta_share.html”
  6. 重复3

通过上面的六个步骤,可以体验到,setTitleButtons对同域与不同域都造成的相同的污染。 污染有两条:

  1. 如果调用了setTitleButtons方法,那么由它配置的左右上角按钮的文本和按钮icon在webview被关闭前都做一直保持;
  2. 左上角的回调在页面跳转时会被回收重置回默认回调,但是右上角的的回调在页面跳转时会被回收,但是不会被重置成默认回调,而是被重置成空(即按下去什么反应都没有)。

第一条污染安卓和ios都会有。第二条污染只在ios下有,而安卓下不存在这个问题。第二条污染造成的结果是十分严重的

解决办法

  1. 对于有MobileQQ API授权的域名(如paipai.com,wanggou.com和jd.com)调用新mqq.ui.setTitleButtons来覆盖上一次setTitleButtons的污染。
  2. *使用setActionButton来重置右上角的按钮回调。(强烈不建议使用setActionButton,因为MobileQQ API已经不维护这个接口了) 当然这样做的结果是会造成新的污染。不过,这个已经是没有办法中的办法了。

对于没有MobileQQ API授权的域名(如baidu.com、qian-duan-she-ji.us等)完全没有办法解决这个污染造成的无法通过点击右上角按钮分享的问题,如果要分享只能是通过web分享接口实现了!!!

4. setActionButton造成的全局污染

虽然setActionButton不建议使用了,但是,我们无法保证我们的上游页面不使用这个方法。

污染的原因分析

setActionButton造成的污染与setTitleButtons造成的污染是同量级的,表现也是相似的,如下DEMO:

setActionButtons.html

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no" name="viewport">
    <meta content="yes" name="apple-mobile-web-app-capable">
    <meta content="black" name="apple-mobile-web-app-status-bar-style">
    <meta content="telephone=no" name="format-detection">
    <meta content="email=no" name="format-detection">
    <title>setActionButton</title>
    <style type="text/css">
    html,body{margin: 0; padding: 0; font-size: 24px;}
    </style>
</head>
<body ontouchstart>
<br /><br />
<a href="http://m.baidu.com/?_wv=1">非同域链接</a><br /><br />
<a href="meta_share.html">同域meta_share.html</a><br /><br />
<a href="setOnShareHandler.html">setOnShareHandler.html</a>
</body>
<script type="text/javascript" src="http://pub.idqqimg.com/qqmobile/qqapi.js?_bid=152"></script>
<script type="text/javascript">
var siv=setInterval(function(){
    if(typeof(mqq)!="undefined"&&typeof(mqq.ui)!="undefined"){
        mqq.ui.setActionButton(
            {
                iconID:3
            },
            function(){
                mqq.ui.showShareMenu();
            }
        );
        clearInterval(siv);
    }else{
        arguments.callee.call(this);
    }
},500);
</script>
</html>

扫一扫,体验:

sharemessage DEMO

体验过程:

  1. 扫描二维码进入页面后,点击左上角按钮与右上角按钮确定setTitleButtons已经生效
  2. 进入“非同域链接”
  3. 点击左上角与右上角观察有没有受污染
  4. 退出页面,重新扫二维码进入页面
  5. 进入“同域meta_share.html”
  6. 重复3

通过上述6步骤,可以得到与setTitleButtons右上角按钮相同的结果,即setActionButton造成的污染如下:

解决办法

解决办法与setTitleButtons是一样的:

  1. 对于有MobileQQ API授权的域名(如paipai.com,wanggou.com和jd.com)调用新mqq.ui.setTitleButtons来覆盖上一次setTitleButtons的污染。
  2. *使用setActionButton来重置右上角的按钮回调。(强烈不建议使用setActionButton,因为MobileQQ API已经不维护这个接口了) 当然这样做的结果是会造成新的污染。不过,这个已经是没有办法中的办法了。
  3. 可以监听setOnShareHandler回调函数的参数,如果为undefined则调用mqq.ui.showShareMenu();

对于没有MobileQQ API授权的域名(如baidu.com、qian-duan-she-ji.us等)完全没有办法解决这个污染造成的无法通过点击右上角按钮分享的问题,如果要分享只能是通过web分享接口实现了!!!

5. SetOnShareHandler的隐式bug

mqq.ui.setOnShareHandler(function(type){
    //type 用户点击的分享类型: 0 - QQ好友; 1 - QQ空间; 2 - 微信好友; 3 - 微信朋友圈
});

按照MobileQQ API给出的文档,setOnShareHandler的回调传入参数type有且只有4个类型参数:0~3。

理论上说是没错。不过,在测试mqq.ui.setActionButton的污染时,我发现这个污染有可能会触发setOnShareHandler的隐式bug:除了0~3的四个值外,还有第五个值"undefined"。 setOnShareHandler.html

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no" name="viewport">
    <meta content="yes" name="apple-mobile-web-app-capable">
    <meta content="black" name="apple-mobile-web-app-status-bar-style">
    <meta content="telephone=no" name="format-detection">
    <meta content="email=no" name="format-detection">
    <title>setOnShareHandler</title>
    <style type="text/css">
    html,body{margin: 0; padding: 0; font-size: .48rem;}
    </style>
</head>
<body ontouchstart>
<br /><br /><br />
<a href="#hash">hash</a>
</body>
<script type="text/javascript" src="http://pub.idqqimg.com/qqmobile/qqapi.js?_bid=152"></script>
<script type="text/javascript">
var siv=setInterval(function(){
    if(typeof(mqq)!="undefined"&&typeof(mqq.ui)!="undefined"){
        mqq.ui.setOnShareHandler(function(type){
            alert(arguments.length);
            alert(type);
        });
        clearInterval(siv);
    }else{
        arguments.callee.call(this);
    }
},500);
</script>
</html>

扫一扫验证(下面的二维码不是setOnShareHandler.html的地址,而是setActionButton.html的地址):

sharemessage DEMO

验证过程:

  1. 扫描进入setActionButton.html页面
  2. 点击进入"setOnShareHandler.html"
  3. 点击右上角按钮

通过上面的验证,可以清楚地知道setOnShareHandler的隐式bug的触发条件是:被setActionButton污染过。另外可以发现第五个值"undefined"是传入的参数的值为undefined,而不是没有传参数造成undefined。

利用这个隐式bug可以解决setActionButton造成的bug。

为了使代码健壮,在写setOnShareHandler时,也要把type==undefined的情况考虑上 如下代码:

var siv=setInterval(function(){
    if(typeof(mqq)!="undefined"&&typeof(mqq.ui)!="undefined"){
        mqq.ui.setOnShareHandler(function(type){
            mqq.ui.shareMessage(
                {
                    title:"shareMessage的title",
                    desc:"shareMessage的desc",
                    share_type:type,
                    image_url:"http://wqs.jd.com/promote/2015/childrensday/images/share.png",
                    puin:"12345",
                    sourceName:"JDC测试"
                },
                function(ret){
                    alert(ret.retCode);
                }
            );
        });
        clearInterval(siv);
    }else{
        arguments.callee.call(this);
    }
},500);

建议改成:

var siv=setInterval(function(){
    if(typeof(mqq)!="undefined"&&typeof(mqq.ui)!="undefined"){
        mqq.ui.setOnShareHandler(function(type){
            if(typeof(type)=="undefined")mqq.ui.showShareMenu();
            else{
                mqq.ui.shareMessage(
                    {
                        title:"shareMessage的title",
                        desc:"shareMessage的desc",
                        share_type:type,
                        image_url:"http://wqs.jd.com/promote/2015/childrensday/images/share.png",
                        puin:"12345",
                        sourceName:"JDC测试"
                    },
                    function(ret){
                        alert(ret.retCode);
                    }
                );
            }
        });
        clearInterval(siv);
    }else{
        arguments.callee.call(this);
    }
},500);

6. mqq.ui是个异步接口

在加载完<script type="text/javascript" src="http://pub.idqqimg.com/qqmobile/qqapi.js?_bid=152"></script> 文件后,mqq.ui有可能还处在undefined状态。为了保证代码能够正常使用,本文章在使用mqq.ui之前都用setInterval做了检测。

总结

因为我们制作的页面几乎都是下游页面(即从其它入口点进来的),为了保证分享代码的绝对可行,我们应当假设我们的页面已经是被接口污染过的页面,所以应当使用mqq.ui.setTitleButtons加mqq.ui.setOnShareHandler等接口重新定制分享过程。可能参考如下代码:

var siv=setInterval(function(){
    if(typeof(mqq)!="undefined"&&typeof(mqq.ui)!="undefined"){
        mqq.ui.setTitleButtons({
            left:{
                title:"测试",
                callback:function(){
                    alert("你返回不了的");
                }
            },
            right:{
                iconID:3,
                callback:function(){
                    mqq.ui.showShareMenu();
                }
            }
        });
        mqq.ui.setOnShareHandler(function(type){
            if(typeof(type)=="undefined")mqq.ui.showShareMenu();
            else{
                mqq.ui.shareMessage(
                    {
                        title:"shareMessage的title",
                        desc:"shareMessage的desc",
                        share_type:type,
                        image_url:"http://wqs.jd.com/promote/2015/childrensday/images/share.png",
                        puin:"12345",
                        sourceName:"JDC测试"
                    },
                    function(ret){
                        alert(ret.retCode);
                    }
                );
            }
        });
        clearInterval(siv);
    }else{
        arguments.callee.call(this);
    }
},500);
tflower commented 7 years ago

你好,请教个问题,我调用mqq的方法全部失败,查看各种文档,貌似是说要网站接入qq的平台才能够正常访问?找了半天,没看明白接入的方法,也么有看到文档里需要配置openid的说法。请问你是怎么弄的呀?

leeenx commented 7 years ago

@tflower 我们的域名是白名单,所以我们有相关的权限。如果非白名单的话,很多权限是用不了的。不过,文章比较久远了,你可以看一下手Q最新的 API:https://open.mobile.qq.com/api/common/index#api:qbrowserVisibilityChange

tflower commented 7 years ago

可以请教下白名单是怎么申请的嘛,腾讯各种文档,实在是没怎么找到相关资料

tflower commented 7 years ago

表示这些api试了个遍==

tflower commented 7 years ago

确实是因为域名接入的问题,这边把你的demo代理到本地是好的,换成我们的域名就gg了。

leeenx commented 7 years ago

@tflower 白名单我没有申请过,因为我们公司原本跟腾讯有合作关系,所以默认有权限。建议你去咨询一下腾讯官方

tflower commented 7 years ago

这样子,好的,感谢回复

lingdu1003 commented 6 years ago

@tflower 域名权限问题解决了吗 我也遇到同样问题!

juicechu commented 6 years ago

我也是遇到白名单问题,请问白名单在哪里申请?

asakurayoh1987 commented 6 years ago

@tflower 白名单的问题解决了吗,我也想知道在哪可以申请

JTT5327 commented 4 years ago

setOnShareHandler 这个方法在ios qq(最新版qq)上无法执行,自定义分享文案无效,这个有遇到过么?

qq88519319 commented 3 years ago

@leeenx 请问现在哪里还能申请白名单呢?你们是什么公司啊,当时是怎么跟腾讯合作的

qq88519319 commented 3 years ago

@

这样子,好的,感谢回复

请问你申请到白名单了吗?

qq88519319 commented 3 years ago

@tflower 我们的域名是白名单,所以我们有相关的权限。如果非白名单的话,很多权限是用不了的。不过,文章比较久远了,你可以看一下手Q最新的 API:https://open.mobile.qq.com/api/common/index#api:qbrowserVisibilityChange

请问现在哪里还能申请白名单呢?你们是什么公司啊,当时是怎么跟腾讯合作的