另辟蹊径建立移动应用:iOS和Android代码共享

过去几年,移动应用席卷了整个世界,在工做和生活的方方面面改变着咱们使用互联网的方式。建立移动应用的各类技术也随之兴起,各类开发流程也 将移动应用视为一等公民,开始考虑适应移动开发的流程。尽管已经让人感受无处不在,真正的移动应用时代才刚刚开始。咱们即将面对新一代的移动设备,如可穿戴设备或组成物联网的各类各样的移动装置。咱们将面临全新的用于数据展现和命令接收的用户交互接口。咱们也认识到愈来愈多的公司将真正采起移动优先的战 略。全部的这些都将对咱们将来几年设计、开发和测试软件的方式产生巨大影响。 html

在苹果和安卓的应用商店中有成千上万各类用途的移动应用。iOS设备上的应用一般使用Objective-C工具库建立而成,而安卓设备上的应用则 基于Java语言。在这篇文章中咱们将向您展现两种不太经常使用的使用Java和Xtend构建原生应用的方法,这两种方法可以帮助开发者在两个应用平台上共享代码,从而简化开发工做。 java

使用Java和RoboVM开发原生iOS应用 android

将安卓和iOS两个平台同时做为目标平台的移动应用开发者常常面临不少挑战。在比较这两个平台的原生应用开发环境时,例如分别由谷歌和苹果提供的开 发工具链,很容易就可以发现这二者之间有着本质的区别。谷歌所提供的安卓开发环境是基于Eclipse集成开发环境和Java程序设计语言的。而由苹果所 提供的iOS开发环境则是基于Xcode集成开发环境和Objective-C程序设计语言的。 ios

这些平台间的差别致使代码没法重用。并且不多有开发者可以同时精通两个环境。最终就致使几乎每一个跨平台的移动应用都须要为各个平台准备单独的开发团队并使用单独的代码库。 git

RoboVM是一个新的开源项目,旨在不影响开发者和应用用户体验的前提下解决上述问题。RoboVM项目的目标是在iOS设备上使用Java和其 他JVM语言,如Scala,Clojure和Kotlin。与其余相似的工具不一样,RoboVM不会对开发者所使用的Java平台特性作任何限制,如反 射机制或文件I/O,而且还容许开发人员重用Java庞大的第三方库生态系统。RoboVM的独特之处还在于开发人员可以经过一个Java到 Objective-C的桥接器访问到完整的原生iOS API。这样,应用程序开发时,就可以用Java语言编写真正的原生用户交互界面而且可以获取到完整的硬件访问权限,同时使用的开发工具也是Java开发 人员所熟悉的Eclipse和Maven等。 github

使用RoboVM,进行跨平台开发将变得相对容易;同一组Java开发人员就有能力构建两个版本的移动应用程序而且代码库中的至关一部分代码都可以被共享。 web

如何开始? 数据库

RoboVM有多种调用方式,如命令行方式或使用Maven或Gradle,最容易上手的方式应该是使用RoboVM的Eclipse插件。 编程

配置要求 xcode

安装RoboVM的Eclipse插件以前,请确保系统知足以下要求:

  • 一台运行Mac OS X 10.9操做系统的Mac电脑。
  • Oracle Java SE 7 JDK。
  • 从Mac应用商店下载的Xcode 5.x集成开发环境。

须要注意的是,必须使用Oracle Java SE 7 JDK运行Eclipse。Eclipse没法正常运行在苹果的Java 6 JVM之上。

安装RoboVM的Eclipse插件

系统知足全部的先决条件以后,安装插件是一项很简单的工做。从Eclipse的Help菜单中打开Eclipse Marketplace,搜索RoboVM并点击Install Now便可。

或者也可使用以下更新站点

运行一个简单的iOS应用

接下来咱们将建立一个简单的iOS应用。首先建立一个新的项目:File => New => Project......。在列表中选择RoboVM iOS Project向导。

在Project Name,Main Class和App name栏中输入IOSDemo,在App id一栏中输入org.robovm.IOSDemo。其余栏目保持默认值点击Finish。

