App開發Native.js入門指南

概述

Native.js技術,簡稱NJS,是一種將手機操做系統的原生對象轉義,映射爲JS對象,在JS裏編寫原生代碼的技術。
若是說Node.js把js擴展到服務器世界,那麼Native.js則把js擴展到手機App的原生世界。
HTML/JS/Css所有語法只有7萬多,而原生語法有幾十萬,Native.js大幅提高了HTML5的能力。
NJS突破了瀏覽器的功能限制,也再也不須要像Hybrid那樣由原生語言開發插件才能補足瀏覽器欠缺的功能。
NJS編寫的代碼,最終須要在HBuilder裏打包發行爲App安裝包,或者在支持Native.js技術的瀏覽器裏運行。目前Native.js技術不能在普通手機瀏覽器裏直接運行。javascript

  • NJS大幅擴展了HTML5的能力範圍,本來只有原生或Hybrid App的原生插件才能實現的功能現在可使用JS實現。
  • NJS大幅提高了App開發效率,將iOS、Android、Web的3個工程師組隊才能完成的App,變爲1個web工程師就搞定。
  • NJS再也不須要配置原生開發和編譯環境,調試、打包均在HBuilder裏進行。沒有mac和xcode同樣能夠開發iOS應用。
  • 若是不熟悉原生API也不要緊,咱們彙總了不少NJS的代碼示例,複製粘貼就能夠用。http://ask.dcloud.net.cn/article/114

再次強調,Native.js不是一個js庫,不須要下載引入到頁面的script中,也不像nodejs那樣有單獨的運行環境,Native.js的運行環境是集成在5+runtime裏的,使用HBuilder打包的app或流應用均可以直接使用Native.js。html

技術要求

因爲NJS是直接調用Native API,須要對Native API有必定了解,知道所須要的功能調用了哪些原生API,能看懂原生代碼並參考原生代碼修改成JS代碼。
不然只能直接copy別人寫好的NJS代碼。html5

開始使用

判斷平臺

Native API具備平臺依賴性,因此須要經過如下方式判斷當前的運行平臺:java

