簡單認識:kerkee 是一個多主體共存型 Hybrid 框架,具有跨平台、用戶體驗好、性能高、擴展性好、靈活性強、易維護、規范化、集成雲服務、具有Debug環境、徹底解決跨域問題。該框架從開發者角度支持三種團隊開發模式:Web開發者 、Native開發者 、Web開發者和Native團隊共同合作的開發團隊 。
下面我將從 Web開發者和Native(iOS)團隊共同合作的開發團隊 模式來分析使用該框架。
官網地址:http://www.kerkee.com
Github 源碼地址:https://github.com/kercer
QQ交流群:110710084
二、簡單的使用:
kerkee在iOS上的快速上手指南
1、建立新項目 kerkeeHDDemo 在項目目錄中建立 Podfile 文件:
platform :ios, '7.0'inhibit_all_warnings!pod ‘kerkee’, ’~> 1.0.1’
2、使用 CocoaPods 來導入 kerkee 框架,使用終端 cd 到你的 Podfile 文件所在的目錄:
cd $PodfilePathpod install
3、運行 Podfile 同目錄中的 kerkeeHDDemo.xcworkspace 即可;
4、在項目中添加 html 代碼:(本人對 html 不熟悉,相信懂的人肯定懂)
kerkeeTest.html :
[object Object] [object Object]
上面有三種方式來處理按鈕的點擊事件:
i. 直接在 中處理
ii. 使用 function 處理;
iii. 使用 js 來處理 function。
代碼解釋:
kerkeeTest.js :
var kerkeeJSManager;function buttonClick2(s1)
{ kerkeeJSManager.jsToOc(s1);
}5、在項目中添加 iOS 代碼:
HDJSToOCManager.h:
#import #import "KCJSObject.h"#import "KCArgList.h"// 這個類作為 和 js 交互橋梁類@interface HDJSToOCManager : KCJSObject- (void)jsToOc:(KCWebView*)aWebView argList:(KCArgList*)args; - (void)mutualJSOC:(KCWebView*)aWebView argList:(KCArgList*)args;@end
HDJSToOCManager.m:
#import "HDJSToOCManager.h"#import #import "KCJSBridge.h"@implementation HDJSToOCManager- (NSString*)getJSObjectName{ // 這個 和 js 中的變量要保持一致
return @"kerkeeJSManager";
}// js 中 可以調用 jsToOc() 來調用- (void)jsToOc:(KCWebView*)aWebView argList:(KCArgList*)args{ NSLog(@"JS調用OC args : %@", args);
}
- (void)mutualJSOC:(KCWebView*)aWebView argList:(KCArgList*)args{ NSLog(@"JS調用OC,OC回調JS args : %@", args); NSMutableDictionary *dic = [NSMutableDictionary dictionary];
[dic setObject:@"success" forKey:@"info"]; NSString *json = [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:dic options:0 error:nil] encoding:NSUTF8StringEncoding]; KCAutorelease(json); //回調,callbackId,kerkee.js 內部已經處理好
[KCJSBridge callbackJS:aWebView callBackID:[args getObject:@"callbackId"] jsonString:json];
}@end這個類主要是生成和 js 對應的變量 kerkeeJSManager ,是 iOS 這邊 iOS 和 js 交互橋梁
ViewController.m:
- (void)viewDidLoad {
[super viewDidLoad]; // 將 HDJSToOCManager對象 和 js中的 kerkeeJSManager (詳見HDJSToOCManager)綁定
[KCJSBridge registObject:[[HDJSToOCManager alloc] init]]; NSString *file = [[NSBundle mainBundle] pathForResource:@"kerkeeTest" ofType:@"html"]; NSURL *url = [NSURL URLWithString:file]; NSMutableURLRequest *request =[NSMutableURLRequest requestWithURL:url];
m_webView = [[KCWebView alloc] initWithFrame:self.view.bounds];
[m_webView loadRequest:request];
[self.view addSubview:m_webView]; // 在m_webView 加載 “kerkee.js” 代碼,具體代碼見 KCApiBridge.m 中的 init 方法 (這個時候需要在項目中添加“kerkee.js” 文件)
m_jsBridge = [[KCJSBridge alloc] initWithWebView:m_webView delegate:self];
UIButton *bb = [UIButton buttonWithType:UIButtonTypeInfoLight];
bb.frame = CGRectMake(100, 400, 50, 50);
[bb addTarget:self action:@selector(ocToJs) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:bb];
}
- (void)ocToJs{
[KCJSBridge callJSFunction:@"ocToJs" withJSONObject:@{@"hhh" : @"www"} WebView:m_webView];
}
#pragma mark --
#pragma mark KCWebViewProgressDelegate
-(void)webView:(KCWebView*)webView identifierForInitialRequest:(NSURLRequest*)initialRequest{
}
#pragma mark - UIWebView Delegate
- (void)webViewDidFinishLoad:(UIWebView *)aWebView{ NSString *scrollHeight = [aWebView stringByEvaluatingJavaScriptFromString:@"document.body.scrollHeight;"]; NSLog(@"scrollHeight: %@", scrollHeight); NSLog(@"webview.contentSize.height %f", aWebView.scrollView.contentSize.height);
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error{
}
- (BOOL)webView:(UIWebView *)aWebView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{ return YES;
}上面的代碼並不多,其核心代碼之一在 KCApiBridge 中,可以看到, KCApiBridge 在初始化的時候找到 kerkee.js 並且使用- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script; 在 iOS 的 UIWebView 注入 kerkee.js 代碼。這份 js 才是真正的將 iOS 的橋梁 HDJSToOCManager 實體和 js變量 kerkeeJSManager 的關鍵代碼。在 這裡 可以看到作者在demo中的 kerkee.js 源碼。因為我對 js 也是入門階段,所以選擇在源碼上改動之後,代碼如下:
;(function(window){ if (window.WebViewJSBridge) return; window.WebViewJSBridge = {
}; console.log("--- kerkee init begin---"); var browser={ versions:function(){ var u = navigator.userAgent, app = navigator.appVersion; return {
trident: u.indexOf('Trident') > -1, //IE
presto: u.indexOf('Presto') > -1, //opera
webKit: u.indexOf('AppleWebKit') > -1, //apple&google kernel
gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') == -1,//firfox
mobile: !!u.match(/AppleWebKit.*Mobile.*/), //is Mobile
ios: !!u.match(/(i[^;]+;( U;)? CPU.+Mac OS X/), //is ios
android: u.indexOf('Android') > -1 || u.indexOf('Adr') > -1, //android
iPhone: u.indexOf('iPhone') > -1 , //iPhone or QQHD
iPad: u.indexOf('iPad') > -1, //iPad
iPod: u.indexOf('iPod') > -1, //iPod
webApp: u.indexOf('Safari') == -1, //is webapp,no header and footer
weixin: u.indexOf('MicroMessenger') > -1, //is wechat
qq: u.match(/sQQ/i) == " qq" //is qq
};
}(),
language:(navigator.browserLanguage || navigator.language).toLowerCase()
} var global = this || window; var ApiBridge ={
msgQueue : [],
callbackCache : [],
callbackId : 0,
processingMsg : false,
isReady : false,
isNotifyReady : false
}; ApiBridge.create = function()
{
ApiBridge.bridgeIframe = document.createElement("iframe"); ApiBridge.bridgeIframe.style.display = 'none'; document.documentElement.appendChild(ApiBridge.bridgeIframe);
}; ApiBridge.prepareProcessingMessages = function()
{ ApiBridge.processingMsg = true;
}; ApiBridge.fetchMessages = function()
{ if (ApiBridge.msgQueue.length > 0)
{ var messages = JSON.stringify(ApiBridge.msgQueue); ApiBridge.msgQueue.length = 0; return messages;
} ApiBridge.processingMsg = false; return null;
}; ApiBridge.callNative = function(clz, method, args, callback)
{ var msgJson = {}; msgJson.clz = clz; msgJson.method = method; if (args != undefined) msgJson.args = args; if (callback)
{ var callbackId = ApiBridge.getCallbackId(); ApiBridge.callbackCache[callbackId] = callback; if (msgJson.args)
{ msgJson.args.callbackId = callbackId.toString();
} else
{ msgJson.args =
{ "callbackId" : callbackId.toString()
};
}
} if (browser.versions.ios)
{ if (ApiBridge.bridgeIframe == undefined)
{ ApiBridge.create();
} // var msgJson = {"clz": clz, "method": method, "args": args};
ApiBridge.msgQueue.push(msgJson); if (!ApiBridge.processingMsg) ApiBridge.bridgeIframe.src = "kcnative://go";
} else if (browser.versions.android)
{ // android
return prompt(JSON.stringify(msgJson));
}
}; ApiBridge.log = function(msg)
{ ApiBridge.callNative("ApiBridge", "JSLog",
{ "msg" : msg
});
} ApiBridge.getCallbackId = function()
{ return ApiBridge.callbackId++;
} ApiBridge.onCallback = function(callbackId, obj)
{ if (ApiBridge.callbackCache[callbackId])
{ ApiBridge.callbackCache[callbackId](obj); // ApiBridge.callbackCache[callbackId] = undefined;
// //如果是注冊事件的話,不能undefined;
}
} ApiBridge.onBridgeInitComplete = function(callback)
{ ApiBridge.callNative("ApiBridge", "onBridgeInitComplete", {}, callback);
} ApiBridge.onNativeInitComplete = function(callback)
{ ApiBridge.isReady = true; console.log("--- kerkee onNativeInitComplete end ---"); if (callback)
{ callback(); ApiBridge.isNotifyReady = true; console.log("--- device ready go--- ");
}
} ApiBridge.compile = function(aIdentity, aJS)
{ var value; var error; try
{
value = eval(aJS);
} catch (e)
{ var err = {}; err.name = e.name; err.message = e.message; err.number = e.number & 0xFFFF;
error = err;
} ApiBridge.callNative("ApiBridge", "compile",
{ "identity" : aIdentity, "returnValue" : value, "error" : error
});
} var _Configs =
{
isOpenJSLog:false,
sysLog:{},
isOpenNativeXHR:true,
sysXHR:{}
}; _Configs.sysLog = global.console.log; _Configs.sysXHR = global.XMLHttpRequest; var kerkee = {}; /***************************************** * JS和iOS 交互接口 *****************************************/
kerkee.jsToOc = function(s1)
{ ApiBridge.callNative("kerkeeJSManager", "jsToOc",
{ "s1" : s1
});
}; kerkee.mutualJSOC = function(aString, callback)
{ ApiBridge.callNative("kerkeeJSManager", "mutualJSOC",
{ "aString" : aString
}, callback);
} global.kerkeeJSManager = kerkee; kerkee.openJSLog = function()
{ _Configs.isOpenJSLog = true; global.console.log = ApiBridge.log;
} kerkee.closeJSLog = function()
{ _Configs.isOpenJSLog = false; global.console.log = _Configs.sysLog;
} kerkee.onDeviceReady = function(handler)
{ ApiBridge.onDeviceReady = handler; if (ApiBridge.isReady && !ApiBridge.isNotifyReady && handler)
{ console.log("-- device ready --"); handler(); ApiBridge.isNotifyReady = true;
}
}; kerkee.invoke = function(clz, method, args, callback)
{ if (callback)
{ ApiBridge.callNative(clz, method, args, callback);
} else
{ ApiBridge.callNative(clz, method, args);
}
}; kerkee.onSetImage = function(srcSuffix, desUri)
{ // console.log("--- kerkee onSetImage ---");
var obj = document.querySelectorAll('img[src$="' + srcSuffix + '"]'); for (var i = 0; i < obj.length; ++i)
{
obj[i].src = desUri;
}
}; /***************************************** * XMLHttpRequest實現 *****************************************/
var _XMLHttpRequest = function()
{ this.id = _XMLHttpRequest.globalId++; _XMLHttpRequest.cache[this.id] = this; this.status = 0; this.statusText = ''; this.readyState = 0; this.responseText = ''; this.headers = {}; this.onreadystatechange = undefined; ApiBridge.callNative('XMLHttpRequest', 'create',
{ "id" : this.id
});
} _XMLHttpRequest.globalId = 0; _XMLHttpRequest.cache = []; _XMLHttpRequest.setProperties = function(jsonObj)
{ var id = jsonObj.id; if (_XMLHttpRequest.cache[id])
{ var obj = _XMLHttpRequest.cache[id]; if (jsonObj.hasOwnProperty('status'))
{ obj.status = jsonObj.status;
} if (jsonObj.hasOwnProperty('statusText'))
{ obj.statusText = jsonObj.statusText;
} if (jsonObj.hasOwnProperty('readyState'))
{ obj.readyState = jsonObj.readyState;
} if (jsonObj.hasOwnProperty('responseText'))
{ obj.responseText = jsonObj.responseText;
} if (jsonObj.hasOwnProperty('headers'))
{ obj.headers = jsonObj.headers;
} if (_XMLHttpRequest.cache[id].onreadystatechange)
{ _XMLHttpRequest.cache[id].onreadystatechange();
}
}
} _XMLHttpRequest.prototype.open = function(method, url, async)
{ ApiBridge.callNative('XMLHttpRequest', 'open',
{ "id" : this.id, "method" : method, "url" : url, "scheme" : window.location.protocol, "host" : window.location.hostname, "port" : window.location.port, "href" : window.location.href, "referer" : document.referrer != "" ? document.referrer : undefined, "useragent" : navigator.userAgent, "cookie" : document.cookie != "" ? document.cookie : undefined, "async" : async, "timeout" : this.timeout
});
} _XMLHttpRequest.prototype.send = function(data)
{ if (data != null)
{ ApiBridge.callNative('XMLHttpRequest', 'send',
{ "id" : this.id, "data" : data
});
} else
{ ApiBridge.callNative('XMLHttpRequest', 'send',
{ "id" : this.id
});
}
} _XMLHttpRequest.prototype.overrideMimeType = function(mimetype)
{ ApiBridge.callNative('XMLHttpRequest', 'overrideMimeType',
{ "id" : this.id, "mimetype" : mimetype
});
} _XMLHttpRequest.prototype.abort = function()
{ ApiBridge.callNative('XMLHttpRequest', 'abort',
{ "id" : this.id
});
} _XMLHttpRequest.prototype.setRequestHeader = function(headerName, headerValue)
{ ApiBridge.callNative('XMLHttpRequest', 'setRequestHeader',
{ "id" : this.id, "headerName" : headerName, "headerValue" : headerValue
});
} _XMLHttpRequest.prototype.getAllResponseHeaders = function()
{ var strHeaders = ''; for ( var name in this.headers)
{
strHeaders += (name + ": " + this.headers[name] + "
");
} return strHeaders;
} _XMLHttpRequest.prototype.getResponseHeader = function(headerName)
{ var strHeaders; var upperCaseHeaderName = headerName.toUpperCase(); for ( var name in this.headers)
{ if (upperCaseHeaderName == name.toUpperCase())
strHeaders = this.headers[name]
} return strHeaders;
} _XMLHttpRequest.deleteObject = function(id)
{ if (_XMLHttpRequest.cache[id])
{ _XMLHttpRequest.cache[id] = undefined;
}
} global.ApiBridge = ApiBridge; global.kerkee = kerkee; global.XMLHttpRequest = _XMLHttpRequest; kerkee.register = function(_window)
{ _window.ApiBridge = window.ApiBridge; _window.kerkee = window.kerkee; _window.console.log = window.console.log; _window.XMLHttpRequest = window.XMLHttpRequest; _window.open = window.open;
}; ApiBridge.onBridgeInitComplete(function(aConfigs)
{ if (aConfigs)
{ if (aConfigs.hasOwnProperty('isOpenJSLog'))
{ _Configs.isOpenJSLog = aConfigs.isOpenJSLog;
} if (aConfigs.hasOwnProperty('isOpenNativeXHR'))
{ _Configs.isOpenNativeXHR = aConfigs.isOpenNativeXHR;
}
} if (_Configs.isOpenJSLog)
{ global.console.log = ApiBridge.log;
} ApiBridge.onNativeInitComplete(ApiBridge.onDeviceReady);
});
})(window);如果單純的只是 js 和 iOS 的交互,只需要關注 下面 的代碼即可:(當然你也可以自己設置多個 kerkeeJSManager 橋梁及 更多的 交互方法)
/***************************************** * JS和iOS 交互接口 *****************************************/kerkee.jsToOc = function(s1)
{ ApiBridge.callNative("kerkeeJSManager", "jsToOc",
{ "s1" : s1
});
}; kerkee.mutualJSOC = function(aString, callback)
{ ApiBridge.callNative("kerkeeJSManager", "mutualJSOC",
{ "aString" : aString
}, callback);
}global.kerkeeJSManager = kerkee;6、編譯運行:
7、demo下載地址:GitHub地址
三、總結:
這裡只是簡單的介紹了 iOS 使用 kerkee 框架來加載 html 實現 js 和 iOS 交互 ,如果只是單純的為了簡單的交互,可以看我的另外一篇博客 :JS和OC相互調用 ,這裡介紹了幾個更加輕量級的框架實現 js 和 iOS 交互。但是 js 和 iOS 交互 功能在kerkee 框架的一小部分,更多高性能、支持跨平台、擴展性好、易維護等等優秀的特性,我會慢慢閱讀源碼來說明。
該博客更好的閱讀體驗地址