跨端技术谈
Posted FarmGuo
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了跨端技术谈相关的知识,希望对你有一定的参考价值。
目前流行的跨端方案按实现形式可分为三种
1,通用WebView
2,React Native
3,Flutter
1.通用WebView
在ios端,WebView当前是WKWebView,拥有独立的Content进程渲染和Networking进程请求。性能比UIWebView高效、稳定。通常使用JSContext进行JS和OC的交互(JSPatch),或者使用WKWebView来进行JS和OC的交互(Hybrid)。
JSPatch原理:
1,构建环境,先利用JSContext生成一个交互环境,里面定义了JS调用OC的方法,如_OC_defineClass、_OC_callI、_OC_callC。然后将JSPatch.js进行代码注入,这个文件代码定义了后续Patch执行的环境,如defineClass函数和Object原型链上的__c函数等等。其余.js的文件是要执行的代码,准确说是以defineClass开头的字符串,但字符串里面混合了JS的代码。以defineClass开头,后面的参数是类名(需要hook或增加实现的类名)、对象方法和类方法。而后2个参数是以字典的形式做参数的,字典的key就是方法名,value就是具体的实现(JS语言形式)。在注入前,会修正下格式,将.alloc()的格式修改为.__c(“alloc”)(),对应之前在Object原型链上定义的__c函数。这样注入的时候就调用到了defineClass函数。
2,hook,defineClass函数中,调用Native的_OC_defineClass方法,传入对象方法和类方法。_OC_defineClass指向了defineClass函数。在里面会根据类和方法进行hook,替换原来的实现,具体而言就是:1.将原SEL指向
msg_forward,使得调用SEL走消息转发流程。2.将forwardInvocation的实现替换为自定义的JPForwardInvocation。3.以ORIG开头的SEL保存着原实现。4.将新的实现(JS中定义的,此时是JSValue)保存到到全局的_JSOverideMethods字典中,这个字典是2级字典,一级key为类名,二级key为方法名。5.在类中添加JP_开头SEL的实现,这个实现是一个中转者,会调用callWithArguments来执行新的实现(JS中定义的)。
Hybrid原理
以WebViewjavascript为例,WebViewJavascriptBridgeBase的messageHandlers属性是个字典,保存了JS到OC的回调,key是方法名,value则是block。
在WKWebView初始化加载完成后JS会新建一个iframe,并设置其style.display设置为none,并添加到当前document上。并将这个ifrme的src设置为“bridge_loaded”,decidePolicyForNavigationAction就会收到这个跳转,根据URL的类型通过evaluateJavaScript进行代码注入,注入的JS代码中定义了_handleMessageFromObjC、_fetchQueue、_doSend、callHandler等函数。然后生成了一个新的iframe并保存到名为messagingIframe的全局变量,然后将其style.display设置为none,并添加到当前document上。
Native调用JS的方法,都是通过evaluateJavascript,传入JS代码来实现的。而Hybrid中的调用也是如此,在Native的_dispatchMessage方法中,使用evaluateJavascript调用_handleMessageFromObjC函数,会将参数转换为字符串,里面包含了callbackId和其他数据。JS中根据callbackId来执行具体的调用。
JS要调用Native时,先调用_doSend,里面准备好数据如方法名、参数、方法执行完后的回调,保存到sendMessageQueue中,然后将messagingIframe这个iframe的src置为“queue_message”,decidePolicyForNavigationAction会收到这个跳转,根据URL的类型,调用WKFlushMessageQueue方法,这个方法让WebView执行_fetchQueue()函数,其会返回之前准备的数据(字符串形式)。然后调用flushMessageQueue,其会将之前准备的数据转换为字符串后返回,WKFlushMessageQueue获取执行后的结果,调用flushMessageQueue,在里面将字符串转为字典,取出方法名和参数。通常情况下JS要调用Native后还需要一个回调,来获取执行的结果。在字典中取出callbackId,生成一个临时Block,当作参数传给messageHandlers中的Block。Native的方法执行完成后,会调用这个临时Block并传入执行结果,这个临时的block里面,会拼一个字典,responseId对应callbackId,responseData对应执行后返回的结果。然后调用_dispatchMessage。JS方根据responseId来执行具体的回调。
2.React Native
在iOS端,使用JavaScriptCore引擎进行前端和OC Native进行交互,将前端的语言实现转换为端内的代码(RC开头的类)来进行实现,最终使用Native的渲染,渲染性能比通用WebView好些。由于中间多了一个转换的过程,对性能会有一定的影响。而且涉及到前端到Native的代码转换,当Native版本升级时,需要适配改动。
3.Flutter
新的前端语言Dart,生成Widget Tree,Element Tree,Object Renders
底层使用Skia渲染框架。
4.Hybrid 通信
1,WKScriptMessageHandler,这种方案Native事先调用-[WKUserContentController addScriptMessageHandler:name:];进行注册,前端使用在iOS平台上使用window.webkit.messageHandlers.xxxx.postMessage(),在安卓平台上使用window.xxx.postMessage()进行调用。缺点是这种只能用于WKWebView。无法用于UIWebView。
if(isandroid)
// 给Android传递参数需要用 window.Android.注册的方法名(body:传输的数据 来给native发送消息
window.xxx.postMessage("key":"parameter");
if(isiOS)
// 给iOS传递参数需要用 window.webkit.messageHandlers.注册的方法名.postMessage(body:传输的数据 来给native发送消息
window.webkit.messageHandlers.xxx.postMessage("key":"parameter");
2,使用拦截假请求的方式来进行通信,在decidePolicyForNavigationAction中进行url判断。但这样会有2个问题,1,是不能短时间多次调用,必须进行延时进行调用,否则后续的url会丢失。2,url的长度问题。WebViewJavascriptBridge为了兼容UIWebView,所以使用了拦截假请求的方式。为了解决url的长度问题,又进行了特殊处理,拦截url这里不获取数据,而是再调用一个fetchQueue的JS函数来获取数据,包括Native的方法、参数和callbackId。
这种可以兼容WKWebView和UIWebView
以上是关于跨端技术谈的主要内容,如果未能解决你的问题,请参考以下文章