iOS:JavaScriptCore实现Native和JS交互
2017-12-05 本文已影响42人
风与鸾
前言
自从iOS7以后苹果提供了JavaScriptCore用于实现Native和JS交互
实践
目前项目有些分享页面用H5实现的,后端返回一个URL,前端这边用webView展现这个界面。
1341512444174_.pic.jpg
需求
H5这边需要前端这边提供一个token,同时由H5这边返回需要分享的信息(title,iconImage,需要分享的URL),点击邀请好友调用Native端代码弹出分享窗口。
1351512444422_.pic.jpg
开始之前如果没有JavaScriptCore基础的,需要先去了解JSContext,JSExport,JSValue相关知识了解了这些你就能大概知道Native是如何与JS交互的。
通过获取的webView URL加载webViewController
//如果需要先清理缓存的可以这么做
NSHTTPCookie *cookie;
NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (cookie in [storage cookies]){
[storage deleteCookie:cookie];
}
//清除UIWebView的缓存
[[NSURLCache sharedURLCache] removeAllCachedResponses];
NSURLCache * cache = [NSURLCache sharedURLCache];
[cache removeAllCachedResponses];
[cache setDiskCapacity:0];
[cache setMemoryCapacity:0];
NSMutableURLRequest *requset = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlString]];
[self.webView loadRequest:requset];
在webView的delegate里面
#pragma mark webViewDelegate
- (void)webViewDidFinishLoad:(UIWebView *)webView {
self.title = [self.webView stringByEvaluatingJavaScriptFromString:@"document.title"];
self.jsContext = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
/**注入JS需要的OC对象*/
JSNativeDelegate *jsDelegate = [[JSNativeDelegate alloc] initWith:self];
self.jsContext[@"Toyun"] = jsDelegate;
self.jsContext.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {
context.exception = exceptionValue;
NSLog(@"JKRWebViewController异常信息:%@", exceptionValue);
};
}
JSNativeDelegate.h 的代码
#import <Foundation/Foundation.h>
#import <JavaScriptCore/JavaScriptCore.h>
@protocol JSObjcDelegate<JSExport>
- (void)shareAction;
- (void)getToken;
- (void)setShareInfo:(NSString *)shareInfo;
@end
@protocol JSNativeManagerDelegate
- (void)shareAction;
- (void)getToken;
- (void)setShareInfo:(NSString *)shareInfo;
@end
@interface JSNativeDelegate : NSObject
@property (nonatomic, weak) id<JSNativeManagerDelegate>delegate;
- (instancetype)initWith:(id<JSNativeManagerDelegate>)delegate;
@end
JSNativeDelegate.m实现
#import "JSNativeDelegate.h"
@interface JSNativeDelegate()<JSObjcDelegate>
@end
@implementation JSNativeDelegate
- (instancetype)initWith:(id<JSNativeManagerDelegate>)delegate
{
self = [super init];
if(self)
{
self.delegate = delegate;
}
return self;
}
- (void)shareAction
{
if (self.delegate) {
[self.delegate shareAction];
}
}
- (void)getToken
{
if (self.delegate) {
[self.delegate getToken];
}
}
- (void)setShareInfo:(NSString *)shareInfo
{
if (self.delegate) {
[self.delegate setShareInfo:shareInfo];
}
}
@end
其中处理JS和OC数据部分转换的对象JSValue很重要,也是个核心。
比如你H5这边写了一个函数setShareInfoCallback,可以通过JSContext获取这个函数并前端这边调用如下:
/**获取JS函数*/
JSValue *shareCallback = self.jsContext[@"setShareInfoCallback"];
[shareCallback callWithArguments:@[shareInfo]];
JSExport我的理解是Native与JS的代理,JS这边调用Native的代码通过这个类实现的,不过方法名需要H5和前端这边约定一样即可。
比如:
@protocol JSObjcDelegate<JSExport>
//H5这边邀请好友按钮click事件
- (void)shareAction;
//H5加载时通过前端获取token
- (void)getToken;
//H5这边返回分享信息
- (void)setShareInfo:(NSString *)shareInfo;
@end
这里值得注意的是在ViewController里面,如果直接把self作为JS直接引用对象,会引起内存泄漏,我这里的处理方法是引用一个第三方对象来处理,如上文的JSNativeDelegate
如下文这样写会引起内存泄漏:
/**注入JS需要的OC对象*/
self.jsContext[@"Toyun"] = self;
我的处理方式:
/**注入JS需要的OC对象*/
JSNativeDelegate *jsDelegate = [[JSNativeDelegate alloc] initWith:self];
self.jsContext[@"Toyun"] = jsDelegate;
然后在ViewController处理JSNativeDelegate代理方法即可实现与JS与Native的交互。
Native端部分代码
- (void)setShareInfo:(NSString *)shareInfo {
_shareInfo = shareInfo;
/**获取JS函数*/
JSValue *shareCallback = self.jsContext[@"setShareInfoCallback"];
[shareCallback callWithArguments:@[shareInfo]];
NSData *jsonData = [shareInfo dataUsingEncoding:NSUTF8StringEncoding];
NSError *err;
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&err];
if(err) {
NSLog(@"json解析失败:%@",err);
self.model = nil;
}
JKRShareModel *model = [JKRShareModel mj_objectWithKeyValues:dic];
self.model = model;
}
- (void)setModel:(JKRShareModel *)model {
_model = model;
@weakify(self);
dispatch_async(dispatch_get_main_queue(), ^{
@strongify(self);
if (_model) {
UIBarButtonItem *rightBarBtn = [[UIBarButtonItem alloc] initWithCustomView:self.shareBtn];
self.navigationItem.rightBarButtonItem = rightBarBtn;
} else {
self.navigationItem.rightBarButtonItem = nil;
}
});
}
- (void)shareAction {
__weak typeof(self) weakSelf = self;
if ([weakSelf.model.type isEqualToString:@"normal"]) {
dispatch_async(dispatch_get_main_queue(), ^{
[JKRShareManager jkr_shareURLWithTitle:weakSelf.model.title
icon:weakSelf.model.imgUrl
subHead:weakSelf.model.desc
url:weakSelf.model.link
currentViewController:nil];
});
}
}