HBuilder 第三方插件開發

本人最近開發了 HBulider 集成極光推送(JPush)的插件,鑑於 HBuilder 官網上缺乏 iOS 的示例 ,並且官網也只給出了 JavaScript 調用 native 代碼的接口,對於 native 調用 JavaScript 而且向 JavaScript 發送 event 事件的方法卻在 native層 進行了封裝。筆者在踩過了一些小坑以後,終於成功的開發了插件,而且 實現了 JavaScript 和 native 的雙向溝通 。特此跟你們分享一下在 HBuilder 插件開發過程當中的經驗和關鍵代碼。javascript

JPush 實例展現


首先附上完整 demo [JPush HBuilder Demo] 併爲你們展現一下:html

實例及功能展現

以上即爲根據本文內容開發出的實例java

如您需使用極光推送產品請至此 [極光推送官方網站]git

新插件配置


配置 manifest.json

首先用源碼的方式打開工程 /Pandora/ 目錄下的 manifest.json ,在 "permissions" 中添加新的插件名稱:github

"permissions": {
    "Push":{
        "description": "極光推送插件"
    }
},複製代碼
配置 feature.plist

在 Xcode 中打開 /PandoraApi.bundle/ 目錄下的 feature.plist ,爲插件添加新的 item:web

feature.plist

其中須要注意的是:objective-c

  • 最頂部的 key 值 Push ,必須跟 manifest.json 中配置的插件名一致
  • class 的值須要跟 native 代碼中的類名一致,此處爲 JPushPlugin
  • 由於本插件拓展自 HBuilder 已經封裝好的 PGPush ,故 baseclass 爲父類

經過以上配置,就能夠在 JavaScript 中經過 Push --> JPushPlugin 的對應關係,調用 native 代碼了。json

JavaScript 調用本地代碼的實現


這部分在 [HBuilder 官網插件開發指導] 中已經給出了較詳細的說明,這裏就再也不贅述,附上關鍵代碼:數組

document.addEventListener("plusready", function() {
    var _BARCODE = 'Push';    // 插件名稱
    var B = window.plus.bridge;

    var JPushPlugin = {

        callNative : function(fname, args, successCallback) {
            var callbackId = this.getCallbackId(successCallback, this.errorCallback);
            if (args != null) {
                args.unshift(callbackId);
            } else {
                var args = [callbackId];
            }
            return B.exec(_BARCODE, fname, args);
        },

        getCallbackId : function(successCallback) {
            var success = typeof successCallback !== 'function' ? null : function(args) {
                successCallback(args);
            };
            callbackId = B.callbackId(success, this.errorCallback);
            return callbackId;
        },

        errorCallback : function(errorMsg) {
            console.log("Javascript callback error: " + errorMsg);
        },

        jsHello : function(args){
            this.callNative("nativeHello", args, null);
        },

    window.plus.Push = JPushPlugin;

}, true);複製代碼

其中 callNative 爲封裝好用於調用 native 代碼的方法,參數以下:app

  • fname:要調用的 native 的方法名
  • args:傳給 native 的參數,必須是數組
  • successCallback:成功回調,null 爲沒有

以上代碼最後面的 "jsHello" 方法,即爲封裝好的 js 方法,在工程的其餘文件裏經過

window.plus.Push.jsHello(args);複製代碼

的方式便可調用本地的 "nativeHello" 方法。

Objective-C 調用 js 的實現


與 Phonegap 的差別

在 HBuilder 官方文檔中並無說起 OC 調用 js 的方法,從 OC 中的類名(PGPlugin 等)能夠看出,其應該是對 Phonegap 的封裝,可是卻並無提供 Phonegap 中直接調用 js 的接口,例如:

NSString *evalString = [NSString stringWithFormat:@"jsFunction(%@)",args];
[self.commandDelegate evalJs:evalString];複製代碼

也沒法向 js 發送 event ,例如:

NSString *evalString = [NSString stringWithFormat:@"cordova.fireDocumentEvent('event_name',%@)",args];
[self.commandDelegate evalJs:evalString];複製代碼

其中 self 爲繼承自 CDVPlugin 的插件類實例。

通過筆者的查找,發如今 HBuilder 提供的 PDRCoreAppFrame(:PDRNView :UIView) 類中,有以下方法能夠調用 js 代碼:

/** @brief 在當前頁面同步執行Javascript @param js javasrcipt 腳本 @return NSString* 執行結果 */
- (NSString*)stringByEvaluatingJavaScriptFromString:(NSString*)js;複製代碼

獲取 PDRCoreAppFrame 對象

其中 PDRCoreAppFrame 爲控制 webView 的實例,數量可能爲多個,且在視圖層級中的位置不肯定,故須要經過遍歷 app 中全部 view ,來找出 PDRCoreAppFrame ,如下是經過 遞歸 找出全部 PDRCoreAppFrame 的方法:

-(NSMutableArray*)searchViews:(NSArray*)views{
    NSMutableArray *frames = [NSMutableArray array];
    for (UIView *temp in views) {
        if ([temp isMemberOfClass:[PDRCoreAppFrame class]]) {
            [frames addObject:temp];
        }
        if ([temp subviews]) {
            NSMutableArray *tempArray = [self searchViews:[temp subviews]];
            for (UIView *tempView in tempArray) {
                if ([tempView isMemberOfClass:[PDRCoreAppFrame class]]) {
                    [frames addObject:tempView];
                }
            }
        }
    }
    return frames;
}複製代碼

其中:

  • 參數 views 爲同一層級中的 views
  • 返回值 frames 爲從該層級中找到的 PDRCoreAppFrame

調用 js

這樣咱們就能夠用上述方法獲取到全部的 PDRCoreAppFrame 進而調用 js 代碼了:

-(void)evaluatingJavaScriptFromString:(NSString*)string{
    UIWindow *window = [[UIApplication sharedApplication] keyWindow];
    NSArray *views = [[[window rootViewController] view] subviews];
    //調用上述方法
    NSArray *frames = [self searchViews:views];
    for (PDRCoreAppFrame *appFrame in frames) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [appFrame stringByEvaluatingJavaScriptFromString:string];
        });
    }
}複製代碼

