Kotlin 語言高級安卓開發入門

過去一年,使用 Kotlin 來爲安卓開發的人愈來愈多。即便那些如今尚未使用這個語言的開發者,也會對這個語言的精髓產生共鳴,它給如今 Java 開發增長了簡單而且強大的範式。Jake Wharton 在他的 Øredev 的討論中,提到了 Kotlin 是如何經過提高安卓開發的語言特性和設計模式來解決這些嚴重的問題,經過這些方法你能夠清除那些無用的 API 還有無效代碼。充分利用擴展特性來解決你的開發中的模板性代碼的問題!java

 
爲何要推廣這個語言?
 
好吧,大夥。歡迎來到這裏。咱們今天的主題是使用 Kotlin 語言的高級安卓開發 – 推廣咱們開發應用的語言。首先呢,咱們須要弄清楚咱們爲何要在安卓開發中更進一步?爲何咱們須要一些新的東西來推動安卓開發?
 
許多人都會提到的一個經典的緣由就是咱們如今被 Java 6-ish 困住了,Java 7 也差很少是一樣的情況。這被稱做是一片荒地。
 
javax.time 是 Java 8 才引進的新庫, JSR310。這個庫咱們在安卓上沒有,因此咱們被老的日期和日曆的 API 困住了,它們都很是容易出錯。
 
Java 8 的另一個功能 streams,安卓也沒有。咱們還缺乏其餘的語言特性,好比 lambdas,method references 和 不引用 outer class 的 anonymous classes。最後,咱們沒有 try-with-resources。Try-with-resources 不在 Java 7 裏面,而安卓在 API 19 中大部分都是 Java 7, 在 API 20 中又增長了點,但不是所有。因此只有當你的定義最小的 SDK 的時候,你才能用這些相關的功能。
 
Java 6 不是個問題 
 
儘管上面4個問題都看起來十分使人頭疼,可是事實上它們不是。關於 javax.time,爲 Java 8 SDK 編寫的大部分代碼大部分都可以以 ThreeTenBP 的形式向後兼容 Java 6,因此你能夠這樣使用。
 
關於 streams,Java 6 上也有一個向後兼容的移植或者一個經常使用的 Java 庫,RxJava 實現了一樣的概念,只不過用了一種略微不一樣的有爭議性的更強大的方式。
 
也有一個工具叫作 Retrolambda,它事實上也能夠運行 Java 8 bytecode 而且向後兼容了 lambdas 和 method references 到 Java 6。
 
最後,如我所說,若是你的最小的 SDK 是 19 的話,你可使用 try-with-resources,並且 Retrolambda 也會容許你這樣作。
 
這些關於 Java 6 是個問題的爭論都有工具去解決它們。固然,若是咱們能使用 Java 8 是最好的,可是這些替代方案都是通過實戰測試並且可以很好地工做的方案。因此上面的理由就不太算數了。不管如何,有兩個方面我想重點討論一下,由於這兩個方面沒有相似的解決方案。
 
Java 語言的限制和問題
 
Java 語言的一些限制和所帶來的一些問題,這些問題 Java 語言自己是不可避免的。例如,咱們不能給不是咱們本身寫的 types、classes 或者 interfaces 增長新的方法。長時間以來,咱們都會採用 util 類,雜亂無章地堆砌着咱們代碼或者或者揉在同一個 util package 裏面。若是這是解決方案的話,它確定不理想。
 
Java 語言的類型系統都有 null 的問題,在 android 上更爲明顯。由於它沒有對多是或不是 null 的類型完成 first-class representation。因此,被稱做 「billion dollar mistake」 的 null 指針異常最後會毀了你的應用。
 
接下來, Java 確定不是最簡潔的語言。這件事自己不是件壞事,可是事實上存在太多的常見的冗餘。這會帶來潛在的錯誤和缺陷。在這以前,咱們還要處理安卓 API 帶來的問題。
 
Android API 的設計問題
 
