【转】NativeScript的工作原理:用JavaScript调用原生API实现跨平台
原文:https://blog.csdn.net/qq_21298703/article/details/44982547
------------------------------------------------------------------------
注* 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应用程序的代码:
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”。
我知道你已经很激动了,先不要慌,让我们再来看看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的警告框。
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虚拟机是很重要的,但这只是实现的第一步,让我们来看看上文示例的第一行代码:
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运行原理几乎一样。
NativeScript是怎样管理JavaScript虚拟机的
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到底是干 什么的,然后调用?
Metadata(元数据)
该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
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模块一样:
- var device = require( "./device" );
- console.log( device.version );
如果你已经熟悉了npm的使用,NativeScript模块非常容易编写,分发和使用。就个人而言,作为一个Web开发人员,原生的iOS和 Android代码让我害怕,尤其是当Java / Objective-C的API文档扔在一起的功能,它降低了我们跨平台开发的障碍。
【转】NativeScript的工作原理:用JavaScript调用原生API实现跨平台的更多相关文章
- phonegap+cordova+ionic调用原生API
上一篇博客讲了phonegap+cordova+ionic的环境搭建,今天再来分享一篇cordova调用原生API的文章.从技术角度上来讲,这并不是很难,只是有些细节要是没有注意,或者某些步骤不知道的 ...
- vue 开发系列(五) 调用原生API
概要 我们在开发手机端程序的时候了,我们经常需要使用到拍照,二维码的功能.数字天堂公司提供了大量的原生API支持. http://www.html5plus.org/doc/ 实现 1.在hbuild ...
- iOS的WebView中使用javascript调用原生的api
1. 首先在javascript中加入相关代码 $('.content .saveCode').on('touchstart', function () {//touchstart if (temp ...
- 【Chrome】如何在C++中增加给JavaScript调用的API
本文示例说明了如何在Chrome浏览器中增加JavaScript API.为了简化,先假设是在已有的namespace中增加一个新的API,文章的最后将指出如果增加一下全新的namespace所需注意 ...
- Ionic Cordova 调用原生 Api 实现拍照上传 图片到服务器功能
Ionic 调用 Device 设备 Api 获取手机的设备信息 1. 找到对应的Api: https://ionicframework.com/docs/native/device/ 2. 安装相关 ...
- NativeScript工作原理
NativeScript是一个runtime,它提供一些机制可以使用JavaScript构建原生的IOS.Android甚至WP(未来会加入)应用.NativeScript有很多非常酷的功能,比如MV ...
- How Javascript works (Javascript工作原理) (一) 引擎,运行时,函数调用栈
个人总结:该系列文章对JS底层的工作原理进行了介绍. 这篇文章讲了 运行时:js其实是和AJAX.DOM.Settimeout等WebAPI独立分离开的 调用栈:JavaScript的堆内存管理 和 ...
- JVM工作原理
作为一种阅读的方式了解下jvm的工作原理 JVM工作原理和特点主要是指操作系统装入JVM是通过jdk中Java.exe来完成,通过下面4步来完成JVM环境. 1.创建JVM装载环境和配置 2.装载JV ...
- java中jvm的工作原理
首先我们安装了jdk和jre,但是jdk是为java软件开发工程师而使用的开发工具,我们运行java项目只要含有jre文件即可.对于jvm是内存分配的一块区域,我们知道,当我们开始使用java命令时, ...
随机推荐
- 深入理解python中的select模块
简介 Python中的select模块专注于I/O多路复用,提供了select poll epoll三个方法(其中后两个在Linux中可用,windows仅支持select),另外也提供了kque ...
- C# NPOCO 轻量级ORM框架(进阶)
继续翻译NPOCO wiki. 这篇将home上 下面的几个页面翻译. wiki地址:https://github.com/schotime/NPoco/wiki 上一篇: http://www.cn ...
- BlocksKit(2)-DynamicDelegate
BlocksKit(2)-DynamicDelegate 动态代理可以说是这个Block里面最精彩的一部分了,可以通过自己给一个类的的协议方法指定对应的block来实现让这个协议的回调都直接在bloc ...
- 【转】说下lua使用场景
[今日话题]说下lua使用场景 – flea 1. 我们有用,一些逻辑相对简单,没有复杂的数据交互,访问频次超高的接口实现,可以用lua,省得用phpfpm,太重,浪费资源. – 付坤 2. 也可 ...
- [USACO5.5]Hidden Password
题目大意: 求字符串最小表示. 思路: 本来按照lbn187的课件,知道SAM可以求字符串最小表示. 然而他并没有提供例题,就自己找了一道做. 大体思想就是把字符串复制一遍接在后面,构建SAM,然后每 ...
- Sublime Text2 默认语言(windows/unix)设置,Sublime插件大全
Sublime默认系统语言设置 Sublime Text 2默认使用的就是UTF8,这个UTF8模式使用的是不带BOM的,如果要修改这个配置,到Perference->Settings-User ...
- android 从零单排 第一期 按键显示helloworld
啦啦啦- 我是qscqesze 今天开始android的从零单排啦啦啦- 首先从最简单的开始 要求: 程序运行后,单击屏幕上的按键后可以显示一句话,如“Hello World!” 这是一个最基础最基础 ...
- 读书笔记_Effective_C++_条款三十一:将文件间的编译依存关系降至最低(第三部分)
下面来谈谈书中的第二部分,用Interface Classes来降低编译的依赖.从上面也可以看出,避免重编的诀窍就是保持头文件(接口)不变化,而保持接口不变化的诀窍就是不在里面声明编译器需要知道大小的 ...
- This seems to be a pre-built javascript file. webpack报这个警告怎么办?
增加 module.noParse 进行解决 例如: { resolve: { alias: { 'react': 'my/react/path' } }, module: { noParse: [/ ...
- C#把文字转换成声音
在System.Speech命名空间下,SpeechSynthesizer类可以把文字读出来,一起来玩下~~ 首先在Windows窗体项目中引入System.Speech.界面部分: 后台代码也很简单 ...