調用示例:

NSString *evalString = @"alert("make a js call");";
[self evaluatingJavaScriptFromString:evalString];複製代碼

可是並不建議用這種方式,由於該方法會強制向每一個 webView 的頁面都發送一條執行語句,有時會出現並不但願的結果。所以,建議使用下面發送 event 的方式,並在 js 中接收後進行處理。

向 js 發送 event

筆者對上述方法再次進行了封裝:

-(void)fireEvent:(NSString*)event args:(id)args{
    NSString *evalString = nil;
    NSError  *error      = nil;
    NSString *argsString = nil;

    if (args) {
        if ([args isKindOfClass:[NSString class]]) {
            argsString = args;
        }else{
            NSData   *jsonData   = [NSJSONSerialization dataWithJSONObject:args options:0 error:&error];
            argsString = [[NSString alloc]initWithData:jsonData encoding:NSUTF8StringEncoding];
            if (error) {
                NSLog(@"%@",error);
            }
        }
        evalString = [NSString stringWithFormat:@"\ var jpushEvent = document.createEvent('HTMLEvents');\ jpushEvent.initEvent('%@', true, true);\ jpushEvent.eventType = 'message';\ jpushEvent.arguments = '%@';\ document.dispatchEvent(jpushEvent);",event,argsString];
    }else{
        evalString = [NSString stringWithFormat:@"\ var jpushEvent = document.createEvent('HTMLEvents');\ jpushEvent.initEvent('%@', true, true);\ jpushEvent.eventType = 'message';\ document.dispatchEvent(jpushEvent);",event];
    }
    //調用上述方法
    [self evaluatingJavaScriptFromString:evalString];
}複製代碼

其中對傳入的 args 進行了簡單的處理。

最後咱們經過調用一行代碼便可作到向 js 發送 event :

[self fireEvent:@"event_name" args:args];複製代碼

js 接收 event 並處理

在上一步中發送了 "event_name" 的事件以後,能夠在 html 的 script 中經過如下方式捕獲:

document.addEventListener("event_name", onEventFunc, false);
function onEventFunc(args){
    var obj = JSON.parese(args);
    window.setTimeout(function(){
        alert(obj);
    },0);
}複製代碼

至此,就完全實現了 Objective-C 向 js 的溝通

* 如您對本文有任何疑問或建議,歡迎交流

相關文章
相關標籤/搜索