2018/5/18 15:03:33当前位置推荐好文程序员浏览文章

NativeScript是最近推出的一个跨平台处理方案,能让你能使用JavaScript来直接写Android、iOS本地应使用程序,未来还即将扩展到Windows平台。是最近比较受关注的项目。它与nw (原名node-webkit ,使用Web写winodw/linux桌面应使用)和phonegap内嵌webview写APP的实现方式有着本质的不同,它直接使用JavaScript调使用系统原生API,因此有少量原生应使用的特点。

NativeScript

NativeScript是一个运行环境,能让你用通使用的JavaScript代码,打造原生的iOS,Android和Windows(即将推出)应使用程序。 NativeScript有很多很酷的功可以,比方支持JavaScript对象双向绑定到原生UI组件,以及使用CSS为原生应使用程序写样式。但我最喜欢的功可以是NativeScript能让您直接访问本地平台的原生API。

注 能了解为NativeScript是一个JavaScript V8运行环境的命令转发代理商,将JavaScript调使用转发给不同平台上的原生API如Android、iOS,以及即将支持的Windows。

例如,看看这个NativeScript写的Android应使用程序的代码:

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”。

我知道你已经很激动了,先不要慌,让我们再来看看iOS的代码:

var alert = new UIAlertView();

alert.message = "Hello world!";

alert.addButtonWithTitle( "OK" );

alert.show();

这段JavaScript代码实例化一个Objective-C UIAlertView类,设置它的信息属性,而后调使用它的addButtonWithTitle()和show()方法。当您运行这段代码,你会看到hello word的警告框。

NativeScript运行时

该NativeScript运行环境看起来可可以像变魔术一样,不论你信不信,该架构并不是那么的复杂。一切从JavaScript虚拟机开始,NativeScript从这里开始执行JavaScript指令。具体来说,NativeScript在Android采使用v8;在iOS上采使用JavaScriptCore。因为NativeScript用JavaScript虚拟机,你访问原生API的所有JavaScript代码,依然需要遵守JavaScript的语法结构和规范。

一般来说,NativeScript会同时采使用V8和JavaScriptCore的最新稳固版;因而NativeScript对ECMAScript语言的支持在iOS的桌面Safari上面几乎是相同的,并且NativeScript在Android上面也几乎与桌面浏览器相同。包括少量ES6的新语法。

理解NativeScript用了JavaScript虚拟机是很重要的,但这只是实现的第一步,让我们来看看上文示例的第一行代码:

var time = new android.text.format.Time();

在NativeScript Android运行环境中,该代码会被编译(JIT)并在V8中执行。这我们可可以会很容易了解变...:

var x = 1 + 2;

但是接下来的问题是...V8怎样知道什么是android.text.format.Time()呢?

我们将重点讲V8和Android的实现,基本架构模式同样适使用于iOS上的JavaScriptCore。假如出现显着差别,本文会提到。

这里将不探讨NativeScript在Windows上的实现细节,由于处理方案可可以还会变,但是,当前的Windows实现跟iOS上的JavaScriptCore运行原理几乎一样。

NativeScript是怎么管理JavaScript虚拟机的

V8知道android是什么,由于NativeScript在运行时进行了注入,由于V8拥有一堆让你配置JavaScript环境的API。在JavaScript中您能用自己设置的C++代码来分析CPU用率,管理的JavaScript垃圾收集,等等一大堆API

面对这些API的是几个“Context”类,能让你操纵全局变量,从而有可可以为NativeScript注入一个全局的android对象。这实际上用了与Node.js的相同运行机制,使全局API可使用 - 如 require() - NativeScript用它注入能让你访问本地代码的API。 JavaScriptCore的也有相似的机制。酷吧?

让我们回到我们的代码:

var time = new android.text.format.Time();

现在你知道这个代码在V8中运行时,而V8已经知道什么是android.text.format.Time()了,由于NativeScript注入了必须的对象到全局范围。但仍存在着少量大的悬而未决的问题,如何让NativeScript明白那些注入的API究竟是干什么的,而后调使用?

Metadata(元数据)

对于NativeScript,反射是让NativeScript能调使用每个平台上的API的基石。包括android.text.format.Time。由于从性可以角度来看重构这些API是很困难的,NativeScript会提前做掉这些,并在Android/iOS预编绎过程中嵌入预先生成的元数据。

考虑到这一点,让我们再次回到我们的代码:

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对象与本地平台对象的人工映射。例如,让我们来看看前面代码的下一行:

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

TNS modules是Telerik NativeScript modules的简写。跟Node板块一样,它同样用CommonJS。因而假如你已经会使用require()和exports对象,那么你就已经掌握了TNS板块。

TNS板块允许你将特定的本地调使用笼统成平台无关的API,NativeScript本身提供了几十个这样的板块供您用。举个例子,假设您需要在您的iOS / Android应使用程序创立的文件。你可可以在Android中要写以下代码:

new java.io.File( path );

同样在iOS里写下面的代码:

NSFileManager.defaultManager();

fileManager.createFileAtPathContentsAttributes( path );

但是假如你用TNS文件系统板块,你的代码将是一样的,而不必担心的iOS / Android的内部实现细节:

var fs = require( "file-system" );

var file = new fs.File( path );

更酷的是,你能自己写TNS板块。比方这里有一个TNS板块,检索设施的操作系统版本:

// device.ios.js

module.exports = {

    version: UIDevice.currentDevice().systemVersion

}

// device.android.js

module.exports = {

    version: android.os.Build.VERSION.RELEASE

}

此代码只检索一个版本属性,但它给你多少灵感?用自己设置TNS板块是微不足道的,跟在nodejs中用NPM板块一样:

var device = require( "./device" );

console.log( device.version );

假如你已经熟习了npm的用,NativeScript板块非常容易编写,分发和用。就个人而言,作为一个Web开发人员,原生的iOS和Android代码让我害怕,尤其是当Java / Objective-C的API文档扔在一起的功可以,它降低了我们跨平台开发的障碍。

网友评论