Android開發: 分享利用好Kotlin的特色提升開發效率

Kotlin-first but not kotlin-must

谷歌在 I/O 大會上宣佈,Kotlin 編程語言如今是 Android 應用程序開發人員的首選語言後,有更多的安卓程序投入Kotlin的懷抱。 Kotlin的語法糖更加提升了開發的效率,加快了開發速度,使開發工做變得有趣,也讓咱們有更多時間寫註釋了(笑)。可是其實對於Kotlin和Java在Android開發上的選擇,我的以爲這個除了開發人員對語言的喜愛的,同時也會應該到各自語言的魅力和特色,甚至項目的需求以及後續維護等等各個因素,沒有絕對的選擇的。咱們要作到的是放大不一樣語言優勢並加以拓展,不是一味只選擇某個語言,語言不是問題,用的那我的怎麼用纔是關鍵。java

利用Lazy幫助實現初始化

lazy() 是一個函數, 接受一個 Lambda 表達式做爲參數, 返回一個 Lazy<T> 實例的函數,返回的實例能夠做爲實現延遲屬性的委託: 第一次調用 get() 會執行已傳遞給 lazy() 的 lamda 表達式並記錄結果, 後續調用 get() 只是返回記錄的結果。 先貼上代碼:android

fun startInit(component: Components.()->Unit){
    component.invoke(Components.get())
}

class Components {

    companion object{

        private val entry = ArrayMap<String,Any?>()

        private val instant by lazy { Components() }

        fun get() = instant

        fun getEntry() = entry
    }


    inline fun <reified T>single(single: ()->T){
        val name = T::class.java.name
        getEntry()[name] = single()
    }


}

inline fun <reified T> get(name: String = T::class.java.name) : T{
   return Components.getEntry()[name] as T
}

inline fun <reified T> inject(name: String = T::class.java.name) : Lazy<T> {
    return lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { Components.getEntry()[name]  as T }
}


// 使用例子
startInit {
            single {  RoomApi.getDao() }
            single {  RetroHttp.createApi(Main::class.java) }
        }

 
 private val  main : Main by inject()
 private val dao : MainDao by inject()

複製代碼

總結:簡單的代碼優化,提升開發效率編程

藉助協程實現倒計時和超時等待任務

1、倒計時

這個一直都是安卓開發的常見需求,廣泛可採用以下方案
一、RxJava
二、CountDownTimer
三、Timer+TimerTask
四、線程
這些都是屬於常見的可行方案,相關對應使用時候的坑這裏就不展開,各有各的。 那咱們藉助協程這個工具,咱們能夠本身實現其中一種方案,優雅地編寫代碼,同樣貼代碼bash

fun counter(dispatcher: CoroutineContext,start:Int, end:Int, delay:Long,
onProgress:((value:Int)->Unit),onFinish: (()->Unit)?= null){
    val out = flow<Int> {
        for (i in start..end) {
            emit(i)
            kotlinx.coroutines.delay(delay) 
        }
    }
    GlobalScope.launch {
        withContext(dispatcher) {
            out.collect {
                onProgress.invoke(it)
            }
            onFinish?.invoke()
        }
    }
}
複製代碼

利用flow實現這種異步輸出數字同時,達到每一個輸出結果之間延遲,這樣就能更好地計算出結果,其中的GlobalScope.launch這個方法我十分建議替換成lifecycleScope.launchWhenStarted在安卓上生命週期狀態十分重要,也是近年Google推出這個框架的緣由:網絡

androidx.lifecycle:lifecycle-***-***
複製代碼

其中的lifecycleScope.launchWhenStarted 來自這裏,有興趣的童鞋能夠深刻研究,其實這塊知識和源碼不難理解的,app

androidx.lifecycle:lifecycle-runtime-ktx
複製代碼

2、超時等待任務

在平時的開發中,或多或少遇到一種需求就是,我須要執行一個超時任務,而後這個任務有個最大超時時間,超過了這個時間,任務做廢或執行另外一個任務,超時以內的我要提早執行其餘邏輯等等。例如很多的啓動頁拿廣告的,不管是廣告來自你家的仍是第三方的,涉及到網絡獲取,總有遇到延時問題,我要拿到廣告才能進入主頁面,我也不能等太長時間,影響正常的功能使用,而個人確須要這個廣告的相關收益。那隻好執行相似的超時任務,我我的的解決方案是以下:
仍是藉助協程來進行,一樣GlobalScope.launch這個方法我十分建議替換成lifecycleScope.launchWhenStarted框架

fun waitUtil(dispatcher: CoroutineContext,outTimeMills:Long,onTime:(result:Boolean)->Unit, doWork: suspend () ->Unit){
    GlobalScope.launch(dispatcher) {
        val result = withTimeoutOrNull(outTimeMills){
            doWork.invoke()
        }
        onTime.invoke(result != null)
    }
}
複製代碼

利用協程裏的超時方法來幫助判斷任務的執行是否超時了。
總結:其實協程是一個對線程池封裝的好框架,也不須要把它想象得是什麼十分高大上很難理解的東西,慢慢分析源碼很重要。異步

浮點變量轉dp,px,sp等等

這個也是咱們平時開發常常用到,px轉dp,px轉sp,等等。先貼代碼:編程語言

val Float.dp
   get() = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this,Resources.getSystem().displayMetrics)

val Float.px
  get() = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, this,Resources.getSystem().displayMetrics)

val Float.sp
    get() = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, this,Resources.getSystem().displayMetrics)
