|
转转的移动端开发体系主要是基于Hybrid方案,但长久以来Webview容器和SDK管理等存在标准不统一、更新不及时的问题。随着转转/找靓机/采货侠等多环境开发场景越来越多,适配不同场景极大的影响了业务迭代效率。所有我们决定重新规划SDK的建设。在介绍方案之前。先了解一下基础知识。JSBridge的双向通信原理JSBridge是一种JS实现的Bridge,连接着桥两端的Native和H5。它在APP内方便地让Native调用JS,JS调用Native,是双向通信的通道。JSBridge主要提供了JS调用Native代码的能力,实现原生功能如查看本地相册、打开摄像头、指纹支付等一、JS调用NativeJS调用Native的实现方式较多,目前主流采用是拦截URLScheme、MessageHandler。1、拦截URLSchemeWeb端采用创建隐藏的iframe进行Scheme请求,Android和iOS可以通过拦截URLScheme并解析Scheme来是否进行对应的Native代码逻辑处理。Android端,Webview提供了shouldOverrideUrlLoading方法来进行拦截H5发送的URLScheme请求。代码如下:public boolean shouldOverrideUrlLoading(WebView view, String url){ //读取到url后自行进行分析处理 //如果返回false,则WebView处理链接url,如果返回true,代表WebView根据程序来执行url return true;}iOS的WKWebview可以根据拦截到的URLScheme和对应的参数执行相关的操作。代码如下:- (void)webViewWKWebView *)webView decidePolicyForNavigationActionWKNavigationAction *)navigationAction decisionHandlervoid (^)(WKNavigationActionPolicy))decisionHandler{ if ([navigationAction.request.URL.absoluteString hasPrefix"xxx"]) { [[UIApplication sharedApplication] openURL:navigationAction.request.URL]; } decisionHandler(WKNavigationActionPolicyAllow);}Web端通过动态的创建iframe和客户端通讯function iosExecute (action, param) { param['methodName'] = action let iframe = env.createIframe() let paramStr = JSON.stringify(param) iframe.src = `zznative://zhuanzhuan.hybrid.ios/?infos=${encodeURIComponent(paramStr)}` document.body.appendChild(iframe) setTimeout(() => iframe.remove(), 300)}2、MessageHandler基于Webview提供的能力,我们可以向Window上注入对象或方法。JS通过这个对象或方法进行调用时,执行对应的逻辑操作,可以直接调用Native的方法。使用该方式时,JS需要等到Native执行完对应的逻辑后才能进行回调里面的操作。Android的Webview提供了addJavascriptInterface方法,支持Android4.2及以上系统。gpcWebView.addJavascriptInterface(new JavaScriptInterface(), 'nativeApiBridge');public class JavaScriptInterface { Context mContext; JavaScriptInterface(Context c) { mContext = c; } public void share(String webMessage){ // Native 逻辑 }}iOS的UIWebview提供了JavaScriptScore方法,支持iOS7.0及以上系统。WKWebview提供了window.webkit.messageHandlers方法,支持iOS8.0及以上系统。UIWebview在几年前常用,目前已不常见。以下为创建WKWebViewConfiguration和创建WKWebView示例:WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];WKPreferences *preferences = [WKPreferences new];preferences.javaScriptCanOpenWindowsAutomatically = YES;preferences.minimumFontSize = 40.0;configuration.preferences = preferences;- (void)viewWillAppearBOOL)animated{ [super viewWillAppear:animated]; [self.webView.configuration.userContentController addScriptMessageHandler:self name"share"]; [self.webView.configuration.userContentController addScriptMessageHandler:self name"pickImage"];}- (void)viewWillDisappearBOOL)animated{ [super viewWillDisappear:animated]; [self.webView.configuration.userContentController removeScriptMessageHandlerForName"share"]; [self.webView.configuration.userContentController removeScriptMessageHandlerForName"pickImage"];}Web端通过调用客户端注入的全局变量进行通讯window.zhuanzhuanMApplication.executeCmd(action, oriParam)二、Native调用JSNative调用JS比较简单,只要H5将JS方法暴露在Window上给Native调用即可Android中主要有两种方式实现。在4.4以前,通过loadUrl方法,执行一段JS代码来实现。在4.4以后,可以使用evaluateJavascript方法实现。loadUrl方法使用起来方便简洁,但是效率低无法获得返回结果且调用的时候会刷新WebView。evaluateJavascript方法效率高获取返回值方便,调用时候不刷新WebView,但是只支持Android4.4+。相关代码如下:webView.loadUrl("javascript:" + javaScriptString);webView.evaluateJavascript(javaScriptString, new ValueCallback() { @Override public void onReceiveValue(String value){ xxx }});iOS在WKWebview中可以通过evaluateJavaScript:javaScriptString来实现,支持iOS8.0及以上系统。[jsContext evaluateJavaScript"zhuanzhuanMApplication(ev, data)"]开源通讯方案介绍上面讲完了原理。但是自己开发一套完整和好用的还是比较麻烦,现在我们就来介绍一下现在比较好的开源解决方案DSBridge。DSBridge的主要特点:Android、IOS、Javascript三端易用,轻量且强大、安全且健壮。同时支持同步调用和异步调用支持以类的方式集中统一管理API支持API命名空间支持调试模式支持API存在性检测支持进度回调:一次调用,多次返回支持Javascript关闭页面事件回调支持Javascript模态/非模态对话框支持腾讯X5内核还有一点可以介绍一下。dsBridge如果和Fly.js一起使用。可以直接使用客户端的通讯能力。正如我们所知,在浏览器中,ajax请求受同源策略限制,不能跨域请求资源。然而,Fly.js有一个强大的功能就是支持请求重定向:将ajax请求通过任何Javascriptbridge重定向到端上,并且Fly.js官方已经提供的dsBridge的adapter,可以非常方便的协同dsBridge一起使用。由于端上没有同源策略的限制,所以fly.js可以请求任何域的资源。另一个典型的使用场景是在混合APP中,由于Fly.js可以将所有ajax请求转发到端上,所以,开发者就可以在端上进行统一的请求管理、证书校验、cookie管理、访问控制等。转转面临的问题介绍完基础原理和优秀的开源方案,现在来看看转转面临的问题。1、通信协议不统一转转使用自定义的通讯方案,有200个API,找靓机采用了dsbrige通讯方案有100个API2、URL参数含义和UA不统一两端都有很多的URL参数和UA参数,没有一个统一的规范3、找靓机Webiview分新老两个版本在找靓机合并到转转时,我们做了技术栈统一。把转转的Webview迁移到找靓机,但是找靓机业务大量使用的是老的Webview,所以为了保证老的业务能正常的运行,只能保持两套Webview并行使用4、存在两套文档,不是很好的找到API5、SK文档维护问题转转的SDK文档为了保证准确性和有人维护。把SDK设计成一个对象。如果需要添加新的SDK需要通知维护人员添加。然后发版还能使用,文档和使用的测试Demo由维护人员编写。但是这样就造成SDK的体积越来越大。而且当客户端的同学发现一个文档错误,他们不能直接的修改文档,需要把问题反馈,还能修改。综上,导致SDK文档维护成本巨大。基于此,我们和客户端团队成立了标准Webview小组,打造转转标准Webview容器。转转解决方案1、标准化相关:客户端Cookie管理、UrlQuery管理、UA规范、通讯格式规范我们整理了所有的cookie,url,ua,通讯方案,并制定了规范UA的标准App的UA分为两个层面:公共UA:使用zz{Key}/{Value}的形式添加一个键值信息,Key首字母大写,中间有空格分隔,末尾不包含空格。自定义UA:APP内自定义格式。公共UA与自定义UA不做顺序要求,但公共UA中的zzApp字段一定是最后一个。zzVersion/客户端版本号 zzT/客户端终端值 zzDevice/是否支持顶栏穿透_状态栏高度_设备像素比例 zzApp/App标识符举例:zzVersion/8.18.20zzT/16zzDevice/1_44.0_3.0zzApp/58ZhuanZhuanCookie的标准Cookie的诞生背景:由于HTTP协议是无状态的,网站为了辨别用户身份向用户本地终端上储存数据,这就是Cookie。所以从Cookie设计上来讲,它是为了解决服务端在客户端保存数据问题的,这个存储容器的主动权应当在服务端,客户端不应过多干预,即使客户端拥有修改Cookie的api,但除非用户清除缓存时,可以使用cookie.clear()方法之外,其余情况不建议对Cookie进行修改。其次,cookie会被附加在每个可以被携带的HTTP请求中。所以如果客户端想要给H5传值,不建议采用通过Cookie的方式,因为这些Cookie会原封不动的携带给服务端,占用请求头部大小,造成流量浪费。基于上面的考虑和调研,我们制定了Cookie的标准客户端理论上不再向WebView增加新的Cookie只有通过了Cookie白名单的Url才会写入CookieCookie某一条目不存在、条目的value为空或空串时,该条目应视为无效,客户端的无效条目可能为以上三种的任一形式客户端启动一个WebView页面时,在请求首个Url之前,会写入Cookie,如果该Cookie条目已存在则覆盖UrlQuery标准UrlQuery主要为了解决H5在执行JS方法之前的这段时间里,页面的样式、能力问题。例:加载H5页面时,希望页面背景为黑色,如果依赖Api实现,则执行Api之前页面为非黑色,效果不理想。H5加载失败后,容器层也能够进行设置的一些配置项(不建议在失败页面上支持过于丰富的配置项,失败页面仅支持少部分配置项即可)。例:H5即使加载失败了,页面也可以侧滑关闭。基于上面的考虑和调研,我们制定了UrlQuery的标准其余能依赖Api解决的问题,不建议放到UrlQuery中。UrlQueryName规范,定义时按照驼峰式,但客户端读取时按照大小写不敏感方式读取。UrlQuery匹配规范遵守Url标准规范,如#后边的参数不应进行识别。不使用字符串匹配的方式,如key=13使用key=1会误匹配。通讯格式规范之前的通讯协议不太规范,所以我们把之前的Api作为V1,之后新增的Api使用V2。通讯格式分为V1版本和V2版本,V1V2不兼容,当前已经按照V1标准开发的Api不升级至V2版本,V1V2标准会长期共存。协议分为两层:框架层和业务层。普通的业务开发不需要关注框架层state部分,state是SDK和客户端沟通的字段,用来展示一些异常,比如客户端没有这个方法等,框架层字段等其他逻辑对于业务层来说是透明的使用code来判断状态,msg是提示信息,data是返回的数据。callback('state', '{code: "", msg: "", data: {}}')框架层字段(v1版本使用自然数风格,v2版本采用http状态码风格)image业务层通用字段值(String)说明0业务成功-1业务失败-1001参数不合法(如缺少必填参数)2、SDK重构,实现通讯层,告别频繁的发版只保留通讯层比较简单,但是如果兼容历史的300个Api是比较难的问题,我们的解决方案是老的还是保留,所有的新的使用新的Api通讯方案,慢慢的把老的不标准的Api迁移到新的通讯方案3、SDK文档管理后台,保证了文档的及时更新image文档使用VuePress生成。通过管理后台编辑数据,然后动态的生成md文档。执行VuePress的命令。通过脚手架命令把文档上传到线上。image基于上面的优化我们搭建转转移动开发平台网站,方便FE快速找到相关资料文档展望做完上面的事情,我们还是有很多的挑战没有做。1、SDK进一步标准化老的Api能力不标准。比如分享就有5个不同的Api,我们需要整合成一个完整的share。把所有的Api都标准化是一个工作量巨大的事情,所有只能依靠后面的规范来一点一点做。2、用户全链路监控完整监控现在的SDK的监控是前端和客户端分开做了。后面准备做全链路监控3、沉淀Webview性能优化方案,降低接入成本,大范围业务覆盖现在的性能优化手段比较多。但是接入成本还是比较好。需要改动代码。准备在后面降低接入成本。
|
|