android-ktx 已釋出,你還沒有用過 Kotlin?

lovejjfg發表於2018-03-08

昨天,大神為他們的新作悄悄的點個贊。然後,就在圈子裡面泛起已一圈一圈的漣漪。今天我來蹭下熱點,簡單介紹下這個庫。

android-ktx

截止寫這文章的時候,資料已經是這個樣子。大神果然是大神啊,基本上搞Android 開發的,都會 flow 一下他,就像混黑社會的,總要拜關二爺。可能有些小夥伴兒就是點個 star ,也沒有仔細看相關內容。

WX20180207-111410@2x.png

這個庫是來幹什麼的呢?

A set of Kotlin extensions for Android app development. The goal of Android KTX is to make Android development with Kotlin more concise, pleasant, and idiomatic. It is an explicit goal of this project to not add any new feature to the existing Android APIs.

這裡說的超級清楚了,這是一套用於Android應用開發的 Kotlin 擴充套件。目的就是為了讓我們使用 Kotlin 進行簡潔、愉悅、慣用地 Android 開發,它一個明確的目的就是不向現有的 API 新增新的功能。

簡單的說就是,這個庫就是一個 Kotlin 寫的包裝庫,讓你能夠開心、快速地完成專案開發,它只是包裝,不會增加新的功能。

所以,我們現在的程式碼可以越來越短,越來越簡潔。不屑的人可能就會說,不就又搞了一堆語法糖嘛,這玩意兒不過如此。總有一種全天下的功夫他都會,但是他就懶得寫。

進入正文,先看看這個庫目前提供了那些類的包裝。

WX20180207-113238@2x.png

其實還算挺多的。我瞭解下來,覺得多少真的不重要,關鍵是得實用,真的解決開發過程中實實在在存在的痛點。

實用 Java 開發過程中,總有一些程式碼看著就讓人頭痛。比如說最基本的設定點選事件。ViewTreeObserver 相關監聽,Bundle 建立和存值、SharedPreferences 存值、資料庫 Cursor 的操作、Hander 發訊息處理訊息 Intent 跳轉,Runnable 等等,這些程式碼真沒技術含量,但是為了實現這裡方法,你必須寫出一大堆模版式的方法或者建立所屬的物件,感覺就像每次都在祭天,神聖,流程必須高度一致(玩笑話)。

接下來對比看一下吧。

//before
 val observer = textView.viewTreeObserver
    observer.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
        override fun onPreDraw(): Boolean {
            val maxLines = textView.height / textView.lineHeight
            textView.maxLines = maxLines
            textView.viewTreeObserver.removeOnPreDrawListener(
                    this)
            return false
        }
    })
    //after
textView.doOnPreDraw {
        val maxLines = textView.height / textView.lineHeight
        textView.maxLines = maxLines
    }

//before
val sp = getEditor(context)
sp.putFloat(key, value)
sp.apply()   


//after
getSharedPreferences(context).edit {
        putFloat(key, value)
    }
複製程式碼

記得剛剛入門那會兒,使用 SharedPreferences 忘記呼叫 apply() 或者 commit() 方法,導致結果不生效。所以使用這套庫之後,你完全可以不必再去擔心這些細節問題了。

看完了簡單例子,是不是覺得包裝一下之後程式碼又是少了一大截呢?接著看下內部是具體怎麼實現的。

/**
 * Performs the given action when the view tree is about to be drawn.
 */
inline fun View.doOnPreDraw(crossinline action: (view: View) -> Unit) {
    val vto = viewTreeObserver
    vto.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
        override fun onPreDraw(): Boolean {
            action(this@doOnPreDraw)
            when {
                vto.isAlive -> vto.removeOnPreDrawListener(this)
                else -> viewTreeObserver.removeOnPreDrawListener(this)
            }
            return true
        }
    })
}
複製程式碼

通過上面可以看到,程式碼還是以前那些繁瑣的程式碼,固定的程式碼已經成為模版,而你具體要做的就通過這個 action 方法回撥給你實現。

