簡單認(rèn)識:kerkee 是一個(gè)多主體共存型 Hybrid 框架洲胖,具有跨平臺、用戶體驗(yàn)好坯沪、性能高绿映、擴(kuò)展性好、靈活性強(qiáng)腐晾、易維護(hù)叉弦、規(guī)范化、集成云服務(wù)藻糖、具有Debug環(huán)境淹冰、徹底解決跨域問題。該框架從開發(fā)者角度支持三種團(tuán)隊(duì)開發(fā)模式:Web開發(fā)者 巨柒、Native開發(fā)者 樱拴、Web開發(fā)者和Native團(tuán)隊(duì)共同合作的開發(fā)團(tuán)隊(duì) 柠衍。
下面我將從 Web開發(fā)者和Native(iOS)團(tuán)隊(duì)共同合作的開發(fā)團(tuán)隊(duì) 模式來分析使用該框架。
一晶乔、相關(guān)資料:
官網(wǎng)地址:http://www.kerkee.com
Github 源碼地址:https://github.com/kercer
QQ交流群:110710084
二珍坊、簡單的使用:
[kerkee在iOS上的快速上手指南](http://blog.linzihong.com/kerkeezai-iosshang-de-kuai-su-sha ng-shou-zhi-nan/)
1、建立新項(xiàng)目 kerkeeHDDemo
在項(xiàng)目目錄中建立 Podfile
文件:
platform :ios, '7.0'
inhibit_all_warnings!
pod ‘kerkee’, ’~> 1.0.1’
2正罢、使用 CocoaPods 來導(dǎo)入 kerkee 框架阵漏,使用終端 cd 到你的 Podfile
文件所在的目錄:
cd $PodfilePath
pod install
3、運(yùn)行 Podfile
同目錄中的 kerkeeHDDemo.xcworkspace
即可翻具;
4履怯、在項(xiàng)目中添加 html 代碼:(本人對 html 不熟悉,相信懂的人肯定懂)
kerkeeTest.html :
<!--Create by Harry on 17/06/2016-->
<!-- Copyright ? 2016年 HarryDeng. All rights reserved.-->
<html>
<head>
<title>kerkee測試h5界面</title>
<meta http-equiv="Content-Type"content="text/html; charset=UTF-8">
<script>
function test()
{
alert("test alert...");
return "abcd";
}
</script>
</head>
<body>
<br/>
<br/>
<input type="button" value="js數(shù)據(jù)傳遞給oc" onclick="kerkeeJSManager.jsToOc('js數(shù)據(jù)傳遞給oc'); ">
<br/>
<input type="button" value="js數(shù)據(jù)傳遞給oc裆泳,oc回傳給js" onclick="kerkeeJSManager.mutualJSOC('js數(shù)據(jù)傳遞給oc叹洲,oc回傳給js', function(json)
{
console.log(JSON.stringify(json))
}); ">
<br/>
<button id="hallo" onclick="buttonClick1('js數(shù)據(jù)傳遞給oc,h5的方法單獨(dú)寫出buttonClick1')">
js數(shù)據(jù)傳遞給oc,h5的方法單獨(dú)寫出buttonClick1 </button>
<br/>
<button id="hallo" onclick="buttonClick2('js數(shù)據(jù)傳遞給oc,h5的方法單獨(dú)寫出buttonClick2')">
js數(shù)據(jù)傳遞給oc,h5的方法在js文件實(shí)現(xiàn) buttonClick2 </button>
<br/>
</body>
<script>
var kerkeeJSManager;
function buttonClick1(s1)
{
kerkeeJSManager.jsToOc(s1);
}
function ocToJs(s1)
{
console.log(JSON.stringify(s1))
}
</script>
<!--html頁面綁定js事件,一般放在href之后工禾,如果放在之前加載JS事件會阻塞界面-->
<script type="text/javascript" src="kerkeeTest.js"></script>
</html>
上面有三種方式來處理按鈕的點(diǎn)擊事件:
i. 直接在<button> </button> 中處理
ii. 使用 function 處理疹味;
iii. 使用 js 來處理 function。
代碼解釋:
代碼 | 理解分析 |
---|---|
kerkeeJSManager | js 這邊中 iOS 和 js 交互橋梁帜篇,在 iOS 也同樣存在 kerkeeJSManager 關(guān)鍵字 |
jsToOc() | kerkeeJSManager 方法 ,使用 kerkeeJSManager.jsToOc() 可以調(diào)用 iOS 對應(yīng)的 - (void)jsToOc:(KCWebView)aWebView argList:(KCArgList)args 方法诫咱,實(shí)現(xiàn) js 調(diào)用 iOS 原生代碼** |
ocToJs() | 在 iOS 中使用 [KCJSBridge callJSFunction:@"ocToJs" withJSONObject:@{@"hhh" : @"www"} WebView:m_webView]; 可以調(diào)用 js 的 ocToJs() 方法笙隙,實(shí)現(xiàn)了 iOS 調(diào)用 js 代碼 |
kerkeeTest.js :
var kerkeeJSManager;
function buttonClick2(s1)
{
kerkeeJSManager.jsToOc(s1);
}
5、在項(xiàng)目中添加 iOS 代碼:
HDJSToOCManager.h:
#import <Foundation/Foundation.h>
#import "KCJSObject.h"
#import "KCArgList.h"
// 這個(gè)類作為 和 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 <UIKit/UIKit.h>
#import "KCJSBridge.h"
@implementation HDJSToOCManager
- (NSString*)getJSObjectName{
// 這個(gè) 和 js 中的變量要保持一致
return @"kerkeeJSManager";
}
// js 中 可以調(diào)用 jsToOc() 來調(diào)用
- (void)jsToOc:(KCWebView*)aWebView argList:(KCArgList*)args{
NSLog(@"JS調(diào)用OC args : %@", args);
}
- (void)mutualJSOC:(KCWebView*)aWebView argList:(KCArgList*)args{
NSLog(@"JS調(diào)用OC坎缭,OC回調(diào)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);
//回調(diào)竟痰,callbackId,kerkee.js 內(nèi)部已經(jīng)處理好
[KCJSBridge callbackJS:aWebView callBackID:[args getObject:@"callbackId"] jsonString:json];
}
@end
這個(gè)類主要是生成和 js 對應(yīng)的變量 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 方法 (這個(gè)時(shí)候需要在項(xiàng)目中添加“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 在初始化的時(shí)候找到 kerkee.js 并且使用 - (nullable NSString )stringByEvaluatingJavaScriptFromString:(NSString )script; 在 iOS 的 UIWebView 注入 kerkee.js 代碼。這份 js 才是真正的**將 iOS 的橋梁 HDJSToOCManager 實(shí)體和 js變量 kerkeeJSManager 的關(guān)鍵代碼拾给。在 這里 可以看到作者在demo中的 kerkee.js 源碼祥得。因?yàn)槲覍?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實(shí)現(xiàn)
*****************************************/
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] + "\r\n");
}
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 的交互额衙,只需要關(guān)注 下面 的代碼即可:(當(dāng)然你也可以自己設(shè)置多個(gè) 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饮焦、編譯運(yùn)行:
7怕吴、demo下載地址:GitHub地址
三、總結(jié):
這里只是簡單的介紹了 iOS 使用 kerkee 框架來加載 html 實(shí)現(xiàn) js 和 iOS 交互 县踢,如果只是單純的為了簡單的交互转绷,可以看我的另外一篇博客 :JS和OC相互調(diào)用 ,這里介紹了幾個(gè)更加輕量級的框架實(shí)現(xiàn) js 和 iOS 交互殿雪。但是 js 和 iOS 交互 功能在 kerkee 框架的一小部分暇咆,更多高性能、支持跨平臺丙曙、擴(kuò)展性好爸业、易維護(hù)等等優(yōu)秀的特性,我會慢慢閱讀源碼來說明亏镰。