安卓是一個龐大的繼承系統;他們爲他們的繼承系統而感到自豪,對他們來講這個系統工做的也很是的正常,可是這個系統傾向於把問題推向應用開發者。並且,空引用的問題也回來了,這個問題在安卓系統中十分明顯,由於他們想讓系統更加有效些。null 被用在許多地方來表明值的缺失,而不是封裝成更高級的類型,好比一個類型或者可選項。
 
同時,回到語言的冗餘,安卓的 API 有着本身許多的範式。這也許是由於性能的緣由。設計者們最後寫出的這些 APIs 須要你,開發者,來作許多的事情來提升效率而不是用其餘的方法抽象它們。
 
因此說 Java 語言和安卓 API 是驅動咱們轉向相似 Kotlin 語言的兩個重要的因素。這不是說 Java 6 沒有問題,由於 Kotlin 解決了 Java 6 的許多問題,可是我不認爲那是 Kotlin 做爲一個替代方案的充分緣由,由於其餘的方案也能夠帶來一樣的好處。
 
Kotlin 入門 
 
Kotlin 是公司 JetBrains 研發的語言。市面上爲各類語言開發的 IDE 不少,可是 Intelli J 平臺是 Android Studio 的基礎。在他們的網站上,他們這樣描述 Kotlin :
 
爲 JVM、Android 和瀏覽器而生的靜態編程語言。
它的目標是 JVM、安卓和 Java 6 字節流。他們想在他們的語言裏增長這些特性,並且持續支持 Java 六、JVM 和安卓系統市場。他們特別關注和 Java 的相互調用,這點接下來會討論。
 
Kotlin 語法速成
 
網站上有許多的很棒的指導來學習語法,可是我仍是會很快地介紹一下,而後再解釋爲何這些語法有利於安卓開發。咱們以一個這樣的方法定義的語法開始。
 

[代碼]csharp代碼:

?
1
2
3
fun sum(a: Int, b: Int): Int {
   return a + b
}
咱們有一個 「fun」 的定義,這表明着函數。函數名和第一個要注意的事情是這和 Java 有明顯的不一樣。參數名的順序和參數的類型保留了下來 – 名字後面跟着類型。返回值類型在函數末尾聲明。沒有分號。
 
另一個有意思的事情是這個函數還能夠有單行描述,咱們事實上能夠不用大括號和 return,定義函數和表達式同樣。
 

[代碼]csharp代碼:

?
1
fun sum(a: Int, b: Int) = a + b
咱們接下來還會看到更簡潔的語法。這裏有另一個例子,這個看起來像一個 main 函數,若是你寫一個普通的 Java 應用的話:

[代碼]csharp代碼:

?
1
2
3
fun main(args: Array< string >) {
   println( "Args: $args" )
}</ string >
數組的語法不太同樣。可是處理得十分天然。 編譯後的字節碼會使用一個字符串的數組,可是在你的代碼裏卻把它處理地像一個普通的數組。它也支持字符串的插入;咱們能夠寫一個串,而後引用它其中的變量,而且能夠自動的替換其中的變量。
 
最後,看看變量:

[代碼]csharp代碼:

?
1
2
val name = "Jake"
val people: List< string > = ArrayList()</ string >
這裏我用一個叫作 「name」 的變量給一個字符串命名,並且這裏沒有類型定義。語言會自動解釋類型,由於它只多是串。它有 「val」 的前綴而 「Val」 是它的值,而且是個不能夠修改的值。若是咱們想修改它,咱們就須要用 「var」 做爲前綴定義變量。
 
這個 : List<String> 是一個看起來像在 field 上的類型,它接在名字後面,像一個方法。最後,當咱們調用構造函數的時候,咱們不須要使用 「new」 關鍵字。其餘的語法都是同樣的,就是不要 「new」。
 
Kotlin 語言特性 
 
讓咱們看看那些語言自己的特性,看看他們是如何幫助咱們構建安卓應用的。我指出過這些 util 類都是反設計模式的,並且它們會在你的應用裏愈來愈不受控制……
 
函數擴展 
 
