记录 App webview加载h5页面有上传图片,应用商店审核必须加授权提示问题的解决方案

场景:
1、项目内加载了h5页面的七鱼客服,(相当于webview 加载help.html)
2、应用上架要求必须有授权提示弹窗
解决思路
1、客服的h5 页面在跳转时候,给父层webview 发消息,注入一段拦截代码,监听到有上传操作时候,调用原生的授权提示,下次点击时候就可以根据返回的授权信息判断是否要执行input file 的click事件 。

注:项目用uniapp 写的,自己写了一套webview 和 h5 通信的逻辑,文中$abAct(‘getImagePermission’, ‘’, function(err,succ) {}) ; p o s t M e s s a g e ( " k e f u I n j e c t " ) ; 都是通信交互方法,其中 postMessage("kefuInject");都是通信交互方法,其中 postMessage("kefuInject");都是通信交互方法,其中abAc是有回调的。可以自行处理这块逻辑

客服的html
help.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
  </head>
  <style>
    #YSF-BTN-HOLDER {
      display: none;
    }
  </style>

  <body>
  </body>
  <script src="这里是引入我通信的js代码"></script>
  <script type="text/javascript">
    let kefuCode = query("kefuCode");
    let url = "";
    let plt = '"https://qiyukf.com/script/';
    if (location.href.startsWith("https")) {
      url = "https://qiyukf.com/script/";
    } else {
      url = "http://qiyukf.com/script/";
    }
    if (!kefuCode || kefuCode == 0) {
      url = url + "七鱼客服的key" + ".js";
    } else {
      url = url + kefuCode + ".js";
    }
    url = url + "?subdomain=1";

    console.log("加载客服js", url);
    var script = document.createElement("script");
    script.type = "text/javascript";
    script.src = url;
    var flage = false;
    script.onload = function () {
      // 加载完成后的操作
      initKefu();
    };
    document.body.appendChild(script);
    window.onload = function () {
      initKefu();
    };

    function initKefu() {
      if (flage) {
        return;
      }
      flage = true;
      ysf.on({
        onload: function () {
          var data = [];
          try {
            data = query("data")
              ? JSON.parse(decodeURIComponent(query("data")))
              : [];
            if (data.length > 1 && data[1].url) {
              ysf("product", {
                show: 1, // 1为打开, 其他参数为隐藏(包括非零元素)
                title: data[1].title,
                desc: data[1].desc,
                picture: data[1].picture,
                note: data[1].note,
                url: data[1].url,
              });
              data.splice(1, 1);
            }
          } catch (e) {
            console.error(e);
          }
          var uid = query("uid") ? query("uid") : GetRandomNum(10000, 999999);
          console.log("uiduiduiduid", uid);
          var cfg = {
            uid: uid,
            name: query("name"),
            mobile: query("mobile"),
            data: JSON.stringify(data),
            success: function () {
              // 成功回调
              console.log(ysf.url());
              let ysfurl = ysf.url();
              if (data[0].templateId) {
                ysfurl = ysf.url() + "&templateId=" + data[0].templateId;
              }
            
              console.log(uniReady);
             
              try {
                if (ab_.uniApp && query("newHelp")) {
                  uniReady(() => {
                  	//这里是调用获取授权的方法,是自己封装的 webview 和 h5 的通信方式,可自行处理
                    $postMessage("kefuInject");
                    location.href = ysfurl;
                  });

                  return;
                }
              } catch (e) {
                console.log(e);
              }

              location.href = ysfurl;
             
            },
            error: function () {
              // 错误回调
              location.href = ysf.url();
            },
          };
          // console.log(cfg)
          ysf.config(cfg);
        },
      });
    }

    function GetRandomNum(Min, Max) {
      var Range = Max - Min;
      var Rand = Math.random();
      return Min + Math.round(Rand * Range);
    }

    function query(name) {
      var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
      var r = window.location.search.substr(1).match(reg);
      if (r != null) {
        return decodeURIComponent(r[2]);
      }
      return null;
    }
  </script>
