欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android-Hybrid-问题收集&Android客户端无法拦截Vue路由的问题

程序员文章站 2022-04-21 12:04:53
...

1. 在Android客户端或Chrome浏览器弹出一个引导用户关注公众号的二维码

描述

客户端加载线上义诊Url,偶尔会弹出一个要求关注丁香园公众号的二维码 

问题分析

后端会根据当前登录用户向前端界面注入一段如下js 

<script>
    window.initialState = {
        currentLoginUser: {
            avatar: "https://askpub.dxycdn.com/avatar/128/default.png",
            simUid: "0",
            userId: "0",
            showApplyAnswererHint: false,
            isApplied: false,
            wxFollow: false
        }
        , environment: {
            server: 'prd'
        }
    }
</script>

界面上根据wxFollow字段以及白名单决定是否弹出公众号的二维码
当用户未登录却进入到了义诊界面,或者已登录但未关注公众号,同时,Url未在白名单内时,将引导用户关注公众号 

解决方案

检查wxFollow字段是否为false,检查白名单内有无义诊的Url。
与产品讨论,最终确认将义诊Url加入到白名单,不再在此界面引导用户关注公众号。 

2. Android客户端无法拦截Vue路由的问题

描述

客户端加载使用vue编写的单页面应用,vue进行路由时,没有回调WebView的shouldOverrideUrlLoading生命周期方法。onPageFinished生命周期方法被调用,但无法进行路由拦截。 

问题分析

客户端加载使用vue编写的单页面应用,会将整个站点所有vue组件及较小的资源文件下载到本地,当用户点击医生条目将要跳转至医生详情页时,并不会产生网络资源的加载,也就不会回调客户端WebView类似shouldOverrideUrlLoading的生命周期方法。同时,从vue-router.js可知,createHref创建出类似[/#/demo]的锚点。vue路由时,根据ua进行区分,对于支持pushState的使用window.history.pushState(window.history.replaceState)方法修改Url,不触发hashchange,否则使用window.location.hash(window.location.replace)修改Url,触发hashchange。客户端不应仅根据hashchange来判断vue的路由变化。 

示例: 

window.history.pushState({ key: "475593.080" }, '', "http://localhost:8080/#/demo")
window.history.replaceState({ key: "123" }, '', "http://localhost:8080/#/")
window.location.hash="#/demo"
window.location.replace="#/demo"

解决方案

可以在入口js中(main.js),使用router.beforeEach注册一个全局前置钩子,同时,为确保客户端可以监控到所有vue路由信息,可以将钩子放置在router.start(new Vue())之前。当vue路由发生,全局前置钩子将按照创建顺序调用。钩子方法为异步解析执行,同时,路由将会等待所有钩子resolve完。需要确保所有钩子一定调用了next方法,否则钩子将不会被resolve,造成路由阻塞。
问题一:不调用next方法,将阻塞路由,造成history混乱。
问题二:to、from参数转化为json时要考虑循环引用问题,JSON.stringify()无法处理这些问题,可以引入三方库解决。
问题三:若vue路由时出现异常,整个跳转将出现混乱。当界面上其它地方出现异常,同时使用window.onerror(Vue.config.errorHandler或者try-catch)处理了异常,并进行了统一处理,应当小心添加统一处理异常的代码。 

示例: 

import Util from 'util'
router.beforeEach((to, from, next) => {
  if (window.AndroidJSBridger && window.AndroidJSBridger.beforeEach) {
      const toStr = to ? Util.inspect(to) : '{}';
      const fromStr = from ? Util.inspect(from) : '{}';
      //返回值boolean为true表示客户端已经拦截本次路由,将做进一步处理,返回false,表示客户端不关心本次路由。
      const ret = window.AndroidJSBridger.beforeEach(to.fullPath,toStr,fromStr)
      if(ret){
        next(false);
      }else{
        next(true);
      }
  } else {
    next(true);
  }
})

3. 前端接收到两个DXYJSBridgeReady事件的问题

描述

当h5界面运行在客户端时,h5界面监听了DXYJSBridgeReady事件,并收到多个DXYJSBridgeReady事件。 

问题分析

当h5界面运行在客户端,需要调用客户端一些Api函数,客户端需要准备一些资源,并初始化这些Api函数。正是由于h5界面需要等待客户端初始化环境,固监听DXYJSBridgeReady事件,等客户端环境初始化。
客户端通过loadUrl(evaluateJavascript)向h5界面注入一段js,并通过window.document.dispatchEvent发送一个DXYJSBridgeReady事件,通知h5界面环境初始化完成。
当h5界面接收到多个DXYJSBridgeReady事件时,可能原因有如下几种,需逐一排查:
1)客户端反复注入同一JS片段,并且没有做防重复分发操作(解决-window.hasInitDXYJSBridgeReady标记位)
2)h5界面多处监听DXYJSBridgeReady事件或没做防重复监听操作(解决-全局搜索DXYJSBridgeReady,并删除)
3)h5界面主动分发了DXYJSBridgeReady事件(解决-全局搜索DXYJSBridgeReady,并删除)
3)h5界面监听DXYJSBridgeReady事件时,使用的立即执行函数(解决—查看监听器的写法,防止监听器立即执行) 

