;(function() {
var messagingIframe,
bridge =
'external'
,
CUSTOM_PROTOCOL_SCHEME =
'jscall'
;
if
(window[bridge]) {
return
}
function _createQueueReadyIframe(doc) {
messagingIframe = doc.createElement(
'iframe'
);
messagingIframe.style.display =
'none'
;
doc.documentElement.appendChild(messagingIframe);
}
window[bridge] = {};
var methods = [
"test1"
,
"test2"
];
for
(var i=
0
;i<methods.length;i++){ var=
""
method=
"methods[i];"
code=
"(window[bridge])[method] = function "
+=
""
"()="
" {messagingiframe.src="
CUSTOM_PROTOCOL_SCHEME
" ':'="
" arguments.callee.name="
" encodeuricomponent(json.stringify(arguments));}"
;=
""
eval(code);=
""
}=
""
创建iframe,必须在创建external之后,否则会出现死循环=
""
_createqueuereadyiframe(document);=
""
通知js开始初始化=
""
initready();=
""
})();=
""
<=
""
pre=
""
><br>
在以上代码中,我们在网页中添加了一个iframe,使得改变iframe的src时webview可以捕捉到重定向请求,使用这种方式进行重定向,相比直接修改网页的url要安全得多。同时设置了一个external对象,并为该对象绑定了test1、test2方法,当这两个方法执行时,会修改iframe的src,并将要调用的方法和参数以url的形式来构造。然后,我们需要在webview完成网页加载的时候进行注入,实现以下委托:
<p></p>
<p
class
=
"p1"
></p>
<pre
class
=
"brush:java;"
>- (
void
)webViewDidFinishLoad:(UIWebView *)webView {
//js是否注入成功
if
(![[webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@
"typeof window.%@ == 'object'"
, kBridgeName]] isEqualToString:@
"true"
]) {
NSBundle *bundle = _resourceBundle ? _resourceBundle : [NSBundle mainBundle];
NSString *filePath = [bundle pathForResource:@
"WebViewJsBridge"
ofType:@
"js"
];
NSString *js = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
[webView stringByEvaluatingJavaScriptFromString:js];
}
}</pre><br>
<p></p>
<p
class
=
"p1"
>注意,以上代码在注入前需要判断是否已经注入,避免重复注入。接下来,我们可以再实现一个webview的委托来拦截重定向事件:</p>
<p
class
=
"p1"
></p>
<pre
class
=
"brush:java;"
>- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
NSURL *url = [request URL];
NSString *requestString = [[request URL] absoluteString];
if
([requestString hasPrefix:kCustomProtocolScheme]) {
NSArray *components = [[url absoluteString] componentsSeparatedByString:@
":"
];
NSString *function = (NSString*)[components objectAtIndex:
1
];
NSString *argsAsString = [(NSString*)[components objectAtIndex:
2
]
stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSData *argsData = [argsAsString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *argsDic = (NSDictionary *)[NSJSONSerialization JSONObjectWithData:argsData options:kNilOptions error:NULL];
//将js的数组转换成objc的数组
NSMutableArray *args = [NSMutableArray array];
for
(
int
i=
0
; i<[argsDic count]; i++) {
[args addObject:[argsDic objectForKey:[NSString stringWithFormat:@
"%d"
, i]]];
}
//调用oc方法,忽略警告
#pragma clang diagnostic ignored
"-Warc-performSelector-leaks"
SEL selector = NSSelectorFromString([function stringByAppendingString:@
":"
]);
CLog(@
"sel:%@, args:%@"
, function, args);
if
([self respondsToSelector:selector]) {
[self performSelector:selector withObject:args];
}
return
NO;
}
else
{
return
YES;
}
}</pre><br>
以上代码中对重定向的url进行了判断,如果符合我们事先定义的协议,就进行解析,否则就进行跳转。解析的时候以冒号分隔,取出函数名和参数列表,并调用相应的方法。至此,我们就在iOS中实现了与Android相同的调用机制。
<p></p>
<p
class
=
"p1"
>但以上方法的缺点是还没有达到Android中的方便程度,最理想的方法是当进行js注入时,动态设置待绑定的对象,同时动态获取当前类的实例方法,并生成相应的js代码。这就完全实现了Android中的绑定机制。这个实现起来并不难,有兴趣的朋友可以试试。</p>
<p
class
=
"p1"
>为了方便大家使用,我将以上代码封装成了一个类,具体的使用方法见Demo。</p>
<p
class
=
"p1"
>需要注意的是:</p>
<p
class
=
"p1"
>
1
. 在实际应用中可能出现js执行顺序的问题,如果网页中的js在注入前先获取绑定的对象进行保存,是无法获取到的,因为这时候待绑定的对象为空。这就需要网页中js的初始化在注入之后,因此在上文代码中有一个initReady方法。</p>
<p
class
=
"p1"
>
2
. 由于performSelector最多只能包含两个参数,因此例子中是通过数组来传递参数列表的。</p>
<p
class
=
"p1"
><br>
</p>
<p
class
=
"p1"
></p>
如果大家觉得对自己有帮助的话,还希望能帮顶一下,谢谢:)
个人博客:http:
//blog.csdn.net/zhaoxy2850
本文地址:http:
//blog.csdn.net/zhaoxy_thu/article/details/22794201
转载请注明出处,谢谢!
<br>
<p></p> </methods.length;i++){>