</html>

注入js的代码
sdk.ts

 kefuInject(v: string, tag?: any, source?: any, proj?: any) {
      proj = proj || vueSelf.$proj(tag, source)
      // #ifdef APP
      setTimeout(() => {
        console.log('kefuInject', proj)
        // if(false) {
        //     return
        // }
        proj.evalJs(`
          window.permissionApply = false
          // window.οnlοad=function(){
            //  alert('0 οnlοad')
              loadJS( 'https://p2.dev.yiyiny.com/plt-demo/static/open.plt.0.1.js?v=2',function(){
                  // ab_.vconsoled = true
                  //加载,并执行回调函数
                  injectKefuPermissions()
                  //  alert('2injectKefuPermissions' )
              });
          // }
          
          function injectKefuPermissions() {
              var videoInput = document.querySelector('input[accept="video/*"]')
              var imageInput =  document.querySelector('input[accept="image/*"]')
              if (!imageInput) {
                  setTimeout(injectKefuPermissions, 500)
              }else{
                var imageInputClick = function(e) {
                  saveImagePermissionApply(imageInput,e);
                }
                var videoInputClick = function(e) {
                  saveImagePermissionApply(videoInput,e);
                }

                imageInput.addEventListener('click', imageInputClick);
                videoInput.addEventListener('click', videoInputClick);
              }
          }
          
          function saveImagePermissionApply(el,e){
            // alert('0' + window.permissionApply)
            if(!window.permissionApply){
              // alert('1' +window. permissionApply)
              e.preventDefault();
              uniReady(function() {
                 $abAct('getImagePermission', '', function(err,succ) {
                    // alert('2' + succ)
                    if(succ){
                      window.permissionApply = true
                      imageInput.removeEventListener('click', imageInputClick)
                      videoInput.removeEventListener('click', videoInputClick)
                      el.click()
                    }
                  })  
              })
                
            }else{
              //  window.permissionApply = false
            }
          }
          
          function loadJS(url, callback) {
              var script = document.createElement('script'), fn = callback || function() {
              };
              script.type = 'text/javascript';
              // IE
              if (script.readyState) {
                  script.onreadystatechange = function() {
                      if (script.readyState == 'loaded'
                              || script.readyState == 'complete') {
                          script.onreadystatechange = null;
                          fn();
                      }
                  };
              } else {
                  // 其他浏览器
                  script.onload = function() {
                      fn();
                  };
              }
              script.src = url;
              document.getElementsByTagName('head')[0].appendChild(script);
          }
        `)
      }, 3000);
      // #endif
    },

  };

获取权限代码

