iOS程序中网页与OC代码互相调用的原理+一个实现方案的分析

作者:jcmp      发布时间:2021-05-03      浏览量:0
导语核心技术点一、网页JS如何向Nati

导语

核心技术点

一、网页JS如何向Native Code传递其想调用的Native Code接口和参数。

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType。

在这个方法内部我们可以通过 request 参数获取网页即将访问的url。

[[request URL] absoluteString]

网页可以通过触发访问新的url,将想调用的Native Code接口和参数通过JSON格式化后,放在新的url中,通知回Native Code的 UIWebViewDelegate 的回调方法。

document.location='http://new.url/params?key=value'

newIframe = document.createElement('iframe');newIframe.style.display = 'none';document.documentElement.appendChild(newIframe);newIframe.src = 'http://new.url/params?key=value';document.documentElement.removeChild(newIframe);

二、Native Code如何调用网页JS接口并向其传递参数

- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;

参数 script 就是需要执行的JS代码,返回结果为JS执行结果。这里建议如果JS需要返回大量数据,最好将数据放入数组或对象,再转化成JSON字符串返回给Native Code。

function getOrderList() { var orderList = ['10001', '10045', '54321']; var orderListString = JSON.stringify(orderList); orderList = []; return orderListString;};

NSString *js = @"getOrderList();";NSString *result = [webView stringByEvaluatingJavaScriptFromString:js];

就可以让网页JS执行相关的代码,并同步的取得返回值

三、一个完整的JSSDK实现方案

NSString *const jssdk_getUserInfo = @"jssdk.getUserInfo";NSString *const jssdk_payMyBill = @"jssdk.payMyBill";NSString *const jssdk_showLogistics = @"jssdk.showLogistics";

var callback_index = 1;var callback_map = {};function callNativeCode(func, params, callback) { if (!func || typeof func !== 'string') return; if (typeof params !== 'object') params = {}; var callbackID = (callback_index++).toString(); if (typeof callback === 'function') callback_map[callbackID] = callback; var paramsObj = {'func':func,'params':params,'callbackID':callbackID}; var paramsForNative = JSON.stringify(paramsObj); callNativeCode(paramsForNative);}callNativeCode('jssdk.getUserInfo',{'code':'0fec3575758a06c203868edcca748565'},function(openid, name, sex){// to something to process return value});

上面代码中的callNativeCode实际上进行了两个工作。将接口名称、参数、回调callbackID对象经过JSON处理的字符串保存起来。以及通过通过改变iframe属性的方式,通知Native主动来取数据。Native Code在回调中就可以在回调中收到JS传递过来的新url。

var callNativeCodeQueue = [];function callNativeCode(message) { callNativeCodeQueue.push(message); messagingIframe = document.createElement('iframe'); messagingIframe.style.display = 'none'; messagingIframe.src = 'http://new.url/params?key=value'; document.documentElement.appendChild(messagingIframe); document.documentElement.removeChild(messagingIframe);};

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {... NSRange jssdkSchemaRange = [urlString rangeOfString:@"'http://new.url/params?key=value"]; if (jssdkSchemaRange.location == 0) { NSString *jssdkInputQueueJSON = [webView stringByEvaluatingJavaScriptFromString:@"fetchJSSDKInputQueueJSON();"]; [self unpackJSSDKInputQueueJSON:jssdkInputQueueJSON]; return NO; }...} - (void)unpackJSSDKInputQueueJSON:(NSString *)jssdkInputQueueJSON { NSArray *inputQueue = [jssdkInputQueueJSON JSONArray]; if (nil == inputQueue) return; for (int i = 0; i < inputQueue.count; i++) { NSString *oneJSCall = [inputQueue stringAtIndex:i]; NSDictionary *jsCallParams = [oneJSCall JSONDictionary]; if (nil == jsCallParams) return; NSString *funcName = [jsCallParams stringForKey:@"func"]; NSDictionary *params = [jsCallParams dictionaryForKey:@"params"]; NSString *callbackID = [dic stringForKey:@"callbackID"]; if ((funcName.length == 0) || (params == nil)) return; [self jsInvokeNativeCode:funcName withParams:params withCallbackID:callbackID];} - (void)functionCall:(NSString *)funcName withParams:(NSDictionary *)params withCallbackID:(NSString *)callbackID { if ("jssdk.getUserInfo" == funcName) return [self getUserInfo:params withCallbackID:callbackID]; if ("jssdk.payMyBill" == funcName) return [self payMyBill:params withCallbackID:callbackID]; if ("jssdk.showLogistics" == funcName) return [self showLogistics:params withCallbackID:callbackID]; }

-(NSString*) callbackToJS:(NSString*)callbackID withResult:(NSDictionary*)nativeResult { NSMutableDictionary *toJSResult = [NSMutableDictionary dictionary]; [toJSResult setObject:nativeResult ? nativeResult:[NSDictionary dictionary] forKey:@"params"]; [toJSResult setObject:callBackID forKey:@"callBackID"]; NSString *toJSResultJSON = [toJSResult JSONRepresentation]; //将NSDictionary格式化成NSString字符串 NSString *jsFinalResult = [_webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"processFinalResultFromNativeCode(%@);", toJSResultJSON]]; return jsFinalResult; }

function processFinalResultFromNativeCode(jsFinalResult) { var msgWrap = jsFinalResult; var ret = callback_map [jsFinalResult['callbackID']](jsFinalResult['params']); delete callback_map [jsFinalResult['callbackID']]; return JSON.stringify(ret);};

NSString *jssdkPath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"jssdk.js"];NSString *jssdkContent = [NSString stringWithContentsOfFile:jsPath encoding:NSUTF8StringEncoding error:nil];[_webView stringByEvaluatingJavaScriptFromString:jssdkContent];