Kotlin 有擴展函數的概念。這不是 Kotlin 語言獨有的,可是和其餘語言裏面咱們看到的擴展又不太同樣。若是咱們在純 Java 語言的環境下添加一個 date 的方法,咱們須要寫一個 utils 類或者 dates 類,而後增長一個靜態方法。它接收一個實例,而後作些事情,可能會返回一個值。
 

[代碼]java代碼:

?
1
2
3
4
5
static boolean isTuesday(Date date) {
   return date.getDay() == 2 ;
}
 
boolean tuesday = DateUtils.isTuesday(date);
這裏我增長一個十分有用的 「isTuesday」 函數給咱們的 date utils,而後咱們用傳統的靜態方法調用它。在我展示 Kotlin 語法前,我想和 C# 作個比較。這是在 C# 中咱們須要在 date 類中添加一個函數的實現, DateTime:

[代碼]java代碼:

?
1
2
3
4
static boolean IsTuesday( this DateTime date)
{
   return date.DayOfWeek == DayOfWeek.Tuesday;
}
這裏咱們獲得 date 的實例,而後調用這個方法,在任何 .NET 環境下這都能行得通,只要你在某處定義了該擴展方法,你能在你的整個項目中都能引用到 DateTime。我接下來會解釋一個有意思的語言特徵。這是 Kotlin 定義的方法:

[代碼]csharp代碼:

?
1
2
3
4
5
fun Date.isTuesday(): Boolean {
   return getDay() == 2
}
 
val tuesday = date.isTuesday();
在 Kotlin 中,咱們用咱們想增長的函數的方法的類型來給原有的函數名增長了一個前綴。咱們如今調用 Date.IsTuesday 而不是 isTuesday。而後你能在最後獲得返回值。咱們最後能調用 「getDay」 而且這個擴展的方法可以被調到,儘管咱們沒有使用實例來調用它。咱們的調用方式和該類原來就有有這個方法時調用的方式同樣。咱們也可以在 Date 上調用其餘的方法。
 
Kotlin 的一個很是好的功能是,它會自動地轉換有 getters 和 setters 綜合屬性的類型。因此我可以替換 getDay() 爲 day,由於這個 day 的屬性是存在的。它看起來像一個 field,可是其實是個 property – getter 和 setter 的概念融合在了一塊兒。
 
前面我指出的單行函數表達式會使得語法變得更簡潔,因此咱們能夠把上面的代碼修改爲下面的樣子,而且隱藏返回值:

[代碼]csharp代碼:

?
1
fun Date.isTuesday() = day == 2
如今咱們有一個很是漂亮的單行實現的而且在 date 上使用的擴展方法了。
 
不像 C#,若是咱們不在同一個 package 裏面的話,擴展函數須要顯示引用。若是不是同一個文件裏,咱們須要很是清楚地描述這個函數從何而來: import com.example.utils.isTuesday
 
這和 Java 的靜態方法 import 很是相似。咱們須要顯示聲明咱們調用的函數,這樣函數的來源就不會模糊。由於當我看到這段代碼時,做爲一個 Java 開發者,我知道 date 沒有 「isTuesday」 的函數,可是顯式的 import 告訴我它來自於公共的某個 util package。而在 C# 中,咱們不知道這個擴展函數從何而來。它可能來自一個庫,你的源代碼,或者其餘的地方,你沒法靜態地知道,除非你到 IDE 裏面找到它的定義。
 
固然,你能夠 command B 而後進入這個函數,這看起來像是 date 類型的一個方法。這和咱們在 Java 裏面編寫它,而後產生的二進制代碼如出一轍。並且,由於它關注 inter-op,你能夠從 Java 側使用一個自動生成類來調用它。
 
類型系統中的 Null 
 
我提過 null 會是個問題。Kotlin 事實上在它的類型系統裏從新表述了 null。對於 get string 可能返回 null 的函數來講,我會返回 String? 來表述這多是個 null 值。而後在 get string 函數中,我使用 double exclamation mark 語法來直接調用這個 null。這基本上是說,「我知道這多是個 null,因此把它變成一個普通字符串。」 當它真是 null 的時候,它會發出一個檢查的信號,而後拋出異常。
 