function judgePlatform(){ switch ( plus.os.name ) { case "Android": // Android平臺: plus.android.* break; case "iOS": // iOS平臺: plus.ios.* break; default: // 其它平臺 break; } } 

類型轉換

在NJS中調用Native API或從Native API返回數據到NJS時會自動轉換數據類型。node

類型轉換表

類型 Objective-C Java JavaScript
基本數據 byte/short/int/long/float/double/... byte/short/int/long/float/double/... Number
字符 char char String
字符串 NSString/@"" String/"" String
數組 @[1,2,3]/NSArray new XXX[] InstanceObject
@interface class ClassObject
對象(實例) * * InstanceObject
空對象 nil null null
其它 Protocol Interface Object(JSON)

其餘轉換

  • Android原生應用的主Activity對象 轉爲plus.android.runtimeMainActivity()
    Android的主Activity對象是啓動應用時自動建立的,不是代碼建立,此時經過plus.android.runtimeMainActivity()方法獲取該Activity對象
  • Objective-C方法冒號剔除
    [pos setPositionX:(int)x Y:(int)y;] 轉爲 pos.setPositionXY(x,y);
    OC語法中方法的定義格式爲:
    「(返回值類型) 函數名: (參數1類型) 形參1 參數2名稱: (參數2類型) 形參2」
    方法的完整名稱爲: 「函數名:參數2名稱:」。
    如:「(void)setPositionX:(int)x Y:(int)y;」,方法的完整名稱爲「setPositionX:Y:」,調用時語法爲:「[pos setPositionX:x Y:y];」。
    在JS語法中函數名稱不能包含「:」字符,因此OC對象的方法名映射成NJS對象方法名時將其中的「:」字符自動刪除,上面方法名映射爲「setPositionXY」,在NJS調用的語法爲:「pos.setPositionXY(x,y);」。
  • 文件路徑轉換
    Web開發裏使用的image/1.png是該web工程的相對路徑,而原生API中常常須要使用絕對路徑,好比/sdcard/apptest/image/1.png,此時使用這個擴展方法來完成轉換:plus.io.convertLocalFileSystemURL("image/1.png")

概念

類對象

因爲JavaScript中自己沒有類的概念,爲了使用Native API層的類,在NJS中引入了類對象(ClassObject)的概念,用於對Native中的類進行操做,如建立類的實例對象、訪問類的靜態屬性、調用類的靜態方法等。其原型以下:android

Interface ClassObject { function Object plusGetAttribute( String name ); function void plusSetAttribute( String name, Object value ); } 

獲取類對象
在iOS平臺咱們能夠經過plus.ios.importClass(name)方法導入類對象,參數name爲類的名稱;在Android平臺咱們能夠經過plus.android.importClass(name)方法導入類對象,其參數name爲類的名稱,必須包含完整的命名空間。ios

示例:web

// iOS平臺導入NSNotificationCenter類 var NSNotificationCenter = plus.ios.importClass("NSNotificationCenter");  // Android平臺導入Intent類 var Intent = plus.android.importClass("android.content.Intent"); 

獲取類對象後,能夠經過類對象「.」操做符獲取類的靜態常量屬性、調用類的靜態方法,類的靜態很是量屬性需經過plusGetAttribute、plusSetAttribute方法操做。objective-c

實例對象

在JavaScript中,全部對象都是Object,爲了操做Native層類的實例對象,在NJS中引入了實例對象(InstanceObject)的概念,用於對Native中的對象進行操做,如操做對象的屬性、調用對象的方法等。其原型以下:chrome

Interface InstanceObject { function Object plusGetAttribute( String name ); function void plusSetAttribute( String name, Object value ); } 

獲取實例對象
有兩種方式獲取類的實例對象,一種是調用Native API返回值獲取,另外一種是經過new操做符來建立導入的類對象的實例,以下:

// iOS平臺導入NSDictionary類 var NSDictionary = plus.ios.importClass("NSDictionary"); // 建立NSDictionary的實例對象 var ns = new NSDictionary();  // Android平臺導入Intent類 var Intent = plus.android.importClass("android.content.Intent"); // 建立Intent的實例對象 var intent = new Intent(); 

獲取實例對象後,能夠經過實例對象「.」操做符獲取對象的常量屬性、調用對象的成員方法,實例對象的很是量屬性則需經過plusGetAttribute、plusSetAttribute方法操做。

操做對象的屬性方法

  • 常量屬性
    獲取對象後就能夠經過「.」操做符獲取對象的常量屬性,若是是類對象則獲取的是類的靜態常量屬性,若是是實例對象則獲取的是對象的成員常量屬性。

  • 很是量屬性
    若是Native層對象的屬性值在原生環境下被更改,此時使用「.」操做符獲取到對應NJS對象的屬性值就可能不是實時的屬性值,而是該Native層對象被映射爲NJS對象那一刻的屬性值。
    爲獲取獲取Native層對象的實時屬性值,需調用NJS對象的plusGetAttribute(name)方法,參數name爲屬性的名稱,返回值爲屬性的值。調用NJS對象的plusSetAttribute(name,value)方法設置Native層對象的很是量屬性值,參數name爲屬性的名稱,value爲要設置新的屬性值。
    注意:使用plusGetAttribute(name)方法也能夠獲取Native層對象的常量屬性值,但不如直接使用「.」操做符來獲取性能高。

  • 方法
    獲取對象後能夠經過「.」操做符直接調用Native層方法,若是是類對象調用的是Native層類的靜態方法,若是是實例對象調用的是Native層對象的成員方法。
    注意:在iOS平臺因爲JS語法的緣由,Objective-C方法名稱中的「:」字符轉成NJS對象的方法名稱後將會被忽略,所以在NJS中調用的方法名需去掉全部「:」字符。

  • 類的繼承
    Objective-C和Java中類若是存在繼承自基類,在NJS中對應的對象會根據繼承關係遞歸將全部基類的公有方法一一換成NJS對象的方法,全部基類的公有屬性也能夠經過其plusGetAttribute、plusSetAttribute方法訪問。

開始寫NJS

使用NJS調用Native API很是簡單,基本步驟以下:
1. 導入要使用到的類;
2. 建立類的實例對象(或者調用類的靜態方法建立);
3. 調用實例對象的方法;

如下例子使用NJS調用iOS和Android的原生彈出提示框(相似但不一樣於js的alert)。

Android

如下代碼在Android平臺展現調用Native API顯示系統提示框。
首先是Android原生 Java代碼,用於比對參考:

import android.app.AlertDialog; //... // 建立提示框構造對象,Builder是AlertDialog的內部類。參數this指代Android的主Activity對象,該對象啓動應用時自動生成 AlertDialog.Builder dlg = new AlertDialog.Builder(this); // 設置提示框標題 dlg.setTitle("自定義標題"); // 設置提示框內容 dlg.setMessage("使用NJS的原生彈出框,可自定義彈出框的標題、按鈕"); // 設置提示框按鈕 dlg.setPositiveButton("肯定(或者其餘字符)", null); // 顯示提示框 dlg.show(); //... 

Native.js代碼:

/** * 在Android平臺經過NJS顯示系統提示框 */ function njsAlertForAndroid(){ // 導入AlertDialog類 var AlertDialog = plus.android.importClass("android.app.AlertDialog"); // 建立提示框構造對象,構造函數須要提供程序全局環境對象,經過plus.android.runtimeMainActivity()方法獲取 var dlg = new AlertDialog.Builder(plus.android.runtimeMainActivity()); // 設置提示框標題 dlg.setTitle("自定義標題"); // 設置提示框內容 dlg.setMessage("使用NJS的原生彈出框,可自定義彈出框的標題、按鈕"); // 設置提示框按鈕 dlg.setPositiveButton("肯定(或者其餘字符)",null); // 顯示提示框 dlg.show(); } //... 

注意:NJS代碼中建立提示框構造對象要求傳入程序全局環境對象,可經過plus.android.runtimeMainActivity()方法獲取應用的主Activity對象,它是HTML5+應用運行期自動建立的程序全局環境對象。

Android設備上運行效果圖:
Android Native.js示例運行效果圖
`注意:其實HTML5+規範已經封裝過原生提示框消息API:plus.ui.alert( message, alertCB, title, buttonCapture)。此處NJS的示例僅爲了開發者方便理解,實際使用時調用plus.ui.alert更簡單,性能也更高。**

iOS

如下代碼在iOS平臺展現調用Native API顯示系統提示對話框。
iOS原生Objective-C代碼,用於比對參考:

#import <UIKit/UIKit.h> //... // 建立UIAlertView類的實例對象 UIAlertView *view = [UIAlertView alloc]; // 設置提示對話上的內容 [view initWithTitle:@"自定義標題" // 提示框標題 message:@"使用NJS的原生彈出框,可自定義彈出框的標題、按鈕" // 提示框上顯示的內容 delegate:nil // 點擊提示框後的通知代理對象,nil相似js的null,意爲不設置 cancelButtonTitle:@"肯定(或者其餘字符)" // 提示框上取消按鈕的文字 otherButtonTitles:nil]; // 提示框上其它按鈕的文字,設置爲nil表示不顯示 // 調用show方法顯示提示對話框,在OC中使用[]語法調用對象的方法 [view show]; //... 

Native.js代碼:

/** * 在iOS平臺經過NJS顯示系統提示框 */ function njsAlertForiOS(){ // 導入UIAlertView類 var UIAlertView = plus.ios.importClass("UIAlertView"); // 建立UIAlertView類的實例對象 var view = new UIAlertView(); // 設置提示對話上的內容 view.initWithTitlemessagedelegatecancelButtonTitleotherButtonTitles("自定義標題" // 提示框標題 , "使用NJS的原生彈出框,可自定義彈出框的標題、按鈕" // 提示框上顯示的內容 , null // 操做提示框後的通知代理對象,暫不設置 , "肯定(或者其餘字符)" // 提示框上取消按鈕的文字 , null ); // 提示框上其它按鈕的文字,設置爲null表示不顯示 // 調用show方法顯示提示對話框,在JS中使用()語法調用對象的方法 view.show(); } //... 

注意:在OC語法中方法的定義格式爲:
「(返回值類型) 函數名: (參數1類型) 形參1 參數2名稱: (參數2類型) 形參2」
方法的完整名稱爲: 「函數名:參數2名稱:」。
如:「(void)setPositionX:(int)x Y:(int)y;」,方法的完整名稱爲「setPositionX:Y:」
調用時語法爲:「[pos setPositionX:x Y:y];」。
在JS語法中函數名稱不能包含「:」字符,因此OC對象的方法名映射成NJS對象方法名時將其中的「:」字符自動刪除,上面方法名映射爲「setPositionXY」,在NJS調用的語法爲:「pos.setPositionXY(x,y);」。

iOS設備上運行效果圖:
iOS Native.js示例運行效果圖
`注意:其實HTML5+規範已經封裝過原生提示框消息API:plus.ui.alert( message, alertCB, title, buttonCapture)。此處NJS的示例僅爲了開發者方便理解,實際使用時調用plus.ui.alert更簡單、性能也更高。

在HBuilder自帶的Hello H5+模板應用中「Native.JS」(plus/njs.html)頁面有完整的源代碼,可真機運行查看效果。

經常使用API

API on Android

爲了能更好的理解NJS調用Java Native API,咱們在Android平臺用Java實現如下測試類,將在會後面API說明中的示例來調用。
文件NjsHello.java代碼以下:

package io.dcloud;  // 定義類NjsHello public class NjsHello { // 靜態常量 public static final int CTYPE = 1; // 靜態變量 public static int count; // 成員常量 public final String BIRTHDAY = "2013-01-13"; // 成員變量 String name; //定義屬性name NjsHelloEvent observer; public void updateName( String newname ) { //定義方法updateName name = newname; } public void setEventObserver( NjsHelloEvent newobserver ) { observer = newobserver; } public void test() { //定義方法test System.out.printf( "My name is: %s", name ); observer.onEventInvoked( name ); } public static void testCount() { System.out.printf( "Static count is:%d", count ); } static{ // 初始化類的靜態變量 NjsHello.count = 0; } } 

文件NjsHelloEvent.java代碼以下:

package io.dcloud;  // 定義接口NjsHelloEvent public interface NjsHelloEvent { public void onEventInvoked( String name ); } 

注:此NjsHello示例僅爲了說明原生代碼與NJS代碼之間的映射關係,如下示例代碼沒法直接在HBuilder裏真機運行,必須在之後HBuilder開放自定義打包後方可把NjsHello類打入app並被NJS調用。實際使用中,這種須要並不是必要,大多數狀況能夠經過直接寫NJS代碼調用操做系統API,而無需由原生語言二次封裝類供JS調用。

plus.android.importClass

導入Java類對象,方法原型以下:

ClassObject plus.android.importClass( String classname ); 

導入類對象後,就能夠經過「.」操做符直接調用對象(類對象/實例對象)的常量和方法。
classname:要導入的Java類名,必須是完整的命名空間(使用"."分割),若是指定的類名不存在,則導入類失敗,返回null。

注意:導入類對象能夠方便的使用「.」操做符來調用對象的屬性和方法,但也會消耗較多的系統資源。所以導入過多的類對象會影響性能,此時可使用「高級API」中提供的方法在不導入類對象的狀況下調用Native API。

示例:
1. 導入類對象
Java代碼:

import io.dcloud.NjsHello; //... public class Test { public static void main( String args[] ) { // 建立對象的實例 NjsHello hello = new NjsHello(); //... } //... } 

NJS代碼:

// 導入測試類NjsHello var NjsHello = plus.android.importClass("io.dcloud.NjsHello"); // 建立NjsHello的實例對象 var hello = new NjsHello(); // ... 

ClassObject

調用plus.android.importClass()方法導入類並返回ClassObject類對象,經過該類對象,能夠建立類的實例對象。在Java中類的靜態方法會轉換成NJS類對象的方法,可經過類對象的「.」操做符調用;類的靜態常量會轉換爲NJS類對象的屬性,可經過類對象的「.」操做符訪問;類的靜態屬性則需經過NJS類對象的plusGetAttribute、plusSetAttribute方法操做。
示例:
1. 導入類後獲取類的靜態常量屬性
Java代碼:

import io.dcloud.NjsHello; //... public class Test { public static void main( String args[] ) { // 獲取類的靜態常量屬性 int type = NjsHello.CTYPE; System.out.printf( "NjsHello Final's value: %d", type ); // 輸出「NjsHello Final's value: 1」 //... } //... } 

NJS代碼:

// 導入測試類NjsHello var NjsHello = plus.android.importClass("io.dcloud.NjsHello"); // 獲取類的靜態常量屬性 var type = NjsHello.CTYPE; console.log( "NjsHello Final's value: "+type ); // 輸出「NjsHello Final's value: 1」 // ... 

 

  1. 導入類後調用類的靜態方法
    Java代碼:
import io.dcloud.NjsHello; //... public class Test { public static void main( String args[] ) { // 調用類的靜態方法 NjsHello.testCount(); //... } //... } 

NJS代碼:

// 導入測試類NjsHello var NjsHello = plus.android.importClass("io.dcloud.NjsHello"); // 調用類的靜態方法 NjsHello.testCount(); // ... 
ClassObject.plusGetAttribute

獲取類對象的靜態屬性值,方法原型以下:

Object classobject.plusGetAttribute( String name ); 

導入類對象後,就能夠調用其plusGetAttribute方法獲取類的靜態屬性值。
- name:要獲取的靜態屬性名稱,若是指定的屬性名稱不存在,則獲取屬性失敗,返回null。

注意:若是導入的類對象中存在「plusGetAttribute」同名的靜態方法,則必須經過plus.android.invoke()方法調用。

示例:
1. 導入類後獲取類的靜態屬性值
Java代碼:

import io.dcloud.NjsHello; //... public class Test { public static void main( String args[] ) { // 獲取類的靜態屬性 int count = NjsHello.count; System.out.printf( "NjsHello Static's value: %d", count ); // 輸出「NjsHello Static's value: 0」 //... } //... } 

NJS代碼:

// 導入測試類NjsHello var NjsHello = plus.android.importClass("io.dcloud.NjsHello"); // 獲取類的靜態屬性 var count = NjsHello.plusGetAttribute( "count" ); console.log( "NjsHello Static's value: "+count ); // 輸出「NjsHello Static's value: 0」 // ... 
ClassObject.plusSetAttribute

設置類對象的靜態屬性值,方法原型以下:

void classobject.plusSetAttribute( String name, Object value ); 

導入類對象後,就能夠調用其plusSetAttribute方法設置類的靜態屬性值。
- name:要設置的靜態屬性名稱,若是指定的屬性名稱不存在,則設置屬性失敗,返回null。
- value:要設置的屬性值,其類型必須與Native層類對象的靜態屬性區配,不然設置操做不生效,將保留之前的值。

注意:若是導入的類對象中存在「plusSetAttribute」同名的靜態方法,則必須經過plus.android.invoke()方法調用。

示例:
1. 導入類後設置類的靜態屬性值
Java代碼:

import io.dcloud.NjsHello; //... public class Test { public static void main( String args[] ) { // 設置類的靜態屬性值 NjsHello.count = 2; System.out.printf( "NjsHello Static's value: %d", NjsHello.count ); // 輸出「NjsHello Static's value: 2」 //... } //... } 

NJS代碼:

// 導入測試類NjsHello var NjsHello = plus.android.importClass("io.dcloud.NjsHello"); // 設置類的靜態屬性值 NjsHello.plusSetAttribute( "count", 2 ); console.log( "NjsHello Static's value: "+NjsHello.plusGetAttribute( "count" ) ); // 輸出「NjsHello Static's value: 2」 // ... 

InstanceObject

NJS中實例對象與Java中的對象對應,調用plus.android.importClass()方法導入類後,經過new操做符可建立該類的實例對象,或直接調用plus.android.newObject方法建立類的實例對象,也可經過調用Native API返回實例對象。在Java中對象的方法會轉換成NJS實例對象的方法,可經過實例對象的「.」操做符調用;對象的常量屬性會轉換NJS實例對象的屬性,可經過實例對象的「.」操做符訪問。對象的很是量屬性則必須經過NJS實例對象的plusGetAttribute、plusSetAttribute方法操做。
示例:
1. 導入類建立實例對象,調用對象的方法
Java代碼:

import io.dcloud.NjsHello; //... public class Test { public static void main( String args[] ) { // 建立NjsHello的實例對象 NjsHello hello = new NjsHello(); // 調用對象的方法 hello.updateName( "Tester" ); //... } //... } 

NJS代碼:

// 導入測試類NjsHello var NjsHello = plus.android.importClass("io.dcloud.NjsHello"); // 建立NjsHello的實例對象 var hello = new NjsHello(); // 調用對象的方法 hello.updateName( "Tester" ); // ... 

 

  1. 導入類建立實例對象,獲取對象的常量屬性
    Java代碼:
import io.dcloud.NjsHello; //... public class Test { public static void main( String args[] ) { // 建立NjsHello的實例對象 NjsHello hello = new NjsHello(); // 訪問對象的常量屬性 String birthday = hello.BIRTHDAY; System.out.printf( "NjsHello Object Final's value: %s", birthday ); // 輸出「NjsHello Object Final's value: 2013-01-13」 //... } //... } 

NJS代碼:

// 導入測試類NjsHello var NjsHello = plus.android.importClass("io.dcloud.NjsHello"); // 建立NjsHello的實例對象 var hello = new NjsHello(); // 訪問對象的常量屬性 var birthday = hello.BIRTHDAY; console.log( "NjsHello Object Final's value: "+birthday ); // 輸出「NjsHello Object Final's value: 2013-01-13」 // ... 
InstanceObject.plusGetAttribute

獲取實例對象的屬性值,方法原型以下:

Object instancebject.plusGetAttribute( String name ); 

獲取實例對象後,就能夠調用其plusGetAttribute方法獲取對象的屬性值。
name:要獲取對象的屬性名稱,若是指定的屬性名稱不存在,則獲取屬性失敗,返回null。

注意:若是實例對象中存在「plusGetAttribute」同名的方法,則必須經過plus.android.invoke()方法調用。

示例:
1. 導入類建立實例對象,獲取對象的屬性值
Java代碼:

import io.dcloud.NjsHello; //... public class Test { public static void main( String args[] ) { // 建立對象的實例 NjsHello hello = new NjsHello(); hello.updateName( "Tester" ); // 獲取其name屬性值 String name = hello.name; System.out.printf( "NjsHello Object's name: %s", name ); // 輸出「NjsHello Object's name: Tester」 //... } //... } 

NJS代碼:

// 導入測試類NjsHello var NjsHello = plus.android.importClass("io.dcloud.NjsHello"); // 建立對象的實例 var hello = new NjsHello(); hello.updateName( "Tester" ); // 獲取其name屬性值 var name = hello.plusGetAttribute( "name" ); console.log( "NjsHello Object's name: "+name ); // 輸出「NjsHello Object's name: Tester」 // ... 
InstanceObject.plusSetAttribute

設置類對象的靜態屬性值,方法原型以下:

void instanceobject.plusSetAttribute( String name, Object value ); 

導入類對象後,就能夠調用其plusSetAttribute方法設置類的靜態屬性值。
- name:要設置的靜態屬性名稱,若是指定的屬性名稱不存在,則設置屬性失敗,返回null。
- value:要設置的屬性值,其類型必須與Native層類對象的靜態屬性區配,不然設置操做不生效,將保留之前的值。

注意:若是導入的類對象中存在「plusSetAttribute」同名的靜態方法,則必須經過plus.android.invoke()方法調用。

示例:
1. 導入類建立實例對象,設置對象的屬性值
Java代碼:

import io.dcloud.NjsHello; //... public class Test { public static void main( String args[] ) { // 建立對象的實例 NjsHello hello = new NjsHello(); // 設置其name屬性值 hello.name = "Tester"; System.out.printf( "NjsHello Object's name: %s", hello.name ); // 輸出「NjsHello Object's name: Tester」 //... } //... } 

NJS代碼:

// 導入測試類NjsHello var Hello = plus.android.importClass("NjsHello"); // 建立對象的實例 var hello = new NjsHello(); // 設置其name屬性值 hello.plusSetAttribute( "name", "Tester" ); console.log( "NjsHello Object's name: "+hello.plusGetAttribute("name") ); // 輸出「NjsHello Object's name: Tester」 // ... 

plus.android.implements

在Java中能夠經過定義新類並實現Interface的接口,並建立出新類對象做爲其它接口的參數,在NJS中則可快速建立對應的Interface對象,方法原型以下:
Object plus.android.implements( String name, Object obj );

此方法建立Native層中Java的接口實現對象,做爲調用其它Native API的參數。
- name:接口的名稱,必須是完整的命名空間(使用"."分割),若是不存在此接口,則建立接口實現對象失敗,返回null。
- obj:JSON對象類型,接口實現方法的定義,JSON對象中key值爲接口方法的名稱;value值爲Function,方法參數必須與接口中方法定義的參數區配。

示例:
1. Test類中實現接口NjsHelloEvent的方法,並調用NjsHello對象的test方法觸發接口中函數的運行。
Java代碼:

import io.dcloud.NjsHello; import io.dcloud.NjsHelloEvent; //... // Test類實現NjsHelloEvent接口 public class Test implements NjsHelloEvent { public static void main( String args[] ) { // 建立對象的實例 NjsHello hello = new NjsHello(); // 調用updateName方法 hello.updateName( "Tester" ); // 設置監聽對象 hello.setEventObserver( this ); // 調用test方法,觸發接口事件 hello.test(); // 觸發onEventInvoked函數運行 //... } // 實現接口NjsHelloEvent的onEventInvoked方法 @Override public void onEventInvoked( String name ) { System.out.printf( "Invoked Object's name is: %s", name ); } //... } 

NJS代碼:

// 導入測試類NjsHello var NjsHello = plus.android.importClass("io.dcloud.NjsHello"); // 實現接口「NjsHelloEvent」對象 var hevent = plus.android.implements( "io.dcloud.NjsHelloEvent", { "onEventInvoked":function( name ){ console.log( "Invoked Object’s name: "+name ); // 輸出「Invoked Object’s name: Tester」 } } ); // 建立對象的實例 var hello = new NjsHello(); // 調用updateName方法 hello.updateName( "Tester" ); // 設置監聽對象 hello.setEventObserver( hevent ); // 調用test方法,觸發代理事件 hello.test(); // 觸發上面定義的匿名函數運行 // ... 

plus.android.runtimeMainActivity

獲取運行期環境主Activity實例對象,方法原型以下:

InstanceObject plus.android.runtimeMainActivity(); 

此方法將獲取程序的主Activity的實例對象,它是Html5+運行期環境主組件,用於處理與用戶交互的各類事件,也是應用程序全局環境android.app.Activity的實現對象。android.app.Activity是一個特殊的類,須要在原生開發環境中註冊後才能使用,因此使用new操做符建立對象無實際意義。

示例:
1. 調用Activity的startActivity方法來撥打電話
Java代碼:

import android.app.Activity; import android.content.Intent; import android.net.Uri; //... // 獲取主Activity對象的實例 Activity main = context; // 建立Intent Uri uri = Uri.parse("tel:10086"); Intent call = new Intent("android.intent.action.CALL",uri); // 調用startActivity方法撥打電話 main.startActivity(call); //... 

NJS代碼:

// 導入Activity、Intent類 var Intent = plus.android.importClass("android.content.Intent"); var Uri = plus.android.importClass("android.net.Uri"); // 獲取主Activity對象的實例 var main = plus.android.runtimeMainActivity(); // 建立Intent var uri = Uri.parse("tel:10086"); var call = new Intent("android.intent.action.CALL",uri); // 調用startActivity方法撥打電話 main.startActivity( call ); // ... 

plus.android.currentWebview

獲取當前Webview窗口對象的native層實例對象,方法原型以下:

InstanceObject plus.android.currentWebview(); 

Android平臺完整Java類名爲android.webkit.Webview,完整API請參考Android開發文檔android.webkit.Webview

示例:
1. 調用Webview的loadUrl方法跳轉頁面
Java代碼:

import android.webkit.Webview; //... // 獲取Webview對象 Webview wv = this; // 跳轉頁面 wv.loadUrl("http://www.dcloud.io/"); //... 

NJS代碼:

// 導入Webview類 var Webview = plus.android.importClass("android.webkit.Webview"); // 當前Webview對象的實例 var wv = plus.android.currentWebview(); // 跳轉頁面 wv.loadUrl("http://www.dcloud.io/"); // ... 

完整API文檔參考:HTML5+ API - Native.js for Android

API on iOS

爲了能更好的理解NJS調用Objective-C Native API,咱們在iOS平臺用Objective-C實現如下測試類,將會在後面API說明中的示例來調用。
頭文件njshello.h代碼以下:

// 定義協議 @protocol NjsHelloEvent <NSObject> @required -(void) onEventInvoked:(NSString*)name; @end // ------------------------------------------------------------- // 定義類NjsHello @interface NjsHello : NSObject { NSString *_name; id<NjsHelloEvent > _delegate; } @property (nonatomic,retain) NSString *name; @property (nonatomic,retain) id delegate; -(void)updateName:(NSString*)newname; -(void)setEventObserver:(id<NjsHelloEvent >)delegate; -(void)test; +(void)testCount; @end 

實現文件njshello.m源代碼以下:

#import "njshello.h" // 實現類NjsHello @implementation NjsHello @synthesize name=_name; -(void)updateName:(NSString*)newname{ _name = [newname copy]; } -(void)setEventObserver:(id<NjsHelloEvent >)delegate{ _delegate = delegate; } -(void)test{ NSLog("My name is: %@",_name); [[self delegate]onEventInvoked:name]; } -(void)dealloc{ [_name release]; [supper dealloc]; } +(void)testCount{ NSLog( "Static test count" ); } @end 

plus.ios.importClass

導入Objective-C類對象,方法原型以下:

ClassObject plus.ios.importClass( String classname ); 

導入類對象後,就能夠經過「.」操做符直接調用對象(類對象/實例對象)的常量和方法。經過「.」操做符號調用方法時,不須要使用「:」來分割方法名。
- classname:要導入的Objective-C類名,若是指定的類名不存在,則導入類失敗,返回null。

注意:導入類對象能夠方便的使用「.」操做符來調用對象的屬性和方法,但也會消耗較多的系統資源。所以導入過多的類對象會影響性能,此時可使用「高級API」中提供的方法在不導入類對象的狀況下調用Native API。
示例:
1. 導入類並建立實例對象
Objective-C代碼:

#import "njshello.h" int main( int argc, char *argv[] ) { // 建立對象的實例 NjsHello* hello = [[NjsHello alloc] init]; // ... } // ... 

NJS代碼:

// 導入測試類NjsHello var NjsHello = plus.ios.importClass("NjsHello"); // 建立對象的實例 var hello = new NjsHello(); // ... 

ClassObject

調用plus.ios.importClass()方法導入類並返回ClassObject類對象,經過該類對象,能夠建立類的實例對象。在Objective-C中類的靜態方法會轉換成NJS類對象的方法,可經過類對象的「.」操做符調用;

注意:因爲Objective-C中類沒有靜態變量,而是經過定義全局變量來實現,目前NJS中沒法訪問全局變量的值。對於全局常量,在NJS中也沒法訪問,對於原類型常量可在文檔中找到其具體的值,在JS代碼中直接賦值;對於非原類型常量目前還沒法訪問。

示例:
1. 導入類後調用類的靜態方法
Objective-C代碼:

#import "njshello.h" // ... int main( int argc, char *argv[] ) { // 調用類的靜態方法 [NjsHello testCount]; // ... } // ... 

NJS代碼:

// 導入測試類NjsHello var NjsHello = plus.ios.importClass("NjsHello"); // 調用類的靜態方法 NjsHello.testCount(); // ... 

InstanceObject

NJS中實例對象與Objective-C中的對象對應,調用plus.ios.importClass()方法導入類後,經過new操做符可建立該類的實例對象,或直接調用plus.ios.newObject方法建立類的實例對象,也可經過調用Native API返回實例對象。在Objective-C中對象的方法會轉換成NJS實例對象的方法,可經過實例對象的「.」操做符調用;對象的屬性則必須經過NJS實例對象的plusGetAttribute、plusSetAttribute方法操做。

示例:
1. 導入類建立實例對象,調用對象的方法
Objective-C代碼:

#import "njshello.h" int main( int argc, char *argv[] ) { // 建立對象的實例 NjsHello* hello = [[NjsHello alloc] init]; // ... } // ... 

NJS代碼:

// 導入測試類NjsHello var NjsHello = plus.ios.importClass("NjsHello"); // 建立對象的實例 var hello = new NjsHello(); // ... 
InstanceObject.plusGetAttribute

獲取實例對象的屬性值,方法原型以下:

Object instancebject.plusGetAttribute( String name ); 

獲取實例對象後,就能夠調用其plusGetAttribute方法獲取對象的屬性值。
- name:要獲取對象的屬性名稱,若是指定的屬性名稱不存在,則獲取屬性失敗,返回null。

注意:若是實例對象中存在「plusGetAttribute」同名的方法,則只能經過plus.ios.invoke()方法調用。

示例:
1. 導入類建立實例對象,獲取對象的屬性值
Objective-C代碼:

#import "njshello.h" int main( int argc, char *argv[] ) { // 建立對象的實例 NjsHello* hello = [[NjsHello alloc] init]; [hello updateName:@"Tester"]; // 獲取其name屬性值 NSString* name = hello.name; NSLog("NjsHello Object's name: %@",name); // 輸出「NjsHello Object's name: Tester」 // ... } 

NJS代碼:

// 導入測試類NjsHello var NjsHello = plus.ios.importClass("NjsHello"); // 建立對象的實例 var hello = new NjsHello(); hello.updateName( "Tester" ); // 獲取其name屬性值 var name = hello.plusGetAttribute( "name" ); console.log( "NjsHello Object’s name: "+name ); // 輸出「NjsHello Object’s name: Tester」 // ... 
InstanceObject.plusSetAttribute

設置類對象的靜態屬性值,方法原型以下:

void instanceobject.plusSetAttribute( String name, Object value ); 

導入類對象後,就能夠調用其plusSetAttribute方法設置類的靜態屬性值。
- name:要設置的靜態屬性名稱,若是指定的屬性名稱不存在,則設置屬性失敗,返回null。
- value:要設置的屬性值,其類型必須與Native層類對象的靜態屬性區配,不然設置操做不生效,將保留之前的值。

注意:若是導入的類對象中存在「plusSetAttribute」同名的靜態方法,則只能經過plus.android.invoke()方法調用。

示例:
1. 導入類建立實例對象,設置對象的屬性值
Java代碼:

#import "njshello.h" int main( int argc, char *argv[] ) { // 建立對象的實例 NjsHello* hello = [[NjsHello alloc] init]; // 設置其name屬性值 hello.name = @"Tester"; NSLog("NjsHello Object's name: %@",hello.name); // 輸出「NjsHello Object's name: Tester」 // ... } //... 

NJS代碼:

// 導入測試類NjsHello var NjsHello = plus.ios.importClass("NjsHello"); // 建立對象的實例 var hello = new NjsHello(); // 設置其name屬性值 hello.plusSetAttribute( "name", "Tester" ); console.log( "NjsHello Object’s name: "+hello.plusGetAttribute("name") ); // 輸出「NjsHello Object’s name: Tester」 // ... 

plus.ios.implements

在Objective-C中能夠經過定義新類並實現Protocol的協議,並建立出新類對象做爲代理對象,在NJS中則可實現協議快速建立代理對象,方法原型以下:

Object plus.ios.implements( String name, Object obj ); 

此方法返回一個NJS實例對象,映射到Native層中的代理對象,其父類爲「NSObject」,而且實現obj中指定的協議方法。一般做爲調用其它Native API的參數。
- name:協議的名稱,也能夠是自定的字符串名稱用於定義一個代理。
- obj:JSON對象類型,代理實現方法的定義,JSON對象中key值爲協議中定義的方法名稱,必須保留方法名稱中的「:」字符;value值爲Function,方法參數必須與協議中定義方法的參數區配。

示例:
1. 實現一個代理,並調用test方法觸發調用代理的方法
Objective-C代碼:

#import "njshello.h" // 定義代理類NjsDelegate @interface NjsDelegate: NSObject<NjsHelloEvent> { -(void) onEventInvoked:(NSString*)name; } @end // ------------------------------------------------------------- // 實現代理類NjsDelegate @implementation NjsDelegate -(void) onEventInvoked:(NSString*)name{ NSLog("Invoked Object's name:%@",name); // 輸出「Invoked Object’s name: Tester」 } @end // ------------------------------------------------------------- // 主函數 int main( int argc, char *argv[] ) { // 建立對象的實例 NjsHello* hello = [[NjsHello alloc] init]; // 調用updateName方法 [hello updateName:@"Tester"]; // 建立代理對象 NjsDelegate* delegate = [[NjsDelegate alloc] init]; // 設置監聽對象 [hello setEventObserver:delegate]; // 調用test方法,觸發代理事件 [hello test]; // 觸發上面代理對象定義的onEventInvoked運行 // ... } 

在NJS中不須要建立新的類對象,調用plus.ios.implements實現協議接口便可建立出代理對象,代碼以下:

// 導入測試類NjsHello var NjsHello = plus.ios.importClass("NjsHello"); // 實現協議「NjsHelloEvent」的代理 var hevent = plus.ios.implements( "NjsHelloEvent", { "onEventInvoked":function( name ){ console.log( "Invoked Object’s name: "+name ); // 輸出「Invoked Object’s name: Tester」 } } ); // 調用updateName方法 hello.updateName( "Tester" ); // 設置監聽對象 hello.setEventObserver( hevent ); // 調用test方法,觸發代理事件 hello.test(); // 觸發上面代理對象定義的匿名函數運行 // ... 

plus.ios.deleteObject

釋放NJS中實例對象中映射的Native對象,方法原型以下:

void plus.ios.deleteObject( Object obj ); 

NJS中全部的實例對象(InstanceObject)均可以經過此方法釋放,會將Native層的對象使用的資源進行釋放。
- obj:要釋放的實例對象,若是obj對象不是有效的實例對象,則不執行對象的是否資源操做。

注意:此方法是可選的,若是不調用此方法釋放實例對象,則在頁面關閉時會自動釋放全部對象;若對象佔用較多的系統資源,則在業務邏輯處理完成時應該主動調用此方法釋放資源,以提到程序的運行效率。

示例:
1. 建立實例對象使用完成後,顯式操做銷燬對象
Objective-C代碼:

#import "njshello.h" int main( int argc, char *argv[] ) { // 建立對象的實例 NjsHello* hello = [[NjsHello alloc] init]; // 調用updateName方法 [hello updateName:@"Tester"]; // ... // 使用完後銷燬對象的實例 [hello release]; } 

NJS代碼:

// 導入測試類NjsHello var NjsHello = plus.ios.importClass("NjsHello"); // 建立對象的實例 var hello = new NjsHello(); // 調用updateName方法 hello.updateName( "Tester" ); // ... // 使用完後銷燬對象的實例 plus.ios.deleteObject( hello ); 

plus.ios.currentWebview

獲取當前Webview窗口對象的native層UIWebview實例對象,方法原型以下:

InstanceObject plus.ios.currentWebview(); 

UIWebview對象的API請參考Apple開發文檔UIWebview

示例:
1. 建立實例對象使用完成後,顯式操做銷燬對象
Objective-C代碼:

// 獲取當前Webview對象的實例 UIWebview* wv=self; // 建立請求對象 NSURLRequest *req = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.dcloud.io/"]]; // 跳轉頁面 [web loadRequest:req]; // 釋放對象 // 系統自動回收 // ... 

NJS代碼:

// 導入UIWebview、NSURLRequest、NSURL類 var Webview = plus.ios.importClass("UIWebview"); var NSURLRequest = plus.ios.import('NSURLRequest'); var NSURL = plus.ios.import('NSURL'); // 獲取當前Webview對象的實例 var wv = plus.ios.currentWebview(); // 建立請求對象 var req = NSURLRequest.requestWithURL(NSURL.URLWithString('http://www.dcloud.io/')); // 跳轉頁面 plus.ios.invoke(wv,"loadRequest:",req); // 釋放對象(可選) plus.ios.deleteObject(req); plus.ios.deleteObject(wv); // ... 

完整API文檔參考:HTML5+ API - Native.js for iOS

完整業務演示

Android

在Android手機桌面上建立快捷方式圖標,這是本來只有原生程序才能實現的功能。即便使用Hybrid方案,也須要原生工程師來配合寫插件。
下面咱們演示如何直接使用js在Android手機桌面建立快捷方式,在HelloH5+應用中Native.JS頁面中「Shortcut (Android)」能夠查看運行效果。
這段代碼是使用原生Java實現的建立快捷方式的代碼,用於參考比對:

import android.app.Activity; import android.content.Intent; import android.graphics.BitmapFactory; import android.graphics.Bitmap; // 建立桌面快捷方式 void createShortcut(){ // 獲取主Activity Activity main = this; // 建立快捷方式意圖 Intent shortcut = new Intent("com.android.launcher.action.INSTALL_SHORTCUT"); // 設置快捷方式的名稱 shortcut.putExtra(Intent.EXTRA_SHORTCUT_NAME, "HelloH5+"); // 設置不可重複建立 shortcut.putExtra("duplicate",false); // 設置快捷方式圖標 Bitmap bitmap = BitmapFactory.decodeFile("/sdcard/icon.png"); shortcut.putExtra(Intent.EXTRA_SHORTCUT_ICON, bitmap); // 設置快捷方式啓動執行動做 Intent action = new Intent(Intent.ACTION_MAIN); action.setComponent( main.getComponentName() ); shortcut.putExtra( Intent.EXTRA_SHORTCUT_INTENT, action ); // 廣播建立快捷方式 main.sendBroadcast(shortcut); } 

使用NJS實現時首先導入須要使用到的android.content.Intent、android.graphics.BitmapFactory類,按照Java代碼中的方法對應轉換成JavaScript代碼。
其中快捷方式圖標是經過解析本地png文件進行設置,在JavaScript中須要使用plus.io.* API轉換成本地路徑傳遞給Native API,完整代碼以下:

var Intent=null,BitmapFactory=null; var main=null; document.addEventListener( "plusready", function() {//"plusready"事件觸發時執行plus對象的方法 // ... if ( plus.os.name == "Android" ) { // 導入要用到的類對象 Intent = plus.android.importClass("android.content.Intent"); BitmapFactory = plus.android.importClass("android.graphics.BitmapFactory"); // 獲取主Activity main = plus.android.runtimeMainActivity(); } }, false); /** * 建立桌面快捷方式 */ function createShortcut(){ // 建立快捷方式意圖 var shortcut = new Intent("com.android.launcher.action.INSTALL_SHORTCUT"); // 設置快捷方式的名稱 shortcut.putExtra(Intent.EXTRA_SHORTCUT_NAME, "測試快捷方式"); // 設置不可重複建立 shortcut.putExtra("duplicate",false); // 設置快捷方式圖標 var iconPath = plus.io.convertLocalFileSystemURL("/icon.png"); // 將相對路徑資源轉換成系統絕對路徑 var bitmap = BitmapFactory.decodeFile(iconPath); shortcut.putExtra(Intent.EXTRA_SHORTCUT_ICON,bitmap); // 設置快捷方式啓動執行動做 var action = new Intent(Intent.ACTION_MAIN); action.setClassName(main.getPackageName(), 'io.dcloud.PandoraEntry'); shortcut.putExtra(Intent.EXTRA_SHORTCUT_INTENT,action); // 廣播建立快捷方式 main.sendBroadcast(shortcut); console.log( "桌面快捷方式已建立完成!" ); } 

注意:提交到雲平臺打包時須要添加Android權限才能在桌面建立快捷方式,在HBuilder工程中雙擊應用的「manifest.json」文件,切換到「代碼視圖」中在plus->distribute->google->permissions節點下添加權限數據:

"google": { // ... "permissions": [ "<uses-permission android:name=\"com.android.launcher.permission.INSTALL_SHORTCUT\"/>" ] } 

以下圖所示:
manifest.json中Android權限 permissions

iOS

在iOS手機上登陸game center,一個遊戲中心服務,這是本來只有原生程序才能實現的功能。即便使用Hybrid方案,也須要原生工程師來配合寫插件。
下面咱們演示如何直接使用js在iOS手機上登陸game center,在HelloH5+應用中Native.JS頁面中的「Game Center (iOS)」能夠查看運行效果。
注意手機未開通game center則沒法登錄,請先點擊iOS自帶的game center進行配置。
這段代碼是使用原生Objective-C實現的登陸game center的代碼,用於參考比對。原生Objective-C代碼的頭文件Test.h中代碼以下:

@interface Test: NSObject // 遊戲玩家登陸狀態監聽函數 - (void)authenticationChanged:(NSNotification*)notification; // 獲取遊戲玩家狀態信息 - (void)playerInformation:(GKPlayer *)player; // 登陸到遊戲中心 - (void)loginGamecenter; // 中止監聽登陸游戲狀態變化 - (void)logoutGamecenter; @end  實現文件Test.m中代碼以下: @implementation Test // 遊戲玩家登陸狀態監聽函數 - (void)authenticationChanged:(NSNotification*)notification { // 獲取遊戲玩家共享實例對象 GKLocalPlayer *player = notification.object; if ( player.isAuthenticated ) { // 玩家已登陸認證,獲取玩家信息 [self playerInformation:player]; } else { // 玩家未登陸認證,提示用戶登陸 NSLog(@"請登陸!"); } // 釋放使用的對象 [player release]; } // 獲取遊戲玩家狀態信息 - (void)playerInformation:(GKPlayer *)player { // 獲取遊戲玩家的名稱 NSLog(@"Name: %@",player.displayName); }  // 登陸到遊戲中心 - (void)loginGamecenter { // 監聽用戶登陸狀態變動事件 NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; [nc addObserver:self selector:@selector(authenticationChanged) name:@"GKPlayerAuthenticationDidChangeNotificationName" object:nil]; // 獲取遊戲玩家共享實例對象 GKLocalPlayer *localplayer = [GKLocalPlayer localPlayer]; // 判斷遊戲玩家是否已經登陸認證 if ( localplayer.isAuthenticated ) { // 玩家已登陸認證,獲取玩家信息 [self playerInformation:localplayer]; } else { // 玩家未登陸認證,發起認證請求 [localplayer authenticateWithCompletionHandler:nil]; NSLog(@"登陸中..."); } // 釋放使用的對象 [localplayer release]; [nc release]; }  // 中止監聽登陸游戲狀態變化 - (void)logoutGamecenter { // 取消監聽用戶登陸狀態變化 NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; [nc removeObserver:self name:@"GKPlayerAuthenticationDidChangeNotificationName" object:nil]; // 釋放使用的對象 [nc release]; } @end 

使用NJS實現時能夠按照Objective-C代碼中的方法對應轉換成JavaScript代碼,最關鍵的代碼是loginGamecenter方法中對用戶登陸狀態的監聽,需調用NSNotificationCenter對象的「addObserver:selector:name:object」方法,
1. addObserver:後要求傳入一個實例對象用於查找selector參數中指定的方法,在Objective-C中一般將對象自身(self)傳入,但在NJS中沒有此概念,所以需使用plus.ios.implements方法來建立一個新的對象:
var delegate = plus.ios.implements("NSObject",{"authenticationChanged:":authenticationChanged});
第一個參數「NSObject」表示對象的類型,第二個參數中的JSON對象代表對象擁有的方法,「authenticationChanged」方法是delegate對象的方法。
2. selector:後要傳入一個類函數指針,在Objective-C中經過「@selector」指令可選擇函數指針,在NJS中則需使用plus.ios.newObject方法來建立一個函數對象:
plus.ios.newObject("@selector","authenticationChanged:")
第一個參數需固定值爲「@selector」,表示建立的是類函數指針對象,第二個參數。
在"plusready"事件中導入GKLocalPlayer和NSNotificationCenter類,並調用登陸方法longinGamecenter()。

完整JavaScript代碼以下:

// 處理"plusready"事件 var bLogin=false; document.addEventListener( "plusready", function() { // ... if ( plus.os.name == "iOS" ) { GKLocalPlayer = plus.ios.importClass("GKLocalPlayer"); NSNotificationCenter = plus.ios.importClass("NSNotificationCenter"); longinGamecenter(); } else { alert("歡迎您"); bLogin = true; setTimeout( function(){ plus.ui.toast( "此平臺不支持Game Center功能!" ); }, 500 ); } }, false);  var GKLocalPlayer=null,NSNotificationCenter=null; var delegate=null;  // 遊戲玩家登陸狀態監聽函數 function authenticationChanged( notification ){ // 獲取遊戲玩家共享實例對象 var player = notification.plusGetAttribute("object"); if ( player.plusGetAttribute("isAuthenticated") ) { // 玩家已登陸認證,獲取玩家信息 playerInformation(player); bLogin = true; } else { // 玩家未登陸認證,提示用戶登陸 alert("請登陸"); bLogin = false; } // 釋放使用的對象 plus.ios.deleteObject(player); }  // 獲取遊戲玩家狀態信息 function playerInformation( player ){ var name = player.plusGetAttribute("displayName"); alert( name+" 已登陸!" ); }  // 登陸到遊戲中心 function longinGamecenter(){ if ( bLogin ){ return; } // 監聽用戶登陸狀態變動事件 var nc = NSNotificationCenter.defaultCenter(); delegate = plus.ios.implements("NSObject",{"authenticationChanged:":authenticationChanged}); nc.addObserverselectornameobject(delegate, plus.ios.newObject("@selector","authenticationChanged:"), "GKPlayerAuthenticationDidChangeNotificationName", null); // 獲取遊戲玩家共享實例對象 var localplayer = GKLocalPlayer.localPlayer(); // 判斷遊戲玩家是否已經登陸認證 if ( localplayer.isAuthenticated() ) { // localplayer.plusGetAttribute("isAuthenticated") // 玩家已登陸認證,獲取玩家信息 playerInformation( localplayer ); bLogin = true; } else { // 玩家未登陸認證,發起認證請求 localplayer.authenticateWithCompletionHandler(null); alert( "登陸中..." ); } // 釋放使用的對象 plus.ios.deleteObject(localplayer); plus.ios.deleteObject(nc); }  // 中止監聽登陸游戲狀態變化 function stopGamecenterObserver() { // 取消監聽用戶登陸狀態變化 var nc = NSNotificationCenter.defaultCenter(); nc.removeObservernameobject(delegate,"GKPlayerAuthenticationDidChangeNotificationName",null); plus.ios.deleteObject(nc); plus.ios.deleteObject(delegate); delegate = null; } 

注意
1. 提交到雲平臺打包時須要添加Game Center API的系統庫(framework)才能正確調用,在HBuilder工程中雙擊應用的「manifest.json」文件,切換到「代碼視圖」中在plus->distribute->apple->frameworks節點下添加要引用的系統Framework:

"apple": { "devices": "universal", "frameworks": [ "GameKit.framework" ] } 

,以下圖所示:
HBuilder frameworks
2. 正式發佈提交到AppStore時,在配置蘋果開發者網站上配置App ID須要選中「Game Center」服務:
Game Center

開發注意和建議用途

Native.js的運行性能仍然不比純原生應用;JS與Native之間的數據交換效率並不如在js內部的數據交換效率;基於如上緣由,有幾點開發建議:
- 以標準web 代碼爲主,當遇到web能力不足的時候,調用Native.js。
- 以標準web 代碼爲主,當遇到web性能不足的時候,須要分析,
if ((原生進行運算的效率-js與原生通信的損耗)>純web的效率){
使用Native.js
}else{
還應該使用純js
}
- 應避免把程序設計爲在短期內併發觸發Native.js代碼

調試

使用safari和chrome的控制檯調試HBuilder的5+App時,同樣能夠調試NJS對象,便可以在瀏覽器控制檯中查看各類原生對象的屬性和方法,以下圖所示,57行設了斷點,watch了Intent對象,並在右邊展開了該對象的全部屬性方法:
Chrome Debug
關於如何在瀏覽器控制檯調試HBuilder的5+App,請參考HBuilder的5+App開發入門教程。

開發資源

iOS 官方在線文檔:https://developer.apple.com/library/ios/navigation/
Android 官方在線文檔:https://developer.android.com/reference/packages.html
演講視頻:http://v.youku.com/v_show/id_XNzYzNTcwNDI4.html

高級API

有前述的經常使用API,已經能夠完成各項業務開發。此處補充的高級API,是在熟悉NJS後,爲了提高性能而使用的API。高級API沒法直接用「.」操做符使用原生對象的方法,在debug時也沒法watch原生對象,但高級API性能高於常規API。
雖然導入類對象(plus.android.importClass和plus.ios.importClass)後,能夠方便的經過「.」操做符來訪問對象的常量、調用對象的方法,但導入類對象也須要消耗較多的系統資源,因此在實際開發時應該儘量的減小導入類對象,以提升程序效率。能夠參考如下依據進行判斷:
1. 如導入的類特別複雜,繼承自不少基類,方法和屬性特別多則考慮不導入類;
2. 對導入類是否須要頻繁操做,若導入類僅是爲了實例化,並做爲調用其它API的參數,則不該該導入類;
3. 在同一頁面中是否導入了不少類?若是導入太多則須要考慮減小導入類的數目。

若是咱們不導入類對象則沒法經過new操做符實例化類對象,這時可經過plus.ios.newObject()、plus.android.newObject()方法來建立實例對象,以下:

// iOS平臺建立NSDictionary的實例對象 var ns = plus.ios.newObject( "NSDictionary" );  // Android平臺建立Intent的實例對象 var intent = plus.android.newObject( "android.content.Intent" ); 

API on Android

plus.android.newObject

不導入類對象直接建立類的實例對象,方法原型以下:

InstanceObject plus.android.newObject( String classname, Object...args ); 

此方法對Native層中對類進行實例化操做,建立一個類的實體並返回NJS層的實例對象。相比導入類對象後使用new操做符建立對象效率要高。
- classname:要建立實例對象的類名,類名必須是完整的命名空間,使用「.」分隔符(如「android.app.AlertDialog」),若是須要建立內部類對象須要使用「$」分割符(如「android.app.AlertDialog$Builder」)。若是指定的類名不存在,則建立對象失敗,返回null。
- args:調用類構造函數的參數,其類型和數目必須與Native層Java類構造函數區配,不然沒法建立類對象,將返回null。

注意:因爲沒有導入類對象,因此經過此方法建立的實例對象沒法經過「.」操做符直接調用對象的方法,而必須使用plus.android.invoke方法來調用。

示例:
1. 不導入類建立實例對象
Java代碼:

import io.dcloud.NjsHello; //... public class Test { public static void main( String args[] ) { // 建立對象的實例 NjsHello hello = new NjsHello(); //... } //... } 

NJS代碼:

// 不調用plus.android.importClass("io.dcloud.NjsHello")導入類NjsHello // 建立對象的實例 var hello = plus.android.newObject( "io.dcloud.NjsHello" ); // ... 

plus.android.getAttribute

不導入類對象,則沒法經過類對象並訪問類的靜態屬性,需調用如下方法獲取類的靜態屬性值,方法原型以下:

Object plus.android.getAttribute( String|Object obj, String name ); 

此方法也能夠獲取類對象或實例對象的屬性值,若是是類對象獲取的則是類的靜態屬性,若是是實例對象則獲取的是對象的非靜態屬性。
- obj:如果String類型,表示要獲取靜態屬性值的類名,類名必須是完整的命名空間(使用"."分割);如果ClassObject類型,表示要獲取靜態屬性的類對象;如果InstanceObject類型,表示要獲取屬性值的實例對象。
- name:要獲取的屬性名稱,若是指定的屬性名稱不存在,則獲取屬性失敗,返回null。

注意:一樣導入類對象後也能夠調用此方法,obj參數類型爲ClassObject時,其做用與ClassObject.plusSetAttribute方法一致。obj參數類型爲InstanceObject時,其做用與InstanceObject.plusSetAttribute方法一致。

示例:
1. 不導入類對象獲取類的靜態常量屬性
Java代碼:

import io.dcloud.NjsHello; //... public class Test { public static void main( String args[] ) { // 獲取類的靜態常量屬性 int type = NjsHello.CTYPE; System.out.printf( "NjsHello Final's value: %d", type ); // 輸出「NjsHello Final's value: 1」 // 獲取類的靜態屬性 int count = NjsHello.count; System.out.printf( "NjsHello Static's value: %d", count ); // 輸出「NjsHello Static's value: 0」 //... } //... } 

NJS代碼:

// 不調用plus.android.importClass("io.dcloud.NjsHello")導入類NjsHello // 訪問類的靜態常量屬性 var type = plus.android.getAttribute( "io.dcloud.NjsHello", "CTYPE" ); console.log( "NjsHello Final's value: "+type ); // 輸出「NjsHello Final's value: 1」 // ... 

 

  1. 不導入類對象,建立實例對象,並獲取其name屬性值
    Java代碼:
import io.dcloud.NjsHello; //... public class Test { public static void main( String args[] ) { // 建立對象的實例 NjsHello hello = new NjsHello(); // 獲取其name屬性值 String name = hello.name; System.out.printf( "NjsHello Object's name: %s", name ); // 輸出「NjsHello Object's name: Tester」 //... } //... } 

NJS代碼:

// 不調用plus.android.importClass("io.dcloud.NjsHello")導入類NjsHello // 建立對象的實例 var hello = plus.android.newObject( "io.dcloud.NjsHello" ); // 獲取其name屬性值 var name = plus.android.getAttribute( hello, "name" ); console.log( "NjsHello Object's name: "+name ); // 輸出「NjsHello Object's name: Tester」 // ... 

plus.android.setAttribute

若沒有導入類對象,則沒法經過類對象設置類的靜態屬性值,需調用如下方法設置類的靜態屬性值,方法原型以下:

void plus.android.setAttribute( String|Object obj, String name, Object value ); 

此方法也能夠設置類對象或實例對象的屬性值,若是是類對象設置的則是類的靜態屬性,若是是實例對象則設置的是對象的非靜態屬性。
- obj:如果String類型,表示要設置靜態屬性值的類名,類名必須是完整的命名空間(使用"."分割);如果ClassObject類型,表示要設置靜態屬性的類對象;如果InstanceObject類型,表示要設置屬性值的實例對象。
- name:要設置的屬性名稱,若是指定的屬性名稱不存在,則設置屬性失敗,返回null。
- value:要設置的屬性值,其類型必須與Native層obj對象的屬性區配,不然設置操做不生效,將保留之前的值。

注意:一樣導入類對象後也能夠調用此方法,obj參數類型爲ClassObject時,其做用與ClassObject.plusSetAttribute方法一致。obj參數類型爲InstanceObject時,其做用與InstanceObject.plusSetAttribute方法一致。

示例:
1. 不導入類對象設置類的靜態屬性值
Java代碼:

import io.dcloud.NjsHello; //... public class Test { public static void main( String args[] ) { // 設置類的靜態屬性值 NjsHello.count = 2; System.out.printf( "NjsHello Static's value: %d", NjsHello.count ); // 輸出「NjsHello Static's value: 2」 //... } //... } 

NJS代碼:

// 不調用plus.android.importClass("io.dcloud.NjsHello")導入類NjsHello // 設置類的靜態屬性值 plus.android.setAttribute( "io.dcloud.NjsHello", "count", 2 ); console.log( "NjsHello Static's value: "+plus.android.getAttribute("io.dcloud.NjsHello","count") ); // 輸出「NjsHello Static's value: 2」 // ... 

 

  1. 導入類對象,建立實例對象,並設置其name屬性值
    Java代碼:
import io.dcloud.NjsHello; //... public class Test { public static void main( String args[] ) { // 建立對象的實例 NjsHello hello = new NjsHello(); // 設置其name屬性值 hello.name = "Tester"; System.out.printf( "NjsHello Object's name: %s", hello.name ); // 輸出「NjsHello Object's name: Tester」 //... } //... } 

NJS代碼:

// 不調用plus.android.importClass("io.dcloud.NjsHello")導入類NjsHello // 建立對象的實例 var hello = plus.android.newObject( "io.dcloud.NjsHello" ); // 設置其name屬性值 plus.android.setAttribute( hello, "name", "Tester" ); console.log( "NjsHello Object's name: "+hello.plusGetAttribute("name") ); // 輸出「NjsHello Object's name: Tester」 // ... 

plus.android.invoke

若沒有導入類對象,則沒法經過實例對象的「.」操做符調用其成員方法,需經過如下方法調用實例對象的成員方法,方法原型以下:

Object plus.android.invoke( String|Object obj, String name, Object... args ); 

此方法也能夠調用類對象或實例對象的方法,若是是類對象則調用的是類的靜態方法,若是是實例對象則調用的是對象的普通成員方法。函數返回值是調用Native層方法運行後的返回值,Native對象的方法無返回值則返回undefined。
- obj:如果String類型,表示要調用靜態方法的類名,類名必須包含完整的包名;如果ClassObject類型,表示要調用靜態方法的類對象;如果InstanceObject類型,表示要調用成員方法的實例對象。
- name:要調用的方法名稱,若是指定的方法不存在,則調用方法失敗,返回值爲null。
- args:調用方法的參數,其類型和數目必須與Native層對象方法的函數區配,不然沒法調用對象的方法,將返回null。

注意:一樣導入類對象後也能夠調用此方法,其做用與經過類對象或實例對象的「.」操做符調用方法做用一致。

示例:
1.不導入類對象,調用類的靜態方法
Java代碼:

import io.dcloud.NjsHello; //... public class Test { public static void main( String args[] ) { // 調用類的靜態方法 NjsHello.testCount(); //... } //... } 

NJS代碼:

// 不調用plus.android.importClass("io.dcloud.NjsHello")導入類NjsHello // 調用類的靜態方法 plus.android.invoke( "io.dcloud.NjsHello", "testCount" ); // ... 

 

  1. 不導入類對象,建立實例對象,並調用其updateNmae方法
    Java代碼:
import io.dcloud.NjsHello; //... public class Test { public static void main( String args[] ) { // 建立對象的實例 NjsHello hello = new NjsHello(); // 調用updateName方法 hello.updateName( "Tester" ); System.out.printf( "NjsHello Object's name: %s", name ); // 輸出「NjsHello Object's name: Tester」 //... } //... } 

NJS代碼:

// 不調用plus.android.importClass("io.dcloud.NjsHello")導入類NjsHello // 建立對象的實例 var hello = plus.android.newObject( "io.dcloud.NjsHello" ); // 調用updateName方法 plus.android.invoke( hello, "updateName", "Tester" ); console.log( "NjsHello Object's name: "+hello.getAttribute("name") ); // 輸出「NjsHello Object's name: Tester」 // ... 

完整API文檔參考:HTML5+ API - Native.js for Android

API on iOS

plus.ios.newObject

不導入類對象直接建立類的實例對象,方法原型以下:

InstanceObject plus.ios.newObject( String classname, Object..args ); 

此方法會在Native層中對類進行實例化操做,建立一個類的實體並返回NJS層的類實例對象。相比導入類對象後使用new操做符建立對象效率要高。
- classname:要建立實例對象的類名,若是指定的類名不存在,則建立對象失敗,返回null。
- args:調用類構造函數的參數,其類型和數目必須與Native層對象構造函數區配,不然沒法建立類對象,將返回null。

注意:因爲沒有導入類對象,因此經過此方法建立的實例對象沒法經過「.」操做符直接調用對象的方法,而必須使用plus.ios.invoke方法來調用。classname參數值爲「@selector」表示須要建立一個函數指針對象,與Objective-C中的@selector指令功能類似,args參數爲函數的名稱,此時函數的名稱須要包含「:」字符。

示例:
1. 不導入類建立實例對象
Objective-C代碼:

#import "njshello.h" int main( int argc, char *argv[] ) { // 建立對象的實例 NjsHello* hello = [[NjsHello alloc] init]; // ... } 

NJS代碼:

// 未導入「NjsHello」類 // 建立對象的實例 var hello = plus.ios.newObject( "NjsHello" ); // ... 

plus.ios.invoke

若沒有導入類對象,則沒法經過實例對象的「.」操做符調用其成員方法,需經過如下方法調用實例對象的成員方法,方法原型以下:

Object plus.ios.invoke( String|Object obj, String name, Object... args ); 

此方法也能夠調用類對象或實例對象的方法,若是是類對象則調用的是類的靜態方法,若是是實例對象則調用的是對象的普通成員方法。函數返回值是調用Native層方法運行後的返回值,Native對象的方法無返回值則返回undefined。
- obj:如果String類型,表示要調用靜態方法的類名,類名必須包含完整的包名;如果ClassObject類型,表示要調用靜態方法的類對象;如果InstanceObject類型,表示要調用成員方法的實例對象。
- name:要調用的方法名稱,必須保留方法名稱中的「:」字符,若是指定的方法不存在,則調用方法失敗,返回值爲null。
- args:調用方法的參數,其類型和數目必須與Native層對象方法的函數區配,不然沒法調用對象的方法,將返回null。

注意:一樣導入類對象後也能夠調用此方法,其做用與經過類對象或實例對象的「.」操做符調用方法做用一致。

示例:
1. 不導入類建立實例對象,並調用updateName方法
Objective-C代碼:

#import "njshello.h" int main( int argc, char *argv[] ) { // 建立對象的實例 NjsHello* hello = [[NjsHello alloc] init]; // 調用updateName方法 [hello updateName:@"Tester"]; NSLog("NjsHello Object's name: %@",hello.name); // 輸出「NjsHello Object's name: Tester」 // ... } 

NJS代碼:

// 未導入「NjsHello」類 // 建立對象的實例 var hello = plus.ios.newObject( "NjsHello" ); // 調用updateName方法 plus.ios.invoke( hello, "updateName", "Tester" ); console.log( "NjsHello Object's name: "+hello.getAttribute("name") ); // 輸出「NjsHello Object's name: Tester」 // ... 

完整API文檔參考:HTML5+ API - Native.js for iOS

性能優化

調整代碼結構優化

前面章節中咱們介紹如何經過NJS調用Native API來顯示系統提示框,在真機運行時會發現第一次調用時會有0.5s左右的延時,再次調用則不會延時。這是由於NJS中導入類對象操做會花費較長的時間,再次調用時因爲類對象已經導入過,會能很快執行完畢。所以能夠調整代碼結構進行優化,在頁面打開後觸發的「plusready」事件中進行類對象的導入操做,從而避免第一次調用的延時。

Android平臺調整NJS代碼結構以下:

// 保存Android導入對象和全局環境對象 var AlertDialog=null,mainActivity=null; // H5+事件處理 document.addEventListener("plusready",function(){ switch ( plus.os.name ) { case "Android": // 程序全局環境對象,內部自動導入Activity類 mainActivity = plus.android.runtimeMainActivity(); // 導入AlertDialog類 AlertDialog = plus.android.importClass("android.app.AlertDialog"); break; default: break; } },false); //... /** * 在Android平臺經過NJS顯示系統提示框 */ function njsAlertForAndroid(){ // 建立提示框構造對象,構造函數須要提供程序全局環境對象,經過plus.android.runtimeMainActivity()方法獲取 var dlg = new AlertDialog.Builder(mainActivity); // 設置提示框標題 dlg.setTitle("自定義標題"); // 設置提示框內容 dlg.setMessage("使用NJS的原生彈出框,可自定義彈出框的標題、按鈕"); // 設置提示框按鈕 dlg.setPositiveButton("肯定(或者其餘字符)",null); // 顯示提示框 dlg.show(); } //... 

iOS平臺調整NJS代碼結構以下:

// 保存iOS平臺導入的類對象 var UIAlertView=null; // H5+事件處理 document.addEventListener("plusready",function(){ switch ( plus.os.name ) { case "iOS": // 導入UIAlertView類 UIAlertView = plus.ios.importClass("UIAlertView"); break; default: break; } },false); //... /** * 在iOS平臺經過NJS顯示系統提示框 */ function njsAlertForiOS(){ // 建立UIAlertView類的實例對象 var view = new UIAlertView(); // 設置提示對話上的內容 view.initWithTitlemessagedelegatecancelButtonTitleotherButtonTitles("自定義標題" // 提示框標題 , "使用NJS的原生彈出框,可自定義彈出框的標題、按鈕" // 提示框上顯示的內容 , null // 操做提示框後的通知代理對象,暫不設置 , "肯定(或者其餘字符)" // 提示框上取消按鈕的文字 , null ); // 提示框上其它按鈕的文字,設置爲null表示不顯示 // 調用show方法顯示提示對話框 view.show(); } //... 

使用高級API優化

前面章節中咱們提到導入類對象會消耗較多的系統資源,導入過多的類對象會影響性能。在高級API中提供一組接口能夠在不導入類對象的狀況下調用Native API,從而提高代碼運行性能。

Android平臺使用高級API優化代碼以下:

// 保存Android導入對象和全局環境對象 var mainActivity=null; // H5+事件處理 document.addEventListener("plusready",function(){ switch ( plus.os.name ) { case "Android": // 程序全局環境對象,內部自動導入Activity類 mainActivity = plus.android.runtimeMainActivity(); break; default: break; } },false); //... /** * 在Android平臺經過NJS顯示系統提示框 */ function njsAlertForAndroid(){ // 因爲Builder類是android.app.AlertDialog類的內部類,這裏須要使用$符號分割 var dlg = plus.android.newObject("android.app.AlertDialog$Builder",mainActivity); // 設置提示框標題 plus.android.invoke(dlg,"setTitle","自定義標題"); // 設置提示框內容 plus.android.invoke(dlg,"setMessage","使用NJS的原生彈出框,可自定義彈出框的標題、按鈕"); // 設置提示框按鈕 plus.android.invoke(dlg,"setPositiveButton","肯定(或者其餘字符)",null); // 顯示提示框 plus.android.invoke(dlg,"show"); } //... 

iOS平臺使用高級API優化代碼以下:

/** * 在iOS平臺經過NJS顯示系統提示框 */ function njsAlertForiOS(){ // 建立UIAlertView類的實例對象 var view = plus.ios.newObject("UIAlertView"); // 設置提示對話上的內容,這裏的方法名稱中必須包含':'字符 plus.ios.invoke(view,"initWithTitle:message:delegate:cancelButtonTitle:otherButtonTitles:" ,"自定義標題" // 提示框標題 , "使用NJS的原生彈出框,可自定義彈出框的標題、按鈕" // 提示框上顯示的內容 , null // 操做提示框後的通知代理對象,暫不設置 , "肯定(或者其餘字符)" // 提示框上取消按鈕的文字 , null ); // 提示框上其它按鈕的文字,設置爲null表示不顯示 // 調用show方法顯示提示對話框,在JS中使用()語法調用對象的方法 plus.ios.invoke(view,"show"); } //...
相關文章
相關標籤/搜索