原文: https://blog.csdn.net/qq_21298703/article/details/44982547javascript
------------------------------------------------------------------------php
注* NativeScript是最近推出的一個跨平臺解決方案,可讓你能夠用JavaScript來直接寫Android、iOS本地應用程序,將來還即將擴展到Windows平臺。是最近比較受關注的項目。它與 nw(原名node-webkit ,用Web寫winodw/linux桌面應用)和 phonegap內嵌webview寫APP的實現方式有着本質的不一樣,它直接用JavaScript調用系統原生API,於是有一些原生應用的特色。html
NativeScript是一個運行環境,可讓你使用通用的JavaScript代碼,打造原生的iOS,Android和 Windows(即將推出)應用程序。 NativeScript有不少很酷的功能,好比支持JavaScript對象雙向綁定到原生UI組件,以及用CSS爲原生應用程序寫樣式。但我最喜歡的 功能是NativeScript可讓您直接訪問本地平臺的原生API。java
注* 能夠理解爲NativeScript是一個JavaScript V8運行環境的命令轉發代理,將JavaScript調用轉發給不一樣平臺上的原生API如Android、iOS,以及即將支持的Windows。node
例如,看看這個NativeScript寫的Android應用程序的代碼:linux
1
2
|
var
time =
new
android.text.format.Time();time.set( 1, 0, 2015 );
console.log( time.format(
"%D"
) );
|
你只須要一兩分鐘來分析一下就明白了,這段JavaScript代碼實例化一個Java android.text.format.Time()對象,調用其set()方法,而後打印format後的返回值,是字符串「01/01/15」。android
我知道你已經很激動了,先不要慌,讓咱們再來看看iOS的代碼:ios
1
2
3
4
|
var
alert =
new
UIAlertView();
alert.message =
"Hello world!"
;
alert.addButtonWithTitle(
"OK"
);
alert.show();
|
這段JavaScript代碼實例化一個Objective-C UIAlertView類,設置它的信息屬性,而後調用它的addButtonWithTitle()和show()方法。當您運行這段代碼,你會看到hello word的警告框。git
該NativeScript運行環境看起來可能像變魔術同樣,無論你信不信,該架構並非那麼的複雜。一切從JavaScript虛擬機開 始,NativeScript從這裏開始執行JavaScript指令。具體來講,NativeScript在Android採用v8;在iOS上採用 JavaScriptCore。因爲NativeScript使用JavaScript虛擬機,你訪問原生API的全部JavaScript代碼,仍然須要遵照JavaScript的語法結構和規範。github
通常來講,NativeScript會同時採用V8和JavaScriptCore的最新穩定版;所以NativeScript對 ECMAScript語言的支持在iOS的桌面Safari上面幾乎是相同的,而且NativeScript在Android上面也幾乎與桌面瀏覽器相 同。包括一些ES6的新語法。
瞭解NativeScript使用了JavaScript虛擬機是很重要的,但這只是實現的第一步,讓咱們來看看上文示例的第一行代碼:
1
|
var
time =
new
android.text.format.Time();
|
在NativeScript Android運行環境中,該代碼會被編譯( JIT)並在V8中執行。這咱們可能會很容易理解變...:
1
|
var
x = 1 + 2;
|
可是接下來的問題是...V8怎麼知道什麼是android.text.format.Time()呢?
咱們將重點講V8和Android的實現,基本架構模式一樣適用於iOS上的JavaScriptCore。若是出現顯着差異,本文會提到。
這裏將不討論NativeScript在Windows上的實現細節,由於解決方案可能還會變,可是,當前的Windows實現跟iOS上的JavaScriptCore運行原理幾乎同樣。
V8知道android是什麼,由於NativeScript在運行時進行了注入,由於V8擁有一堆讓你配置JavaScript環境的 API。在JavaScript中您可使用自定義的C++代碼來分析CPU使用率,管理的JavaScript垃圾收集,等等一大堆API
面對這些API的是幾個「Context」類,可讓你操縱全局變量,從而有可能爲NativeScript注入一個全局的android對 象。這實際上使用了與Node.js的相同運行機制,使全局API可用 - 如 require() - NativeScript使用它注入可讓你訪問本地代碼的API。 JavaScriptCore的也有相似的機制。酷吧?
讓咱們回到咱們的代碼:
1
|
var
time =
new
android.text.format.Time();
|
如今你知道這個代碼在V8中運行時,而V8已經知道什麼是android.text.format.Time()了,由於 NativeScript注入了必需的對象到全局範圍。但仍存在着一些大的懸而未決的問題,如何讓NativeScript明白那些注入的API究竟是幹 什麼的,而後調用?
該NativeScript運行環境看起來可能像變魔術同樣,無論你信不信,該架構並非那麼的複雜。一切從JavaScript虛擬機開 始,NativeScript從這裏開始執行JavaScript指令。具體來講,NativeScript在Android採用v8;在iOS上採用 JavaScriptCore。因爲NativeScript使用JavaScript虛擬機,你訪問原生API的全部JavaScript代碼,仍然須要遵照JavaScript的語法結構和規範。
對於NativeScript,反射是讓NativeScript能夠調用每一個平臺上的API的基石。包括 android.text.format.Time。由於從性能角度來看重構這些API是很困難的,NativeScript會提早作掉這些,並在 Android/iOS預編繹過程當中嵌入預先生成的元數據。
考慮到這一點,讓咱們再次回到咱們的代碼:
1
|
var
time =
new
android.text.format.Time();
|
如今你瞭解了這個V8代碼是這樣運行的,即NativeScript注入了android.text.format.Time的 JavaScript對象,經過每個單獨的元數據注入。下一個問題:如何將NativeScript裏的JavaScript調用Time()轉發到本 機android.text.format.Time()?
NativeScript如何調用本機代碼的答案就在於JavaScript虛擬機的API。咱們上次使用V8的API是注入全局變量。這一次,咱們將着眼於在JavaScript回調中調用給定的C++代碼。
例如,JavaScript函數調用的代碼 new android.text.format.Time(),V8會產生一個回調。也就是說V8有一個回調,讓NativeScript攔截函數調用,而後用自定義的C ++代碼執行一些動做,並返回一 個新的結果。
在Android中的情 況下,NativeScript運行的C++代碼不能直接訪問Java API,如android.text.format.Time。然而,Android的 JNI,或Java本地接口,提供了C++和Java之間的橋接能力,因此NativeScript使用JNI完成轉發。在iOS中這個橋樑是沒必要要的,由於C++代碼能夠直接調用Objective-C的API。
瞭解了這些,讓咱們再回到代碼:
var time = new android.text.format.Time();
咱們已經知道,這個代碼在V8中運行;是由於NativeScript注入過對象,它知道什麼是 android.text.format.Time;而且NativeScript中有這些基於元數據生成的API。咱們如今知道,當 Timer() 執行時,會發生下面的事情:
1)V8運行回調函數。
2)NativeScript運行時經過它的元數據知道,Time()調用須要實例化一個android.text.format.Time對象。
3)NativeScript運行時使用JNI來實例化一個android.text.format.Time對象並保持對它的引用。
4)NativeScript運行時將代理的Java Time對象轉化成JavaScript對象返回。
5)控制返回的JavaScript代理對象爲被存儲起來的本地時間變量。
代理對象是在NativeScript中保持JavaScript對象與本地平臺對象的人工映射。例如,讓咱們來看看前面代碼的下一行:
1
2
|
var
time =
new
android.text.format.Time();
time.set( 1, 0, 2015 );
|
基於所生成的元數據,NativeScript知道代理對象上的全部方法。在這種狀況下,代碼調用Timer對象的set()方法時。此方法會再次調用V8及其功能回調; 而後NativeScript經過Android JNI轉發到Java時間對象上相應的方法調用。
這就是NativeScript在部分的工做原理。酷吧?
如今,還遺留下了一些很是複雜的部分,由於將Objective-C和Java對象轉換成JavaScript對象可能會很麻煩,尤爲是考慮到不一樣的繼承類型語言時。
咱們不打算深刻探討這些問題的細節,NativeScript的另外一個特色讓你沒必要深刻到本地代碼,好比:TNS模塊。
TNS modules是Telerik NativeScript modules的簡寫。跟Node模塊同樣,它一樣使用CommonJS。所以若是你已經會用require()和exports對象,那麼你就已經掌握了TNS模塊。
TNS模塊容許你將特定的本地調用抽象成平臺無關的API,NativeScript自己提供了幾十個這樣的模塊供您使用。舉個例子,假設您須要在您的iOS / Android應用程序建立的文件。你可能在Android中要寫如下代碼:
new java.io.File( path );
一樣在iOS裏寫下面的代碼:
1
2
|
NSFileManager.defaultManager();
fileManager.createFileAtPathContentsAttributes( path );
|
可是若是你使用TNS文件系統模塊,你的代碼將是同樣的,而沒必要擔憂的iOS / Android的內部實現細節:
var fs = require( "file-system" );var file = new fs.File( path );
更酷的是,你能夠本身寫TNS模塊。好比這裏有一個TNS模塊,檢索設備的操做系統版本:
1
2
3
4
5
|
// device.ios.jsmodule.exports = {
version: UIDevice.currentDevice().systemVersion
}
// device.android.jsmodule.exports = {
version: android.os.Build.VERSION.RELEASE
}
|
此代碼只檢索一個版本屬性,但它給你多少靈感?使用自定義TNS模塊是微不足道的,跟在nodejs中使用NPM模塊同樣:
若是你已經熟悉了npm的使用,NativeScript模塊很是容易編寫,分發和使用。就我的而言,做爲一個Web開發人員,原生的iOS和 Android代碼讓我懼怕,尤爲是當Java / Objective-C的API文檔扔在一塊兒的功能,它下降了咱們跨平臺開發的障礙。