可是在消費者的代碼裏面,代碼每每是直接調用該函數,類型系統會生成一個帶有問號的字符串,而且傳遞到調用者的代碼處。這意味着若是你不首先作 null 檢查或者提早的默認處理機制,你就永遠不能解引用。這樣,它會最終解決消費者代碼中致使 null 指針異常的問題。
 
函數表達式入門 
 
另外,函數表達式也被稱做 lambdas 或者 closures。這裏有一個最簡單的函數表達式: { it.toString() }。它是一段代碼在 「it」 變量上調用了 two-string 函數。「it」 是個 built-in 的名字。當你在寫這些函數表達式的時候,若是你只有一個參數傳入這段代碼,你能夠用 「it」 引用,這只是一個你不須要定義參數的方法。
 
可是當你須要定義參數的時候,或者不止一個參數要定義的時候,語法就是這樣的: { x, y -> x + y }。咱們能夠建立一段代碼,一個函數表達式,輸入兩個參數,而後把它們相加。若是咱們願意,咱們能夠顯示定義類型。
 

[代碼]csharp代碼:

?
1
2
3
4
5
{ x, y -> x + y }
 
val sum: (Int, Int) -> Int = { x, y -> x + y }
 
val sum = { x: Int, y: Int -> x + y }
而後,在 fields 上存儲它們。如今你能夠看到在前二個例子中,類型是在 field 本上上定義的。這意味着函數表達式不須要任何類型信息。而後往下一個,類型信息在函數表達式裏面本身包含了,因此咱們不須要在變量定義時加入類型信息。
 
在最後一個例子中,返回值是推斷出來的。兩個整型相加,輸出只能是整型。因此你不須要顯示定義它們。
 
Higher-order 函數 
 
這是個新奇的術語,它指的是函數能夠接收函數,或者函數能夠返回函數。這裏是個例子

[代碼]csharp代碼:

?
1
2
3
4
5
6
func apply(one: Int, two: Int, func: (Int, Int) -> Int): Int {
   return func(one, two)
}
 
val sum = apply(1, 2, { x, y -> x + y })
val difference = apply(1, 2, { x, y -> x - y })
這裏咱們定義一個函數,接收兩個整型做爲參數。而後第三個參數是一個函數。這和咱們以前看到的語法同樣,那裏咱們定義了一個函數接收兩個整型並返回一個整型。
 
而後,在函數體內部,咱們調用了該函數,而且傳入了兩個參數。這就是咱們如何使用它來計算和或者其餘的東西的方法。咱們把這段代碼應用到了這兩個數字上。回到以前所說的地方,咱們說這段代碼知道如何相加或者相減,並且咱們把它運用到咱們傳入這個方法的數據上。而後這段代碼在本身合適的上下文環境中自動運行。
 
Kotlin 給你提供了一個手動的方式來調整這段代碼成爲更好的語法表達。若是函數的最後一個參數是個表達式,你一直都不須要使用括號,你能夠只用在全部參數初始化後添加一個。

[代碼]csharp代碼:

?
1
2
val sum = apply(1, 2) { x, y -> x + y }
val difference = apply(1, 2) { x, y -> x - y }
這項技術容許你建立很是漂亮的 DSL 和 API。你能夠像 Int 的擴展同樣來編寫它們。
 
包含 higher-order 函數的應用 
 

[代碼]csharp代碼:

?
01
02
03
04
05
06
07
08
09
10
11
12
fun <t> List<t>.filter(predicate: (T) -> Boolean): List<t> {
   val newList = ArrayList<t>()
   for (item in this ) {
     if (predicate(item)) {
       newList.add(item)
     }
   }
   return newList
}
 
val names = listOf( "Jake" , "Jesse" , "Matt" , "Alec" )
val jakes = names.filter { it == "Jake" }</t></t></t></t>
一個常見的操做是你有一個事情的列表,而後你須要基於一些條件來過濾它們。由於咱們有 extension methods 和 higher-order functions,咱們能夠在列表上寫一個這樣的函數。這和你指望的實現同樣,這個函數比較每一個列表中的元素而後返回正確的結果。
 