SdkActs.ts


  getImagePermission(v?: string, tag?: any, source?: any, proj?: any) {
    // proj = proj || vueSelf.$proj(tag, source);
    let data = {
      state: false,//是否原生授权
      denied: true,//是否提示
      permissionList: [
        {
          permissionName: "相机",
          tips: "允许应用打开摄像头",
          androidPermission: "android.permission.CAMERA",
        },
        {
          permissionName: "相册",
          tips: "允许应用读取存储卡上的照片、媒体内容和文件",
          androidPermission: "android.permission.READ_EXTERNAL_STORAGE",
        },
      ],
    };
    SdkActs.userPermissions(
      JSON.stringify(data), null, null, null,
      function (res) {
        console.log("getImagePermission res", res);
        // console.log("getImagePermission back", back);
        // back(res === 1 ? true : false)
        vueSelf.$callback('abAct.getImagePermission', null, res == 1 ? true : false);
      }
    );
    return true
  },
  _androidPermissions: <any>undefined,
 //此为48小时授权和提示的代码
  userPermissions(v?: string, tag?: any, source?: any, proj?: any, back?: any) {
    proj = proj || vueSelf.$proj(tag, source);
    // #ifdef APP
    try {
      if (axCc.info.platform == "android") {
        let permissionDs: any[] = JSON.parse(v);
        let permissionState = false;
        let permissionDenied = "";
        if (!Array.isArray(permissionDs)) {
          // @ts-ignore
          if (Array.isArray(permissionDs.permissionList)) {
            // @ts-ignore
            permissionState = permissionDs.state;
            // @ts-ignore
            permissionDenied = permissionDs.denied;
            // @ts-ignore
            permissionDs = permissionDs.permissionList;
          } else {
            permissionDs = [permissionDs];
          }
        }

        let androidPermissionsDirty = false;
        let androidPermissions = SdkActs._androidPermissions;
        if (!androidPermissions) {
          androidPermissions = axCc.getStorage("_androidPermissions");
          if (typeof androidPermissions !== "object") {
            androidPermissions = {};
          }

          SdkActs._androidPermissions = androidPermissions;
        }

        let mainActivity: any;
        let nowTime: any;
        let deniedTime: any;
        let deniedPermissions;
        let requestPermissions;
        let nonePermissions = false;
        let tips = "";
        for (let i = permissionDs.length - 1; i >= 0; i--) {
          let permissionD = permissionDs[i];
          let permission = permissionD.androidPermission;
          let permissionV = androidPermissions[permission];
          if (permissionV === 1) {
            permissionDs.splice(i, 1);
          } else {
            mainActivity || (mainActivity = plus.android.runtimeMainActivity());
            if (mainActivity.checkSelfPermission(permission) === 0) {
              permissionDs.splice(i, 1);
              androidPermissionsDirty = true;
              androidPermissions[permission] = 1;
            } else {
              // 暂停二次授权间隔48小时
              if (!deniedTime) {
                nowTime = new Date().getTime();
                deniedTime = nowTime - 48 * 24 * 3600;
              }
              if (permissionV > deniedTime) {
                // 暂停二次授权
                (deniedPermissions || (deniedPermissions = [])).push(
                  permissionD
                );
              } else {
                if (permissionD.tips && !permissionState) {
                  // 可授权提示
                  tips = permissionD.tips + tips;
                  (requestPermissions || (requestPermissions = [])).push(
                    permission
                  );
                } else {
                  nonePermissions = true;
                }
              }
            }
          }
        }

        if (androidPermissionsDirty) {
          // 权限记录
          androidPermissionsDirty = false;
          axCc.saveStorage("_androidPermissions", androidPermissions);
        }
        if (nonePermissions) {
          // 没有授权
          back
            ? back(-1)
            : vueSelf.$callback("abAct.userPermissions", proj, -1);
          return;
        } else if (deniedPermissions) {
          // 暂停二次授权
          if (permissionDenied) {
            uni.showModal({
              title: "提示",
              content: "没有权限",
              showCancel: false,
              complete(result) {
                back
                  ? back(-2)
                  : vueSelf.$callback("abAct.userPermissions", proj, -2);
              },
            });

            return true;
          }

          back
            ? back(-2)
            : vueSelf.$callback("abAct.userPermissions", proj, -2);
          return;
        } else if (tips) {
          uni.showModal({
            title: "权限申请",
            content: tips,
            success(res) {
              if (res.confirm) {
                // https://www.html5plus.org/doc/zh_cn/android.html#plus.android.requestPermissions
                plus.android.requestPermissions(requestPermissions, (e) => {

                  for (let i = e.deniedAlways.length - 1; i >= 0; i--) {
                    androidPermissions[e.deniedAlways[i]] = nowTime;
                  }

                  for (let i = e.deniedPresent.length - 1; i >= 0; i--) {
                    androidPermissions[e.deniedPresent[i]] = nowTime;
                  }

                  for (let i = e.granted.length - 1; i >= 0; i--) {
                    androidPermissions[e.granted[i]] = 1;
                  }

                  axCc.saveStorage("_androidPermissions", androidPermissions);

                  if (e.deniedAlways.length > 0) {
                    //权限被永久拒绝
                    // 弹出提示框解释为何需要定位权限,引导用户打开设置页面开启
                    back
                      ? back(-2)
                      : vueSelf.$callback("abAct.userPermissions", proj, -2);
                  } else if (
                    e.deniedPresent.length > 0 ||
                    e.granted.length <= 0
                  ) {
                    //权限被临时拒绝
                    // 弹出提示框解释为何需要定位权限,可再次调用plus.android.requestPermissions申请权限
                    back
                      ? back(-1)
                      : vueSelf.$callback("abAct.userPermissions", proj, -1);
                  } else {
                    //权限被允许
                    //调用依赖获取定位权限的代码
                    back
                      ? back(1)
                      : vueSelf.$callback("abAct.userPermissions", proj, 1);
                  }
                });
              } else if (res.cancel) {
                // 拒接授权
                back
                  ? back(0)
                  : vueSelf.$callback("abAct.userPermissions", proj, 0);
              }
            },
          });

          return true;
        }

        back ? back(1) : vueSelf.$callback("abAct.userPermissions", proj, 1);
        return;
      }
    } catch (e) {
      console.error(e);
    }
    // #endif
    back ? back(1) : vueSelf.$callback("abAct.userPermissions", proj, 1);
  },
  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
