如何建立统一的方法来获取客户端信息

151 阅读4分钟

采用混合开发App时,通常会涉及到跟客户端的交互,比如获取系统信息、获取 App 信息、设备信息等。这些都需要通过客户端上暴露方法给WebView才可以通过网页拿到相关信息,但是通常又面临着,不同的端平台用来获取时的方式是不一样的,这就导致了网页在获取一些信息时,需要去适配不同的平台,一旦需要去适配就表示这需要写很多的判断条件,自然而然的就会产生比较多的冗余代码。为了解决这个问题,我们就需要建立一个公用的转换方法,通过双端统一的方式来调用客户端方法,从而减少冗余代码的产生。

1. 问题的由来

在网页中,如果需要获取客户端的系统时,会通过一个方法getOs()来获取,但是不同的客户端暴露出的方法是不同的,比如Android通过 window.nativeAndroidClient.getOS()来获取,而对于iOS来说则是通过 window.nativeIOSClient.call("getOs")来获取,那么网页在获取客户端信息时则需要针对不同的客户端做适配,如下:

function getOs() {
  return window["nativeAndroidClient"] ? nativeAndroidClient.getOs() : nativeIOSClient.call("getOs")
}

这样写法倒也没有什么不可以,但是我们在网页中不仅仅需要获取这一个属性,还有很多的属性,比如:App 版本、用户信息、设备信息、本地语言等,一个属性对应一个方法,那么我们就需要针对每一个方法去做一个判断,这样就显得代码不是那么好看了,最好的方式,则是把客户端的方法都统一,通过统一调用就能获取到客户端的信息。

注:nativeAndroidClientnativeIOSClient都是客户端注册在 WebView window上的对象。

2. 解决问题

从上文描述看来,iOS通过window.nativeIOSClient.call(name)对象来获取客户端相关信息,这么写呢感觉不是太符合调用习惯,我们希望的获取可以是这样的方式:window.nativeIOSClient.getOs()我们需要改造nativeIOSClient对象,注意是改造而不是覆盖(也可以说是给此对象追加属性) ,如果覆盖的话我们则是把客户端暴露给网页的方法给换掉了,这样我们再想用客户端的方法则就用不上了,很危险

2.1 改造 nativeIOSClient 对象

封装一个方法并执行,通过此方法可以给 nativeIOSClient 上追加获取属性的方法,可以通过window.nativeIOSClient.getOs()的调用方式来调用,代码如下:

const iOSClient = window['nativeIOSClient']
const androidClient = window['nativeAndroidClient']
      
function initIOSClient() {
  Object.assign(iOSClient, {
    getOs() {
      return this.call("getOs")
    },
    getAppVersion() {
      return this.call("getAppVersion")
    }
    ...
  })
}

iOSClient && initIOSClient()

这样我们就保证了在window['nativeIOSClient']也可以通过window.nativeIOSClient.getOs()的调用方式来调用。

2.2 封装统一的执行方法

我们已经通过第一步把iOS客户端获取属性的方法进行了改造,现在就是统一调用了,为什么需要统一调用?虽然我们把iOS客户端方法进行了改造,但是我们在页面上用时还需要判断客户端,然后再执行相应的方法因为Android是绑定在nativeAndroidClient对象上,而iOS则是绑定在nativeIOSClient对象上,如果不封装则还是需要在页面上进行客户端判断,再分别调用。接下来整合一下,使得通过一个方法可以调用双端的方法,代码如下:

const iOSClient = window['nativeIOSClient']
const androidClient = window['nativeAndroidClient']

function executeNativeMethod(optios) {
  const { iOSClient, androidClient, name, args } = optios
  
  if (iOSClient && isFunction(iOSClient[name])) {
    return iOSClient[name](args)
  }
  
  if (androidClient && isFunction(androidClient[name])) {
    return androidClient[name](args)
  }
}

function getParam(name, ...args) {
  return executeNativeMethod({
    iOSClient,
    androidClient,
    name,
    args
  })
}

注:isFunction方法用来判断当前如果是非方法类型时,则不去执行,防止意外的报错。也很简单的封装。

export function isFunction(value) {
  return typeof value === "function"
}

2.3 重写所有获取客户端属性的方法

重写后暴露出去,那么在页面中就可以通过getOs()方式进行获取,这样就非常便利在各个页面中的调用了。

export function getOs() {
  return getParam("getOs")
}

export function getAppVension() {
  return getParam("getAppVension")
}

当然,我们还需要考虑另外一个场景,就是说页面可能只在iOS客户端上生效,那么我们也就不需要这么费时费力的重新了直接把iOSClient导出即可。

const iOSClient = window['nativeIOSClient']

export {
  iOSClient
}

3. 总结

通过简单的封装后,在网页中则不需要再写判断条件,就可以通过一个统一的方式来获取,这样就可以减少冗余代码的产生。其实在跟客户端的交互中,会遇到很多方法名不统一的问题,我们必须二次封装才能达到我们想要的效果,这样在维护网页时就可以极大的提高我们的开发效率,而不必再写很多的判断,当然每一次的封装都不是一蹴而就的,只能是一步步去写、一步步去提炼才能形成一个工具类。