另闢蹊徑建立移動應用: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

相關文章
相關標籤/搜索