如今若是你有一個包含數據的列表,只需用一行代碼,如此簡潔的函數就能過濾出咱們想要的數據。謝天謝地,這個功能實際上已經在 Kotlin 標準庫裏面包含了,這樣咱們就不用擴展列表類了。
 
大部分 Kotlin 的庫都已經實現了現有的 Java 類型中相似的高級函數來,這樣能夠統一在這些類型上的操做。
 
看看這個在 Kotlin 標準庫裏面不存在的應用:

[代碼]csharp代碼:

?
1
2
3
4
5
fun Any. lock (func: () -> Unit) {
   synchronized ( this ) {
     func()
   }
}
除了同步阻塞代碼塊,咱們能夠用一個表達式來實現同步阻塞。 「Any」 是 Kotlin 的對象版本。全部的類型都是 「Any」 的子類型。咱們能夠對它增長一個方法,該方法輸入是一個函數,而後執行這個函數的代碼,這段代碼會同步該實例。若是咱們須要一樣的鎖,或者一些對象須要被鎖住,咱們能夠把這段代碼放在函數內部的鎖起來。而後咱們把它傳給每一個實例的這個方法中。這個方法簡化了調用者的代碼,並且清晰不少。
 
簡單清晰的鎖
 
另外一個十分酷的應用存在於咱們經常使用的鎖的情景中,咱們經常忘記在一些操做以前完成鎖的操做。咱們能夠寫這樣的類,這個類只容許你訪問你須要用鎖才能操做的資源。

[代碼]csharp代碼:

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
data class Lock<t>( private val obj: T) {
   public fun acquire(func: (T) -> Unit) {
     synchronized (obj) {
       func(obj)
     }
   }
}
 
val readerLock = Lock(JsonReader(stream))
 
// Later
readerLock.acquire {
   println(it.readString())
}</t>
在這個例子裏面,咱們建立了依賴流的 JsonReader。設想不論什麼緣由咱們須要多線程同時訪問它,這樣咱們就必須使用鎖,鎖會幫助咱們管理同步的問題。
 
而後在後面的代碼中,咱們調用了這個 acquire 的方法,它會在這個 JsonReader 的實例上實現同步,而後把它傳給咱們提供的函數。因此在這種狀況下,咱們會在這個 JsonReader 裏面再次析構,因而咱們須要使用鎖。但如今咱們的代碼根本沒有處理鎖的問題。咱們也沒有顯式的同步,也沒有爲 JsonReader 建立鎖。但是訪問 JsonReader 不使用鎖是不可能的。
 
避免 Kotlin 帶來的泄露
 
前面,我提到過關於 data 的代碼,定義了一個這樣的值:
 

[代碼]csharp代碼:

?
1
val notEmpty: (String) -> Boolean { !it.isEmpty() }
Kotlin 實現函數表達式的方法和在 Java 裏面使用類的方法是同樣的。好處是 Kotlin 並無建立對於外部範圍的引用,由於沒有這樣的類。這樣避免了可能的上下文泄漏。
 
咱們須要關心的就是傳入的數據。它會致使建立一個靜態的單例實例並且沒有引用。用這些函數表達式根本不可能產生上下文泄漏。可是在最上層,咱們兩個是如出一轍的。
 
擴展函數表達式的例子
 
擴展函數 – 給一個類型加入函數可是不修改原來的類型。
 
函數表達式 – 未定義的函數體被用做表達式(i.e.,date)
 
Higher-Order 函數 – 一個參數是函數或者返回是函數的函數。
 
擴展函數表達式是上述三個概念的綜合體,這是個強大的並且能夠建立清晰的 API 的方法。爲了說明這點,我將使用一個 databases 的 API 作爲例子,把它改形成一個更加整潔的、沒有無效代碼的 API。
 

[代碼]csharp代碼:

