版本记录
版本号 | 时间 |
---|---|
V1.0 | 2018.07.10 |
前言
JavaScriptCore
是用来评估应用程序中的JavaScript
程序,并支持应用程序的JavaScript脚本编写。接下来这几篇我们就详细的解析一下JavaScriptCore框架的使用情况。感兴趣的可以看上面写的那篇。
1. JavaScriptCore 框架详细解析(一) —— 基本概要
2. JavaScriptCore 框架详细解析(二) —— JS与OC通信
3. JavaScriptCore 框架详细解析(三) —— 内存管理与线程安全
应用场景
在做项目的时候,有时候全部用H5做不是很好,如果用H5做有很多交互不友好的地方,这个时候我们就需要用原生的去完成。所以,我们就需要监听H5中的事件(比如说点击了H5中的按钮),去完成下面的操作,其实就相当于H5调用了你的原生。
H5调用原生的方法可以在原生中的进行其他操作,这就自然而然的相当于将事件处理权由H5过渡到了原生。其中我们做直播的一个应用场景,就是在直播间中半屏H5活动页面,单击H5中的按钮,然后Push到一个全屏的页面,这个Push操作就需要在点击H5页面后去做,其实就是相当于H5调用了我们的原生完成了后面的操作。
实现流程
1. 定义函数
移动端和前端需要一起定义一个函数,函数名称相同,iOS和Android还有前端都是用这个函数,其实就是以后H5调用原生移动端的一个入口。比如我们定义的就是下面这个函数(或称方法)。
- (void)onJsCallback:(NSString *)json
传递的参数是json格式的,H5调用原生的时候就会将参数以json格式传递给移动端,移动端解析出来,获取到数据,对数据进行处理,把处理权掌握在自己手里,就达到了前端H5和移动端的通信。
2. 封装JS引擎类
其实这一步骤可以不做,可以直接在Webview类里面做处理,但是我这里单独提出来,其实是有原因的,如果都放在webview里面,一是会显的很乱;二是耦合太严重逻辑不清晰,所以这里封装了一个JS引擎单例类。
1. JJJSEngine.h
#import
#import
@protocol JJJSExport
//H5调用native
- (void)onJsCallback:(NSString *)json;
@end
@interface JJJSEngine : NSObject
@property (nonatomic, weak) UIWebView *webview;
@property (nonatomic, weak) JSContext *jsContext;
@property (nonatomic, strong) NSDictionary *moreDict;
+ (instancetype)shared;
//清理
- (void)cleanUp;
//native向js发送消息
- (void)sendJSMessage:(NSString *)content;
@end
2. JJJSEngine.m
#import "JJJSEngine.h"
#import "JSONHelp.h"
#import
@interface JJJSEngine()
@property (nonatomic, strong) dispatch_queue_t jsQueue;
@property (nonatomic, strong) NSMutableDictionary *jsCallbacksDictM;
@end
@implementation JJJSEngine
#pragma mark - Override Base Function
- (instancetype)init
{
self = [super init];
if (self) {
_jsQueue = dispatch_queue_create("com.maobotv.jsbridge", DISPATCH_QUEUE_SERIAL);
_jsCallbacksDictM = [NSMutableDictionary dictionaryWithCapacity:5];
}
return self;
}
#pragma mark - Object Private Function
//发送JS消息
- (void)sendJSMessageName:(NSString *)callback response:(NSDictionary *)response
{
if ([callback isNotEmpty] == NO || self.webview == nil) {
return;
}
NSString *responseStr = [JSONHelp object2NSString:response];
responseStr = responseStr ? : @"";
responseStr = [responseStr stringByReplacingOccurrencesOfString:@"'" withString:@"\'"];
NSString *jsCallStr = [NSString stringWithFormat: @"%@(\'%@\');", callback, responseStr];
dispatch_async(dispatch_get_main_queue(), ^{
@try {
NSString *result = [self.webview stringByEvaluatingJavaScriptFromString:jsCallStr];
if (result == nil) {
DDLogError(@"evaluate script fail :%@",jsCallStr);
}
} @catch (NSException *exception) {
DDLogError(@"webview evaluate error :%@",[exception reason]);
}
});
}
#pragma mark - Object Public Function
- (void)cleanUp
{
[self.jsCallbacksDictM removeAllObjects];
}
//native向js发送消息
- (void)sendJSMessage:(NSString *)content
{
//字符串转对象
NSDictionary *param = [JSONHelp string2NSObject:content];
//这个JJJSUtil是工具类,用于一些常用工具处理
NSDictionary *response = [JJJSUtil jsResponseWithRet:0 errorMsg:nil param:param];
[self sendJSMessageName:self.jsCallbacksDictM[@"自定义的字符串key"] response:response];
}
#pragma mark - Class Public Function
+ (instancetype)shared
{
static JJJSEngine *object;
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
object = [[self alloc] init];
});
return object;
}
#pragma mark - JJJSExport
//H5调用native
- (void)onJsCallback:(NSString *)json
{
IMP_WSELF()
dispatch_async(_jsQueue, ^{
IMP_SSELF()
if (sself == nil) {
return;
}
id jsonObj = [JSONHelp string2NSObject:json];
if ([jsonObj isKindOfClass: [NSDictionary class]]) {
NSDictionary *dict = (NSDictionary *)jsonObj;
NSString *actionTypeStr = _To_Str(dict[@"action"]);
NSDictionary *dataDict = _To_Dict(dict[@"data"]);
NSString *contentURL = _To_Str(dataDict[@"url"]);
dispatch_async(dispatch_get_main_queue(), ^{
//全屏的H5
if ([actionTypeStr isEqualToString:@"toFullPage"]) {
JJWebViewVC *webVC = [[JJWebViewVC alloc] init];
extern BOOL isTestCondition;
webVC.urlStr = contentURL;
self.moreDict = _To_Dict(dataDict[@"more"]);
webVC.isOpenGuardH5 = YES;
webVC.feed = self.feed;
webVC.view.backgroundColor = [UIColor colorWithRed:35/255.0 green:27/255.0 blue:54/255.0 alpha:1.0];
[JJCurrentNaviController pushViewController:webVC animated:YES];
}
//返回上一页
else if([actionTypeStr isEqualToString:@"wvBack"]){
if ([self.webview canGoBack]) {
[self.webview goBack];
}
else {
[JJCurrentNaviController popViewControllerAnimated:YES];
}
}
//展示迷你卡
else if([actionTypeStr isEqualToString:@"showCard"]){
NSString *uid = _To_Str(dataDict[@"uid"]);
[[NSNotificationCenter defaultCenter] postNotificationName:kNotification_show_user_info_card object:uid];
}
});
}
});
}
@end
上面的类JJJSUtil是一个工具类,用于存放一些基本的逻辑处理,比如
//判断url的前缀是否相等(?之前)
+ (BOOL)isH5URLPrefixEqual:(NSString *)url1 url2:(NSString *)url2;
//判断url的前缀是否相等(?之前)
+ (BOOL)isH5URLPrefixEqual:(NSString *)url1 url2:(NSString *)url2
{
if (url1 == nil || url2 == nil) {
return NO;
}
NSRange range1 = [url1 rangeOfString:@"?" options:NSBackwardsSearch];
if (range1.location != NSNotFound) {
url1 = [url1 substringToIndex:range1.location];
}
NSRange range2 = [url2 rangeOfString:@"?" options:NSBackwardsSearch];
if (range2.location != NSNotFound) {
url2 = [url2 substringToIndex:range2.location];
}
return [url1 isEqualToString:url2];
}
3. webview类中的处理
前面我们已经写好了jsbridge引擎,下面我们要在webview中做一些事情,这样才能将我们定义的引擎和webview中的H5连接起来,让路“通”起来。
#import "JJJSEngine.h"
@property (nonatomic, strong) JJJSEngine *jsEngine;
- (void)viewDidLoad
{
[super viewDidLoad];
[self createWebView];
[JJJSEngine shared].webview = self.webView;
self.jsContext = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
self.jsContext[@"JSCallInterface"] = [JJJSEngine shared];
}
上面self.jsContext[@"JSCallInterface"] = [JJJSEngine shared];
,JSCallInterface
是告诉jsbridge
要找那个对象去处理,这个字符串也是和前段定义好的了。
下面接着在代理方法中继续处理
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
_isLoadingFinished = YES;
[self.loadingView removeLoadingView];
self.loadingView = nil;
self.jsContext = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
self.jsContext[@"JSCallInterface"] = [JJJSEngine shared];
[JJJSEngine shared].jsContext = self.jsContext;
[JJJSEngine shared].webview = self.webView;
// 增加jsBridge异常的处理
self.jsContext.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {
context.exception = exceptionValue;
CPLog(@"jsbridge - 异常信息:%@", exceptionValue);
};
}
最后别忘记在dealloc中进行销毁处理
- (void)dealloc
{
[[JJJSEngine shared] cleanUp];
[JJJSEngine shared].webview = nil;
[self.webView stopLoading];
self.webView.delegate = nil;
[self.webView removeFromSuperview];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
4. 场景验证
现在有一个需求,比如说直播间的半屏的H5页面,要求我们点击某一个按钮,我们push到一个全屏的H5页面,按钮点击一定是H5的东西,要求的就是通过jsbridge让原生对点击事件进行响应,具体跳转到哪个界面,都是通过上面一起定义的函数的json数据进行传递的。
后记
本篇主要讲述了jsbridge的集成,感兴趣的给个赞或者关注~~~~