概述
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技術不能在普通手機瀏覽器里直接運行。
- 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。
技術要求
由於NJS是直接調用Native API,需要對Native API有一定了解,知道所需要的功能調用了哪些原生API,能看懂原生代碼並參考原生代碼修改為JS代碼。
否則只能直接copy別人寫好的NJS代碼。
開始使用
判斷平台
Native API具有平台依賴性,所以需要通過以下方式判斷當前的運行平台:
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時會自動轉換數據類型。
類型轉換表
類型 | 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中的類進行操作,如創建類的實例對象、訪問類的靜態屬性、調用類的靜態方法等。其原型如下:
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平台導入NSNotificationCenter類 var NSNotificationCenter = plus.ios.importClass("NSNotificationCenter"); // Android平台導入Intent類 var Intent = plus.android.importClass("android.content.Intent");
獲取類對象后,可以通過類對象“.”操作符獲取類的靜態常量屬性、調用類的靜態方法,類的靜態非常量屬性需通過plusGetAttribute、plusSetAttribute方法操作。
實例對象
在JavaScript中,所有對象都是Object,為了操作Native層類的實例對象,在NJS中引入了實例對象(InstanceObject)的概念,用於對Native中的對象進行操作,如操作對象的屬性、調用對象的方法等。其原型如下:
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設備上運行效果圖:
`注意:其實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設備上運行效果圖:
`注意:其實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” // ...
- 導入類后調用類的靜態方法
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" ); // ...
- 導入類創建實例對象,獲取對象的常量屬性
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\"/>" ] }
如下圖所示:
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]; }