?
1
2
3
4
5
6
7
db.beginTransaction();
try {
   db.delete( "users" , "first_name = ?" , new String[] { "Jake" });
   db.setTransactionSuccessful();
} finally {
   db.endTransaction();
}
若是你想在一個 transaction 裏面執行一個 statement 的話,這是你必須寫的六行代碼。咱們開始一個 transaction,咱們把它放在 「try finally」 裏面,而後咱們標記這個 transaction 爲成功。若是它拋出或者不在 「finally」 裏面拋出異常,咱們須要結束 transaction.
這是一個容易帶來 bug 的代碼,容易健忘的代碼和應該重構的代碼。任何事情都有可能出現錯誤,在這裏你不當心交換了兩個事情,而後忽然,你就會有一個很難找到緣由的 bug。 或者它會在運行時 crash。
擴展函數表達式容許咱們解決這個問題。咱們如今能給 database 本身增長一個方法, 這將給現有的代碼構建一個防禦牆。
 

[代碼]csharp代碼:

?
01
02
03
04
05
06
07
08
09
10
11
12
13
fun SQLiteDatabase.inTransaction(func: (SQLiteDatabase) -> Unit) {
   beginTransaction()
   try {
     func( this )
     setTransactionSuccessful()
   } finally {
     endTransaction()
   }
}
 
db.inTransaction {
   it.db.delete( "users" , "first_name = ?" , arrayOf( "Jake" ))
}
開始一個 transaction,調用 「try」 代碼段,而後設置 transaction 爲成功,最後,結束它。咱們接收一個函數做爲參數,在使用這個小技巧後,咱們在 「try」 代碼段裏面執行該函數。在咱們的消費者代碼裏,咱們會調用這個咱們稱做 inTransaction 的方法,在這個方法裏面咱們寫的代碼都會在一個 transaction 裏面執行。你沒有別的辦法來錯誤地使用它了。
 
渠道化你的內部 SQLiteDatabase 
 
一個有趣的事情是,在這樣的實現方法下,你的代碼裏面仍是會須要 database 的引用。這也是可能會出錯的地方。若是你有兩個數據庫,你可能會在 transaction 裏面引用了那個錯誤的數據庫。
 
咱們有多種方法來解決這個問題。一個方法就是咱們可讓函數的參數包含須要發生 transaction 的數據庫的引用。咱們傳了 this 給函數,而後咱們就不須要調用 「db」,而是使用 「it」 了,它是傳給表達式的第一個參數。
 
這個方法仍是不太好。咱們阻止了潛在問題的發生,可是如今咱們每次訪問 database 的時候都須要調用 「it」。這裏有個有意思的事情是輸入參數是個函數。咱們能夠把這個函數改寫成 「SQLiteDatabase」 的一個擴展函數。
 
這將會使你有些迷惑,由於它是個瘋狂的強大的概念。經過把 func(this) 替換成 this.func(),傳入的函數參數變成了該對象的擴展函數,並且這個擴展函數已經存在 「SQLiteDatabase」 裏了。由於咱們並非真正須要 this,因此咱們拋棄了它。
 
這個函數會像在 SQLiteDatabase 裏面定義的那樣執行。若是咱們調用刪除方法,咱們不須要用任何東西來證實本身有資格調用,由於咱們就是個 SQLiteDatabase。如今咱們能夠不須要 it 了,因此每個你放在這個代碼段裏面的表達式都會看起來像 SQLiteDatabase 內部的一個私有方法同樣。你不須要證實本身是正確的,由於它老是能在正確的對象上執行本身。
 
這太棒了,它會把你的代碼改編成:

[代碼]csharp代碼:

?
1
2
3
4
db.inTransaction {
   it.db.delete( "users" , "first_name = ?" , arrayOf( "Jake" ))
}<font face= "Tahoma, Arial, Helvetica, snas-serif" ><span style= "font-size: 14px; white-space: normal;" >
</span></font>
避免額外的垃圾回收
 
這仍然是安卓的一個大問題。等效的 Java 代碼中,咱們建立了一個函數的新的實例,而後把它傳給了咱們產生的靜態方法。這很是不幸,由於在咱們順序執行代碼以前,咱們並無真正地分配內存。
 
若是咱們用 Java 來實現這個,咱們須要在每次使用這些函數表達式的時候都分配一些極小的函數對象。這很差,由於這會觸發許多的垃圾回收。
 