随着互联网的发展,移动互联网的普及,手机APP已成为人们使用最多的移动终端产品之一。随着越来越多的APP应用的涌现,越来越多的APP需要内嵌H5页面WebView进行网页的展示和交互。WebView是一种可在应用程序中嵌入Web页面的控件,可以用来显示来自互联网上的Web页面。它可以实现在应用中展示网页或在线功能,解决一些本地应用无法实现的功能。下面,我们分别从用户、开发者两个方面来探讨APP内嵌H5页面WebView的优缺点。 一、用户方面: 优点: 1.节省时间:在APP中直接查看嵌入的网页,省去了用户手动打开浏览器输入网址的步骤; 2.良好的用户体验:页面加载速度相对较快,而且对主应用对内存占用少,不影响其他应用的使用; 3.方便分享:在WebView中打开的网页可以长按复制网址链接,方便分享给其它用户; 4.强大的交互能力:在APP中嵌入H5页面,拓展了应用的交互能力。 缺点: 1.便捷性带来的安全隐患:一些App会在内嵌的H5页面中嵌入第三方广告,导致用户隐私泄露; 2.缺乏统一标准:因为浏览器的内核和引擎都是不同的,所以在不同的WebView中,同一网页的显示效果和交互体验可能会有差异。 二、开发者方面: 优点: 1.拓展应用功能:借助WebView内嵌H5页面应用的功能可以得到极大的拓展; 2.代码复用:WebView可以实现HTML、CSS等内容的兼容,减轻了移动开发者负担; 3.节省开发成本:相对于开发单独的H5 APP,内嵌方式更为灵活,可以适用于不同场景和需求。 缺点: 1.不支持多线程:WebView在JS调用本地方法的时候是在同一线程下执行的,如果WebView的内容较为复杂,可能会导致主线程卡顿; 2.性能问题:在内存管理、布局排版等方面还不如原生应用; 3.浏览器兼容性:WebView的内核并没有得到很好的统一,不同的WebView之间有兼容性问题,会导致页面显示和交互问题; 4.安全问题WebView加载HTML页面时容易受到跨站脚本(XSS)和恶意代码注入等安全漏洞的攻击。为此,开发者应该前端页面安全防范,对浏览器缓存和Cookie进行管理并开启CSP(内容安全策略)。 综合来看,在APP内嵌H5页面WebView上,开发者需要在开发时注意安全方面的问题,同时还需要强对WebView性能和兼容性的了解和优化。用户便捷性和良好的用户体验,也需要开发者在开发时重视。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

给钱,谢谢!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值