解决方案

develop模式下,h5界面模拟原生客户端进行事件分发。界面上主动分发了DXYJSBridgeReady事件。故只需要删除模拟分发代码片段即可。 

4. develop is not defined异常的问题

描述

查看义诊页,点击医生条目客户端控制台偶尔显示develop is not defined异常,此时整个界面无法响应事件。 

问题分析

当点击医生条目时,触发了h5界面上的某些资源加载代码。此时WebView的生命周期方法onProgressChanged将被调用。客户端会将此情况理解为新界面的加载,并重新注入JS片段,为“新的”h5界面准备环境。
出现develop is not defined异常可能的原因有如下几种,需逐一排查:
1)客户端为“新的”h5界面准备环境时注入的JS片段中包含对未定义的develop变量的使用。(解决-查看所有客户端注入的js片段)
2)h5界面进行跳转时,调用中某些自定义函数或者三方库中包含对未定义的develop变量的使用。(解决-全局搜索整站所有前端代码) 

解决方案

客户端为h5界面准备环境时,需利用window.env,告知h5界面客户端当前所处状态。由于客户端注入的js片段全部为字符串逐段拼接,导致调用了未定义的develop变量。只需要将develop变量转化为字符串即可解决。为规范化注入的JS,可以将待注入的js放入assets中,使用压缩-读取-替换的方式替代原有方案。压缩时,应当小心由单行注释或者末尾省略分号而带来的问题。 

5. shouldOverrideUrlLoading用法过时问题

描述

WebView生命周期方法shouldOverrideUrlLoading使用了过旧的方式重写,可能导致整个WebView生命周期方法调用的改变。 

问题分析

为支持2.X版本,以往WebView的shouldOverrideUrlLoading的用法为: 

@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
    boolean isProcessed = processJumpUrl(url);
    if(!isProcessed) {
        if(mWebView != null) {
            mWebView.loadUrl(url);
        }
        return false;
    } else {
        return isProcessed;
    }
}

4.X以后
若没有设置WebViewClient则由系统(Activity Manager)处理该 url,通常是使用浏览器打开或弹出浏览器选择对话框。
若设置WebViewClient且该方法返回true ,则说明由应用的代码处理该url,WebView不处理,也就是程序员自己做处理。
若设置WebViewClient且该方法返回false,则说明由WebView处理该url,即用WebView加载该url。
用法如下:

@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
    boolean isProcessed = processJumpUrl(url);
    if (isProcessed){
        return true;
    }else {
        return super.shouldOverrideUrlLoading(view,url);
    }
}

解决方案

使用Google规定的方法重写WebView的shouldOverrideUrlLoading生命周期方法。 

6. 解决义诊界面连续点击时与原生限流产生的冲突问题

描述

在客户端上,连续点击h5界面条目,客户端无法有效拦截所有h5的路由事件。产生的现象是,单击正常,而双击之后,h5跳转到了登录界面或者医生详情界面。 

问题分析

客户端上,vue路由时,会询问客户端是否需要接管路由,若客户端表示不关心本次路由,h5界面将继续自身的路由操作。
对于h5界面发出的事件请求,客户端做了限流操作,减少用户因误操作,多次点击同一条目而产生的困扰。
单击时,客户端会接管路由,并正常进行处理。
双击时,客户端忽略了第二次路由,导致h5界面触发了原有路由规则。
此处,还有一点需要注意。限流操作时间阀值的计算应当小心修改,防止因频繁更新初始时间产生连续点击而不响应事件的效果。 

解决方案

客户端做限流操作时,不能简单的直接忽略前端的路由请求,而应当将限流操作的意图通过某种方式,反馈到前端界面。可以通过假装已处理第二次路由的方式,让前端界面误认为本次路由已经被有效处理,而不再执行原有路由规则。 

7. 关于义诊界面打开过慢的问题

描述

在客户端上,特别是网络较差的环境下,客户端打开义诊界面,加载速度非常缓慢。 

问题分析

1)vue单界面应用,为提高用户进入站点后,进行站内跳转和操作的响应速度,使用了一次加载站点所有组件及较小的资源文件的方式。此方式以少量牺牲流量和界面初始化速度的方式换取了用户进入站点之后的操作响应速度。可考虑拆分的方式或者分平台自动打包的方式解决此问题。
2)vue应用可能包含一些不必要的三方库,影响界面加载速度。
3)客户端未正确设置缓存模式,导致界面二次加载过慢。
4)客户端安装包中可以提前包含部分三方库文件或者其它资源文件,待界面初次打开时,劫持并直接返回本地资源。
5)客户端针对部分或所有Url,与前端约定一套缓存规则,拦截Url-加载并缓存远程资源-二次打开返回本地数据。