複製代碼

其實這種拓展方法在平常的Kotlin開發常常用到, 詳細解疑可見大佬扔物線詳細講解視頻:www.bilibili.com/video/BV16K…
ide

在適應位置跳出Kotlin的ForEach

這裏說的ForEach是這種偷懶寫法list.forEach{},這種狀況下用break跳出是有問題的,能夠本身敲敲代碼看看。 我我的優化方案以下:

inline fun <T> Iterable<T>.forEachBreak(action: (T) -> Boolean ){
    kotlin.run breaking@{
        for (element in this)
            if(!action(element)){
                return@breaking
            }
    }
}
複製代碼

利用最後的返回值爲false的時候跳出去循環,固然其實你若是使用for (element in this)是能夠正常使用break,countine的,我舉個例子就知道了:

val list = ArrayList<Int>()
    for(i in 0..10){
        list.add(i)
    }

    list.forEach {
        if(it>5){
            break;//編譯器會報錯 'break' and 'continue' are only allowed inside a loop
        }
        print("Test 1 Loop in $it")
    }

    for(i in list){
        if(i>5){
            break
        }
        print("Test 2 Loop in $i")
    }
    
       list.forEachBreak {
        if(it>5){
           return@forEachBreak false
        }
        println("Test 3 Loop in $it")
        true
    }
    
複製代碼

除了報錯的那個下面兩個輸出的結果是同樣的

Test 2 Loop in 0
Test 2 Loop in 1
Test 2 Loop in 2
Test 2 Loop in 3
Test 2 Loop in 4
Test 2 Loop in 5
Test 3 Loop in 0
Test 3 Loop in 1
Test 3 Loop in 2
Test 3 Loop in 3
Test 3 Loop in 4
Test 3 Loop in 5
複製代碼

總結:不要懶了,寫多幾行代碼吧,有時候懶得語法糖不必定好吃

EditText的addTextChangedListener和TabLayout的addOnTabSelectedListener優化使用

下面代碼一大坨,有時候真不想寫代碼了(狗頭),先貼一下:

1、EditText的addTextChangedListener:

fun EditText.textWatcher(textWatch: SimpleTextWatcher.() -> Unit) {
    val simpleTextWatcher = SimpleTextWatcher(this)
    textWatch.invoke(simpleTextWatcher)
}

class SimpleTextWatcher(var view: EditText) {

    private var afterText: (Editable?.() -> Unit)? = null
    fun afterTextChanged(afterText: (Editable?.() -> Unit)) {
        this.afterText = afterText
    }

    private var beforeText: ((s: CharSequence?, start: Int, count: Int, after: Int) -> Unit)? = null
    fun beforeTextChanged(beforeText: ((s: CharSequence?, start: Int, count: Int, after: Int) -> Unit)) {
        this.beforeText = beforeText
    }

    private var onTextChanged: ((s: CharSequence?, start: Int, before: Int, count: Int) -> Unit)? =
        null

    fun onTextChanged(onTextChanged: ((s: CharSequence?, start: Int, before: Int, count: Int) -> Unit)) {
        this.onTextChanged = onTextChanged
    }

    init {
        view.addTextChangedListener(object : TextWatcher {
            override fun afterTextChanged(s: Editable?) {
                afterText?.invoke(s)
            }

            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
                beforeText?.invoke(s, start, count, after)
            }

            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
                onTextChanged?.invoke(s, start, before, count)
            }
        })
    }
}
複製代碼

2、TabLayout的addOnTabSelectedListener

有了上面的基礎,下面的就簡單了:

fun TabLayout.onTabSelected(tabSelect: TabSelect.() -> Unit) {
    tabSelect.invoke(TabSelect(this))
}

class TabSelect(tab: TabLayout) {
    private var tabReselected: ((tab: TabLayout.Tab) -> Unit)? = null
    private var tabUnselected: ((tab: TabLayout.Tab) -> Unit)? = null
    private var tabSelected: ((tab: TabLayout.Tab) -> Unit)? = null

    fun onTabReselected(tabReselected: (TabLayout.Tab.() -> Unit)) {
        this.tabReselected = tabReselected
    }

    fun onTabUnselected(tabUnselected: (TabLayout.Tab.() -> Unit)) {
        this.tabUnselected = tabUnselected
    }

    fun onTabSelected(tabSelected: (TabLayout.Tab.() -> Unit)) {
        this.tabSelected = tabSelected
    }

    init {
        tab.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
            override fun onTabReselected(tab: TabLayout.Tab?) {
                tab?.apply { tabReselected?.invoke(tab) }
            }
            override fun onTabUnselected(tab: TabLayout.Tab?) {
                tab?.apply { tabUnselected?.invoke(tab) }
            }
            override fun onTabSelected(tab: TabLayout.Tab?) {
                tab?.apply { tabSelected?.invoke(tab) }
            }

        })
    }
}
  //使用
  tab.onTabSelected {
            onTabSelected {
                pos = position
            }
        }

複製代碼

總結:其實上面這兩個都是很典型的DSL語法,利用Kotlin的DSL,充分的發揮Kotlin的優雅,適當的方法命名,恰當的設計,能讓開發者或者維護人員更好地理解你的代碼,畢竟若是是一大段代碼放在那裏,維護起來確實是有點累人的。

總結

整體而言,分享的內容並很少,都是一些咱們平常常常用到的,平時多敲多寫會有不少不一樣發現。

相關文章
相關標籤/搜索