闲言稍絮
什么是动态功能部署、动态功能部署有什么优缺点、市面上都有哪些动态功能部署的方案、这些方案横向对比优点缺点各是什么等等,类似的问题及答案就不赘述了,某度一搜相关的文章有好几十页,有兴趣的可以去看看,下面直接讨论我们的实现方式。
NJCS的整体介绍
IOS视图控制器之间的关系
[xxx.navigationController pushViewController:yyy animated:YES/NO];
[xxx.navigationController popViewControllerAnimated:YES?NO];
以及
[xxx presentViewController:yyy animated:YES/NO completion:nil];
[xxx dismissViewControllerAnimated:YES/NO completion:nil];
以上几个关于视图转换的方法我们在开发过程中经常用到,但是在这些方法在执行完成之后各个viewController之间是以什么样的关系存在的,没有具体的想过,之前也看过一篇关于相关内容的文章,时间久了再想看时文章已经找不到了,所以这次打算写代码测一下结果,并总结记录下来:
UIViewController分为两种类型:
viewController容器,如:UITabBarController、UINavigationController等
viewcontroller:UIViewController及其自定义的子类
https简要介绍
HTTPS其实是有两部分组成:HTTP + SSL / TLS,
也就是在HTTP上又加了一层处理加密信息的模块。服务端和客户端的信息传输都会通过TLS进行加密,所以传输的数据都是加密后的数据
版本控制
NJCS在接入的时候需要传入js所在的文件路径,初始化时会去相应的路径读取文件,如果文件不存在则会去NSBundle内读取内置文件,所以如果想更新JS文件只需要将最新的文件放入接入时指定的路径即可,目前我们的做法是:
1:app在启动后会拉取一个版本配置文件,里面包含了三个字段
version:js文件版本号
url:js文件地址
controllers:需要被关闭的vc名字如:[“MineViewController”, “setViewController”]
读取配置文件版本号和本地的js版本做对比,如果不相同则从url处拉取最新js zip(如:3.zip)包,并解压到指定文件夹下,解压缩成功后更新本地version字段
2:下次启动时读取最新版本的js文件,并将旧版本文件删除
扩展介绍
目前内置的扩展实现有:
NJCSVCManager
NJCSViewAnimate
NJCSNotificationManager
NJCSUserDefaultManager
NJCSModelAdapter
其余的扩展都是接口层的实现,需要用户根据自己的情况具体实现,如:
NJCSHttp
NJCSAlert
NJCSLogComponent
等……
1:下面以NJCSHttp为例进行扩展接入介绍:
接口协议:
#import#import typedef void(^callbackBlock)(BOOL isSuccess, id resultData); @protocol NJCSHttpInterfaceProtocol // // 外部需要实现的http get请求 // // @param urlString 接口url 字符串 // @param params 请求参数字典 // @param resultBlock 请求结束后的block // - (void)Get:(NSString *)urlString params:(NSDictionary *)params resultBlock:(void(^)(BOOL isSuccess, id resultData))resultBlock; // // 外部需要实现的http post请求 // // @param urlString 接口url 字符串 // @param params 请求参数字典 // @param resultBlock 请求结束后的block // - (void)Post:(NSString *)urlString params:(NSDictionary *)params resultBlock:(void(^)(BOOL isSuccess, id resultData))resultBlock; @end @protocol NJCSHttpProtocol - (void)GetRequestWithURLString:(NSString *)urlString params:(NSDictionary *)params callbackConfig:(NSDictionary *)callbackConfig; - (void)PostRequestWithURLString:(NSString *)urlString params:(NSDictionary *)params callbackConfig:(NSDictionary *)callbackConfig; @end
Notification的简单使用
Notification也是通过扩展的方式接进去的,目前支持的功能有:
- JS->JS
- JS->Native
- Native->JS
扩展的方法如下:
- (void)postNotificationName:(NSString *)notificationName object:(id)notificationSender userInfo:(NSDictionary *)userInfo; - (void)addNotificationName:(NSString *)notificationName processor:(NSDictionary *)processor object:(id)notificationSender; - (void)removeNotificationName:(NSString *)notificationName instanceName:(NSString *)instanceName;
分别为:
发起Notification
添加Notification
移除Notification
详细实现代码如下:
- (void)postNotificationName:(NSString *)notificationName object:(id)notificationSender userInfo:(NSDictionary *)userInfo { [[NSNotificationCenter defaultCenter] postNotificationName:notificationName object:notificationSender userInfo:userInfo]; } - (void)addNotificationName:(NSString *)notificationName processor:(NSDictionary *)processor object:(id)notificationSender { if (notificationName && [processor isKindOfClass:[NSDictionary class]]) { if (!_processors[notificationName]) _processors[notificationName] = [[NSMutableArray alloc] initWithCapacity:0]; (!processor) ?: [_processors[notificationName] addObject:processor]; } [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(p_handleNotification:) name:notificationName object:notificationSender]; } - (void)removeNotificationName:(NSString *)notificationName instanceName:(NSString *)instanceName { if (!notificationName || !instanceName) { return; } [_processors[notificationName] enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { if ([instanceName isEqualToString:obj[kInstanceName]]) { [_processors removeObjectForKey:obj]; } }]; } - (void)p_handleNotification:(NSNotification *)notify { [_processors[notify.name] enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { [[NJCSBridge getInstance] runJSFunctionWithConfigures:obj]; }]; }
使用示例:
var notifiConfig = {kInstanceName:"VCInstanceName", kMethodName:"methodName"};//生成配置 nativeBridge.addNotificationName_processor("NOTIFICATIONNAME", notifiConfig, null);//添加监听 nativeBridge.postNotificationName("NOTIFICATIONNAME", null, null);//发起通知
简单的动画实现
目前动画只支持UIView的几个简单的animate,是通过扩展的方式接入的,直接上代码:
1:接入代码
- (instancetype)init { if (self = [super init]) { _viewControllers = [NSMapTable strongToWeakObjectsMapTable]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(njcsViewControllerDealloc:) name:NJCSVIEWCONTROLLER_DEALLOC object:nil]; _njcsJSContext = [[NJCS_JSContext alloc] init]; _njcsJSContext.jscontext[@"NJCSBridge_Native"] = self; self.macors = [[NJCSMacors alloc] init]; self.njcsHttp = [[NJCSHttp alloc] init]; self.viewAnimate = [[NJCSViewAnimate alloc] init]; self.modelAdapter = [[NJCSModelAdapter alloc] init]; self.notifyManager = [NJCSNotificationManager sharedManager]; self.njcsAlert = [NJCSAlert shareInstance]; } return self; }
viewAnimate是做为必接扩展添加进去的,是用户必须实现的。
对外公开的几个方法如下:
- (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^ _Nullable)(void))animations { [UIView animateWithDuration:duration animations:animations]; } - (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^ _Nullable)(void))animations completion:(void (^ _Nullable)(BOOL))completion { [UIView animateWithDuration:duration animations:animations completion:completion]; } - (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^ _Nullable)(void))animations completion:(void (^ _Nullable)(BOOL))completion { [UIView animateWithDuration:duration delay:delay options:options animations:animations completion:completion]; }
都是些很简单的实现,这里就不详细介绍了。
GCD的使用
闲话少说,直接上代码:
- (void)dispatch_async_main:(JSValue *)function { NSParameterAssert(function); dispatch_async(dispatch_get_main_queue(), ^{ [function callWithArguments:nil]; }); }
异步主线程,只是将dispatch_async进行了简单的包装;
- (void)dispatch_async_global_queue:(JSValue *)function afterFunction:(JSValue *)afterFunction { NSParameterAssert(function); NSParameterAssert(afterFunction); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ JSValue *result = [function callWithArguments:nil]; [afterFunction callWithArguments:@[result]]; }); }
异步全局队列,也是简单的包装,之前看过一片关于JS锁的文章,里面说不管是在哪个线程调用js代码,只要JSContext相同就都会在主线程调用,如果想实现真正的异步,需要新建js虚拟机,后续会进行测试验证;
- (void)dispatch_after:(NSInteger)time function:(JSValue *)function { NSParameterAssert(time); NSParameterAssert(function); dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(time * NSEC_PER_SEC)); dispatch_after(delayTime, dispatch_get_main_queue(), ^{ [function callWithArguments:nil]; }); }
同上,这里都不赘述了,关于其他GCD相关的需要的时候进行封装即可;
NJCS-数据模型
一个对象在JS和OC之间传递的时候会有个对应的类型转换,在JSValue.h源码中有个简单的介绍:
@textblock Objective-C type | JavaScript type --------------------+--------------------- nil | undefined NSNull | null NSString | string NSNumber | number, boolean NSDictionary | Object object NSArray | Array object NSDate | Date object NSBlock (1) | Function object (1) id (2) | Wrapper object (2) Class (3) | Constructor object (3) @/textblock这里重点提一下OC 的 id类型到JS环境后会是Wrapper object,在IOS7 及以下的系统中这个Wrapper object在被访问的时候会引起崩溃异常,这个坑很深,困扰了好几天才想到比较好的解决办法,将id类型封装到NSDictionary内部返回的,详细的情况在[原理部分]()会进行介绍。Native的方法在被执行的时候参数来源有两种,第一种:从OC传给JS,再从JS回传给OC,这种参数是不需要进行类型转换可以直接使用的;第二种:直接在JS生成,简单的类型如:Array、Dictionary、Data会在传递过程中自动转换,复杂的类型:如 CGRect、UIColor以及自定义的数据类型是无法自动转换的就需要手动封装。
NJCS-方法调用
NJCS内方法的调用有两种方式,这里分别介绍一下:
1:链式调用
代码示例:
var UITableView = getNativeClassWithClassName("UITableView");//根据字符串获取UITableView类 var tableView = UITableView.alloc().init();//初始化UITableView实例 this.view().addSubView(tableView);//获取当前viewController的view 并添加tableView tableView.setFrame(NJCSCGRect.create(0, 0, NJCSMacors["mainScreenWidth"], NJCSMacors["screenHeight"] - 50));//设置tableView的frame tableView.setBackgroundColor(NJCSUIColor_JS.create("#999999:0"));//设置tableView的背景颜色UITableView在执行alloc方法后会返回一个对象,然后在执行该对象的init方法,具体的实现会在[原理文章]()中介绍。 2:簇调用