這裡著重介紹三個關鍵字,inlinecrossinline noinline

inline: 我們都知道,在 Java 裡面,不能直接進行方法賦值的,都得物件導向。比如說點選事件的 ClickListener ,其實最後我們就需要的 onClick() 這個方法,但是每次卻不得不將其包裝為一個 ClickListener 物件。當我們在使用 inline 關鍵詞作用在方法上之後,編譯器可以直接發出以下程式碼,而不是再為引數建立一個函式物件然後生成一個呼叫,這聽著可不要太爽呢。

crossinline: 說完 inline 之後,如果我們作為方法引數的函式並不是直接在方法內部被呼叫,而是在一個 lambda 表示式中或者在一個內部類中,這種情況呢,我們據需要使用 crossinline 來宣告該方法,告訴編譯器它是可以通過的。有點兒像 Java 中內部類呼叫了外部的臨時變數,該變數得宣告為 final 一樣。

noinline: 說完了 crossinline ,我們當然也能知道我們有時候也要限制這個函式式引數的作用域,這時候就需要使用 noinline 來宣告。這就有點兒像 @Nullale @NonNull 一樣,宣告後 IDE 自動為你檢測,並警告你。

關鍵字說完,其實就可以結束了,是的,就是這麼簡單,這裡再簡單提下閉包 Closure。Kotlin 裡面閉包用的超級多,最直觀的感受就是你可以少些很多小括號。

觀察下面這個方法呼叫,本來是呼叫的 doOnPreDraw() 方法,但是它的小括號卻不見了。

 textView.doOnPreDraw {
        val maxLines = textView.height / textView.lineHeight
        textView.maxLines = maxLines
    }
複製程式碼

完整一點兒它應該是這樣的:

    textView.doOnPreDraw({
        view -> 
        
    })
複製程式碼

產生上面的寫法,是因為如果你的方法引數就一個,並且它是一個函式表示式,那麼就可以省略掉小括號,保持簡潔易讀。你不覺小括號又跟上大括號看起來是有點兒詭異嗎?

這種閉包其實是十分常見的,比如說你再看下 build.gradle 配置檔案,是不是全部都是這套路?

signingConfigs {
    release {
        storeFile file("../release.jks")
        keyPassword gradle.password
    }
    debug {
        storeFile file("../debug.keystore")
        storePassword "android“
    }
}
複製程式碼

最後學以致用,最開始我就說了,很討厭寫跳轉 Activity 的方法,真的很煩,大概就是這樣的:

    val intent = Intent(context, TopicDetailActivity::class.java)
    intent.putExtra(Constants.ID, id)
    context!!.startActivity(intent)
複製程式碼

然後自己擼一個類似的方法:

inline fun <T> startActivityIntent(context: Context?, clazz: Class<T>, action: (intent: Intent) -> Unit) {
    if (context == null) {
        return
    }
    val intent = Intent(context, clazz)
    action(intent)
    context.startActivity(intent)
}
複製程式碼

接著你就可以開心的呼叫啦:

    startActivityIntent(context, TopicDetailActivity::class.java, {
        it.putExtra(Constants.ID, id)
    })
複製程式碼

說到這裡,本文應該結束。除了這個剛由 Google 推出的 Kotlin 擴充套件庫,Kotlin 官方老早就出過相關擴充套件來方便我們開發,也是生怕我們一開始不適應,不使用。https://github.com/Kotlin/anko

回到題目,你如果到限制真還沒有用過 Kotlin ,那你真的要考慮嘗試用來它來開發,玩一玩,因為它支援 lambda,支援閉包,程式碼更簡潔。而且它得到了Google 的大力支援,就和剛開始的 Android Studio一樣,可謂是根正苗紅。

對了,目前這個庫目前並不是最終正式版本,可能會有 API 的規範性改動或移除,直接用於專案的小夥伴要做好心理準備,畢竟吃螃蟹有風險。

參考資料

Kotlin functions 沒事兒多看看官方文件吧。@Parcelize 用起來也超級爽。

相關文章