而后,建立一个新的名为IOSDemo的类文件,省略包名。将下面的代码拷贝粘贴到新建立的文件中,替换Eclipse自动生成的代码。

 
  1. import org.robovm.apple.coregraphics.*; 
  2. import org.robovm.apple.foundation.*; 
  3. import org.robovm.apple.uikit.*; 
  4.  
  5. public class IOSDemo extends UIApplicationDelegateAdapter { 
  6.  
  7.     private UIWindow window = null
  8.     private int clickCount = 0
  9.  
  10.     @Override 
  11.     public boolean didFinishLaunching(UIApplication application, 
  12.             NSDictionary launchOptions) { 
  13.  
  14.         final UIButton button = UIButton.create(UIButtonType.RoundedRect); 
  15.         button.setFrame(new CGRect(115.0f, 121.0f, 91.0f, 37.0f)); 
  16.         button.setTitle("Click me!", UIControlState.Normal); 
  17.  
  18.         button.addOnTouchUpInsideListener(new UIControl.OnTouchUpInsideListener() { 
  19.             @Override 
  20.             public void onTouchUpInside(UIControl control, UIEvent event) { 
  21.                 button.setTitle("Click #" + (++clickCount), UIControlState.Normal); 
  22.  
  23.                 } 
  24.             }); 
  25.  
  26.             window = new UIWindow(UIScreen.getMainScreen().getBounds()); 
  27.             window.setBackgroundColor(UIColor.colorLightGray()); 
  28.             window.addSubview(button); 
  29.             window.makeKeyAndVisible(); 
  30.  
  31.             return true
  32.        } 
  33.        
  34.        public static void main(String[] args) { 
  35.             NSAutoreleasePool pool = new NSAutoreleasePool(); 
  36.             UIApplication.main(args, null, IOSDemo.class); 
  37.             pool.close(); 
  38.        } 

最后,右键点击刚刚建立的项目,选择Run As... =>iOS Simulator App (iPhone),在iOS模拟器中启动应用程序。这样,应用程序就运行在一个模拟的iPhone上了。

若是须要在真实的设备上运行应用程序,须要使用Run As... =>iOS Device App选项。须要注意的是,执行这一选项以前,所用到的设备须要进行相应的设置。设置的过程超出了本文的讨论范围。详细信息请参照苹果的官方文档

建立用于应用商店发布的IPA文件

若是应用商店的发布证书(Distribution Certificate)和应用描述文件(Provisioning Profile)已经准备稳当,建立用于提交到应用商店的IPA软件包只须要在Eclipse中右键点击RoboVM iOS项目,选择RoboVM Tools=>Package for App Store/Ad-Hoc distribution… ,填写对话框中相关信息便可。

完成上述操做后,将会在目标文件夹中生成一个后缀为.IPA的文件。使用Application Loader便可验证生成的IPA文件并将其提交到应用商店中。使用Spotlight能够很方便地定位到Application Loader应用。

从苹果网站上可以找到不少关于如何加入iOS开发者计划及如何建立用于应用商店发布的证书和应用描述文件的资源

底层实现机制

字节码编译器

RoboVM的核心是它的预编译器。预编译器能够经过命令行或者Maven、Gradle等构建工具或IDE调用。它能够将Java字节码翻译成用 于特定操做系统和CPU型号的机器码。通常来讲会翻译成用于iOS系统和ARM处理器的机器码,不过RoboVM也支持将字节码转为运行在x86 CPU(32位)上的Mac OS X和Linux系统的机器码。

这种预编译的方法与Oracle Hotspot之类的传统JVM的工做机制有很大的区别。这些JVM一般会在运行时读取Java字节码,而后以某种方式执行包含在字节码的虚拟机指令。为 了加快这一进程,JVM采用了一种被称为即时编译的技术。简单来讲,这个过程会在程序第一次调用某个方法时,将这一方法的虚拟机指令翻译成当前系统所用的 CPU型号对应的机器码。

因为苹果内置于iOS中的技术限制,在iOS应用中使用任何形式的即时编译技术都是不可能的。惟一的替代方案就是使用解释器或像RoboVM中所用 的预编译技术,而解释器这种方式速度很慢而且十分耗电。预编译的过程发生在使用开发者的机器进行编译的时候,在iOS设备上运行时,生成的机器码就可以全 速运行,所以在速度上能够与由Objective-C编译生成的代码媲美,甚至可能会更快一些。

因为RoboVM预编译器消费Java字节码,而不是Java源代码,所以至少在理论上能够用于任何可以编译成字节码的JVM语言。目前已知的 RoboVM预编译器可以正常工做的JVM语言有Scala,Clojure和Kotlin。这种方法的另外一个好处是,可在无需任何原始源代码的状况下, 在标准JAR文件中的第三方库上使用RoboVM,这样就能够在应用中使用专有的和闭源的库。

增量编译

即便是很是简单的RoboVM应用,例如IOSDemo应用,在第一次启动时,都须要耗费较长一段时间。RoboVM编译器编译应用的过程是从应用 的main类开始。而后编译main类所用到的全部的类,以后再编译前面的类所用到的全部类,如此循环,直到应用所需的全部类均完成编译为止。在这一过程 中,标准的运行时类,如java.lang.Object和java.lang.String,也包含在编译的范围内。这是一个一次性的过程。 RoboVM会缓存已经编译过的类,只有一个类或与它有直接依赖关系的类已经发生了改变时,才会从新编译这个类。

增量编译和缓存目标文件的好处在于可以减小编译所耗费的时间。在生成的可执行文件中仅包含可以从Main类触及到的类能够下降可执行文件的大小。不 过,在某些状况下(如经过反射机制加载类时),RoboVM编译器没法决定是否应该对某个类进行编译。不过,能够给编译器下达指令,显式地将某个特定的类 或者全部符合某个条件的类包含在编译范围内。

基于安卓的运行时类库

任何的JVM虚拟机都须要运行时类库。这个类库为全部的Java程序提供标准的包和类,如java.lang.Object和 java.lang.String。RoboVM的运行时类库来自于安卓开源项目和已经被移植到RoboVM的非安卓专用的包中。这就意味着若是Java 或JVM代码中只用到了安卓标准包中的类,那么这些代码直接就可以在RoboVM上正常运行。

RoboVM现状

RoboVM目前仍在开发过程当中,不过已经能够基本使用。1.0版本预计将在2014年年末以前发布。

在苹果应用商店中已经有至少50个基于RoboVM的应用。已知应用程序的最新列表请参见这里

目前为止,大概有50%左右的iOS API可用在基于RoboVM的iOS应用中。在RoboVM的Wiki上能够查看到关于这些绑定的当前状态列表。截至如今,RoboVM上已经可以运行由Scala,Clojure和Kotlin所编写的代码。

关于RoboVM的文档,目前仍在完善过程当中。在2014年晚些时候,1.0版本发布时,将会有较为完备的文档说明。

RoboVM的应用目前仍没法进行Debug。这一问题也将在今年的晚些时候解决。

限制

RoboVM只可以将已经完成预编译的类加载到应用中。这就意味着在RoboVM应用中,没法在运行时使用自定义的类加载器动态建立字节码并将其加载到应用中。也就是说,RoboVM没法支持运行时建立或修改类的技术。

更多相关信息

使用Xtend建立安卓应用

Xtend简介

Xtend 1是一种能够编译成可读Java源码的静态类型程序设计语言。这种语言自己是同类设计中的最佳典范,特别是在可 读性和强大的可扩展性方面,不过它也让Java的互操做性问题显而易见。这种语言鼓吹函数式编程风格和多分派、扩展方法、拉姆达表达式及编译期宏等特性。 与其余的Java替代品不一样,Xtend自己并不包含庞大的标准库,而只是在标准的JDK上添加了一些扩展方法。Xtend还能够保证避免Java互操做 性问题的出现而且可以提供强大的IDE支持。

为何安卓上的Java如此难用

Java代码每每十分冗长,特别是在安卓操做系统上。因为Android API的级别很低并且常常出现没有通过充分定义的类型(处处都是int类型)。另一个烦恼就是无处不在的XML文件的使用和绑定。因为Android上 还没有支持Java 8,咱们还不得不仔细阅读无处不在的匿名类。并且不幸的是,Java没法修剪代码以加强可读性,咱们只能将代码与多余的符号、类型信息和样板习语 (boilerplate idioms)混杂在一块儿。

安卓对JVM语言的最低要求

Java语言在安卓上的替代品必需要可以保证不增长任何运行时的系统开销,这就将全部的动态语言排除在外。另外,也不但愿出现任何没必要要的间接类型 转换。例如,代码中应该只使用Java和安卓类型,不该由于互操做性问题而须要来回转换。这不只是处于性能方面的担忧,在调试时也比较使人烦恼。最后,安 卓系统限制每一个应用只可以使用65536个方法。所以,寻找Java的替代品时,必定不能在应用中添加大的标准SDK,由于这样会大大减小开发人员所能使 用的方法数量。举例来讲,使用Groovy的SDK会增长8000多个方法。

Xtend——安卓开发的完美解决方案?

Xtend可以转化成地道的Java源代码,而且基本上只依赖于JDK和安卓系统的类。在运行时,也没有间接寻址、转换或者其余任何额外的开销。也 就是说,Xtend代码可以和Java的源代码有着基本一致的运行速度。另外,Xtend还包含一个通过精简的为安卓系统提供的运行时库,只有275kb 大小而且几乎包含了你所须要的一切。Xtend Eclipse插件与ADT(安卓开发工具)的整合也至关完美,对于新的安卓构建系统3,甚至还提供了相应的Gradle插件2。接下来就让咱们详细了解一下如何使用Xtend改善典型的安卓代码。

Hello安卓!

与往常同样,咱们先看一个简单的Hello World示例程序:

 
  1. class HelloWorldActivity extends Activity {  
  2.  
  3.    override protected void onCreate(Bundle savedInstanceState) {  
  4.       super.onCreate(savedInstanceState)  
  5.     
  6.       val button = new Button(this
  7.       button.text = "Say Hello!"   
  8.       button.onClickListener = [  
  9.          Toast.makeText(context, "Hello Android from Xtend!", Toast.LENGTH_LONG).show  
  10.       ]  
  11.       val layout = new LinearLayout(this)  
  12.       layout.gravity = Gravity.CENTER  
  13.       layout.addView(button)  
  14.       contentView = layout  
  15.   }  
  16. }  

对于Java开发者来讲,这个例子使用了类Java的编程风格,所以第一眼看上去会很是熟悉。另外,你可能会注意到,示例中所用的API 100%来自于安卓SDK和JDK。

主要的区别在于:

  • 没有分号(分号是可选的)
  • 使用setter和getter访问对象属性
  • 属性的默承认见性(如,类默认是共有的)
  • 使用拉姆达表达式替代匿名类

在语言的特性方面,有不少地方能够深刻探讨,不过在此以前,先让咱们看一下如何将Xtend编译器与相应的Android构建过程整合在一块儿。

使用Gradle进行构建

对于目前最经常使用的三个构建系统:Maven,Gradle和Ant,Xtend都有相应的插件支持。谷歌最近为安卓项目引入了新的基于Gradle的构建系统。接下来咱们看一下使用Gradle构建咱们的“Hello World”项目须要作哪些工做。

本文假设你已经在系统中安装了最新版本的Gradle和安卓SDK而且正确的设置了ANDROID_HOME环境变量。同时,你已经将Gradle的/bin目录添加到了PATH环境变量中。

接下来须要将构建脚本“build.gradle”添加到你的Eclipse Android项目的根目录下,build.gradle文件样例以下:

 
  1. buildscript { 
  2.    repositories { 
  3.       mavenCentral() 
  4.    } 
  5.    dependencies { 
  6.       classpath 'com.android.tools.build:gradle:0.8.+' 
  7.       classpath 'org.xtend:xtend-gradle-plugin:0.1.+'   
  8.    } 
  9.  
  10. apply plugin: 'android'  
  11. apply plugin: 'xtend-android'  
  12.  
  13. repositories {  
  14.   mavenCentral()  
  15. }  
  16.  
  17. dependencies {  
  18.   compile ('org.eclipse.xtend:org.eclipse.xtend.lib:2.6.+')  
  19. }  
  20.  
  21. android {  
  22.    compileSdkVersion 19  
  23.    buildToolsVersion "19.1.0" 
  24.    sourceSets {  
  25.       main {  
  26.          manifest {  
  27.             srcFile 'AndroidManifest.xml'  
  28.          }  
  29.          java {  
  30.             srcDir 'src'  
  31.          }  
  32.          res {  
  33.             srcDir 'res'  
  34.          }  
  35.          assets {  
  36.             srcDir 'assets'  
  37.          }  
  38.          resources {  
  39.             srcDir 'src'  
  40.          }  
  41.          aidl {  
  42.             srcDir 'src'  
  43.          }  
  44.       }  
  45.    }  
  46. }  

其主要工做就是导入并调用Maven和Xtend的构建插件。此外,咱们将运行时库添加到项目中并告知Android插件咱们正在使用 Eclipse风格的项目布局。上述工做完成后,在命令行窗口中进入项目的根目录并运行“gradle build”,Gradle将为你完成剩余的全部工做。

深刻Xtend

除了语法糖以外,Xtend还附带了许多很是有用的语言特性,例如操做符重载,模板表达式和switch表达式。并且还能够经过结合不一样的功能建立 新的特性。例如,假如你须要动态的UI,不能用静态的XML文件构建,而须要声明式的编写。Xtend为开发者提供了构建器语法(builder syntax)的支持。“Hello World”实例的UI实现代码以下:

 
  1. import static extension com.example.helloworld.UiBuilder.*  
  2.  
  3. class HelloWorldActivity extends Activity {  
  4.  
  5.    override protected void onCreate(Bundle savedInstanceState) {  
  6.       super.onCreate(savedInstanceState)  
  7.  
  8.       contentView = linearLayout [  
  9.          gravity = Gravity.CENTER  
  10.          addButton("Say Hello!") [  
  11.             onClickListener = [  
  12.                Toast.makeText(context,  
  13.                               "Hello Android from Xtend!",  
  14.                               Toast.LENGTH_LONG).show  
  15.             ]  
  16.         ]  
  17.     ]  
  18.  
  19.   }  

linearLayout(Context ctx, (LinearLayout)=>void initializer) 和button(ViewGroup group, String name, (Button)=>void initializer) 两个方法做为扩展被引入到Activity中。这两个方法将拉姆达函数做为其参数之一。传入拉姆达函数中的参数被称为implicit it,与this相似,implicit it不须要显式地解引用。如上所示,拉姆达函数,扩展方法和implicit it结合使用可以产生很是漂亮的构建器语法。经过Xtend也能够构建许多其余漂亮的API,从而以一种易读的声明式的方式编写代码。

来自于XML地狱的问候!

安卓开发者的至关一大部分平常工做就是配置和开发各类XML文件,用做国际化字符串的资源或用于各种视图的声明。安卓平台推荐使用XML文件,由于 平台已经为开发者提供了针对大型设备和SDK碎片化的解决方案。然而应用程序最终不可能只由静态视图和数据组成。开发者须要将全部的素材组合并为其赋予生 命。在安卓平台,经过R类来完成这些工做。这个自动生成的类包含了许多对应在XML文件中声明的各类元素的整型常量。假设一个视图XML文件中声明了以下 两个元素,点击Button能够更新TextView中的消息:

 
  1. <TextView android:id="@+id/message_view" 
  2. android:layout_weight="1" 
  3. android:layout_width="0dp" 
  4. android:layout_height="wrap_content" 
  5. android:hint="@string/empty" > 
  6. <Button 
  7. android:layout_width="wrap_content" 
  8. android:layout_height="wrap_content" 
  9. android:onClick="sayHello" 
  10. android:text="@string/hello_world" > 
  11. </Button>  

典型的安卓式开发方法是经过R类中生成的常量获取到TextView的控制权而后实现onClick的回调方法“sayHello”:

 
  1. class HelloWorldActivity extends Activity {  
  2.  
  3.    TextView messageView  
  4.  
  5.    override protected void onCreate(Bundle savedInstanceState) {  
  6.       super.onCreate(savedInstanceState)  
  7.       // set the view using the int constant  
  8.       contentView = R.layout.main  
  9.       // get a handle on the TextView  
  10.       messageView = findViewById(R.id.message_view) as TextView  
  11.    }  
  12.  
  13.    /**   
  14.     * Callback automagically called by Android   
  15.     */  
  16.    def void sayHello(View v) {  
  17.      messageView.text = "Hello Android from Xtend!"  
  18.    }  
  19. }  

上面一段安卓的典型代码中包含了不安全的类型转换,命名规范和各类样板文件。用Xtend咱们可以作得更好。

你好,Xtendroid!

Xtendroid4是一个专门为安卓开发提供类库以及所谓的积极注解(active annotation)的小型项目。积极注解能够理解为编译时的宏,它可以参与到从Xtend到Java转化的编译过程当中。你能够随意修改被注解的类,生成附加类型或使用这个钩子读写纯文本文件。

这样只要有一个注释,咱们就知道要绑定哪一个视图而且注释还能够帮助咱们生成样板文件。除此以外,它还可以提供类型安全的元素访问方法和回调方法。下面一段代码就是用Xtendroid的@AndroidActivity注释编写的Activity类。

 
  1. @AndroidActivity(R.layout.main) class HelloWorldActivity {  
  2.  
  3.    /**   
  4.     * Type safe callback   
  5.     */  
  6.    override void sayHello(View v) {  
  7.       messageView.text = "Hello Android from Xtend!"  
  8.    }  
  9. }  

如今,这个Activity中只包含了咱们想要加入的行为。其余的设置都是自动实现的,例如设置管道绑定、内容视图或扩展Activity的样板文件。并且如今一切都是类型安全的,IDE可以了解其中的前因后果并为开发者提供适当的自动完成建议。

此外,Xtendroid还可以为开发者处理JSON对象,资源文件或SQLite数据库提供便利。并且,积极注解以库的形式存在,所以经过自行开发或定制化已有库的方式,开发者能够很容易地构建更适于本身的库。

从下方1下载Eclipse并使用更新站点5安装ADT就能够开始本身亲自尝试上面所讲的内容。Xtendroid项目包含许多相似本文中所展现的示例。最后祝你们能从中找到乐趣。

  1. Eclipse Xtend
  2. Xtend Gradle 插件
  3. Android Gradle 插件
  4. Xtendroid
  5. ADT 更新站点

关于做者

Niklas Therning是开源项目RoboVM的建立者和的联合创始人——RoboVM项目的主要贡献者。他把如何合理地将Java引入iOS平台做为其使命。开始RoboVM项 目前,Niklas参与建立了SpamDrain反垃圾邮件服务,而且做为其承包商,主要从事Java EE和web应用程序开发的工做。Niklas持有位瑞典哥德堡查尔姆斯理工大学的计算机科学理学硕士学位。能够经过Twitter帐号@robovm关注他。

Sven Efftinge是一个充满激情的软件开发人员,他喜欢风筝冲浪运动、音乐和美食。他是Xtext项目的领导人。Xtext是一个程序设计语言、领域特定语言和JVM静态类型程序设计语言Xtend的开发框架。在位于Kiel的itemis公司,Sven领导一个研究部门。

过去几年,移动应用席卷了整个世界,在工做和生活的方方面面改变着咱们使用互联网的方式。建立移动应用的各类技术也随之兴起,各类开发流程也 将移动应用视为一等公民,开始考虑适应移动开发的流程。尽管已经让人感受无处不在,真正的移动应用时代才刚刚开始。咱们即将面对新一代的移动设备,如可穿 戴设备或组成物联网的各类各样的移动装置。咱们将面临全新的用于数据展现和命令接收的用户交互接口。咱们也认识到愈来愈多的公司将真正采起移动优先的战 略。全部的这些都将对咱们将来几年设计、开发和测试软件的方式产生巨大影响。

译文原文:另辟蹊径建立移动应用

查看英文原文:Unusual Ways to Create a Mobile App

相关文章
相关标签/搜索