謝天謝地,咱們有辦法在 Kotlin 裏面來避免這個問題。咱們這裏的函數僅僅是個定義好的函數表達式。它使用函數表達式做爲入參,這會是個問題。那個函數表達式須要轉成一個匿名類。

[代碼]csharp代碼:

?
1
inline fun SQLiteDatabase.inTransaction(func: SQLiteDatabase.() -> Unit) { ... }
咱們能夠把它變成一個 in-line 的函數,這樣咱們就告訴了 Kotlin 編譯器不要把它做爲一個靜態函數調用,我須要編譯器僅僅把個人函數代碼替換到須要調用的地方。雖然這樣會產生不少的 bytecode,可是這樣產生的 Java 類文件會和容易出錯的 Java 代碼編譯出來的類文件同樣的。
 
經驗法則:函數表達式加上 in-line 函數和一樣實現的 Java 代碼如出一轍。如今咱們就能清理咱們想清理的任何 API 了,找到你安卓裏面最差的 API,這樣的 API 確定處處都是。這其中有不少是基於 transaction 的,因此咱們能夠爲 fragments 或者爲 shared preferences 使用一樣的模式。這是一個增長功能的好方法,並且不會對自動產生的代碼帶來額外開銷。
 
JetBrains 提供的 Anko 
 
JetBrains 的同事把這個思路用到了極致,而且建立了一個庫叫作 Anko。基本想法是 XML 描述性很是好,並且很適合定義 UI,由於它是分級的。當你建立 UI 的時候,它們也是具有分層特性的。
 
在這些函數表達式的幫助下,咱們能夠用一樣分層式方式來編寫代碼。固然咱們還可以引用其餘全部的方法,其餘的重構工具,和其餘的 Java 代碼的靜態分析工具來展現 UI。
 

[代碼]csharp代碼:

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
verticalLayout {
   padding = dip(30)
   editText {
     hint = "Name"
     textSize = 24f
   }
   editText {
     hint = "Password"
     textSize = 24f
   }
   button( "Login" ) {
     textSize = 26f
   }
}
這僅僅是擴展函數表達式的一個發展分支。它們會建立這些類的實例,把它們加到它們的父親那裏,設置合適的屬性。除了咱們在 XML 裏面使用的概念之外,好比 layout,你能夠調用一個函數來返回這些 layout 和把這些東西組織在一塊兒。
 
這必定是個有趣的概念。但不必定適合每個人。它們也有定製的預覽插件。使用 XML 的一個好處就是你能夠預渲染視圖,而後看到它在設備上的樣子。他們也寫了一個爲 Java 代碼的工具,這個工具能夠解析 Kotlin 代碼而且完成渲染。
 
這也是個 XML 也會使人煩惱的例子。Java 和 XML 這兩個分離的系統,會帶來一些通常的麻煩。而這個方法會把這兩個分離的系通通一到一個 Koltin 的源文件裏面。這會致使性能的提高,由於你減小了 XML 解析的開銷,也會減小尋找 XML 中定義的類而發生的反射的開銷。
 
因此雖然不是每一個人都喜歡這個解決方法,可是這確是個解決他們碰到問題的新的方案。
 
結論 
 
我想給大家介紹如今解決安卓系統中的問題過程當中的最有用的一些概念。Koltin 語言還有許多其餘的通常性的改進,但那都是爲了 Java 語言的。
 
這就是今天我想討論的讓你意識到的一些安卓系統開發的問題和可能解決它們的具體途徑。Kotlin 網站 有着很是多的好的資源。那有一個交互性編輯器,使用它你能夠在你的瀏覽器裏面建立和運行 Kotlin 代碼。
 
在一樣的編輯器裏面,它們也有一系列的交互性教程來幫助你一步步學習語法。
 
語言還處在 1.0 beta 的狀態,因此對於那些由於這個緣由而持觀望態度的人,大家很快就能加入了。我鼓勵大家都試試 Kotlin 語言。
相關文章
相關標籤/搜索