使用Kotlin高效地開發Android App(五)完結篇

Tony沈哲發表於2019-02-28
水邊騎馬.jpg

一. 單例

使用 Java 來編寫單例模式的話,可以寫出好幾種。同樣,使用 Kotlin 也可以寫出多種單例模式。在這裡介紹的是一種使用委託屬性的方式來實現單例的寫法。

首先,Kotlin 在語法層面上支援委託模式。

委託模式是軟體設計模式中的一項基本技巧。在委託模式中,有兩個物件參與處理同一個請求,接受請求的物件將請求委託給另一個物件來處理。委託模式是一項基本技巧,許多其他的模式,如狀態模式、策略模式、訪問者模式本質上是在更特殊的場合採用了委託模式。委託模式使得我們可以用聚合來替代繼承。

對於一些很常見的屬性,雖然我們可以在每次需要它們的時候手動地實現它們,但更好的方法是一次性全部實現,然後放進一個庫裡面。換句話說,對其屬性值的操作不再依賴於其自身的getter()/setter()方法,而是將其託付給一個代理類,從而每個使用類中的該屬性可以通過代理類統一管理。這種方式是委託屬性

在Kotlin的標準庫中有一系列的標準委託,not null屬性是其中之一。它會含有一個可null的變數並會在我們設定這個屬性的時候分配一個真實的值。如果這個值在被獲取之前沒有被分配,它就會丟擲一個異常。

當然 by lazy 也可以實現單例,下面我們使用 not null 委託來實現 Application 的單例。

class App : Application() {
    companion object {
        var instance: App by Delegates.notNull()
    }

    override fun onCreate() {
            super.onCreate()
            instance = this
    }
}
複製程式碼

二. 封裝Extras

使用ExtrasDelegate來封裝Extras

import android.support.v4.app.Fragment
import android.support.v7.app.AppCompatActivity
import kotlin.reflect.KProperty

/**
 *
 * @FileName:
 *          com.safframework.delegate.extras.Extras.kt
 * @author: Tony Shen
 * @date: 2018-06-11 23:56
 * @version V1.0 <描述當前版本功能>
 */
class ExtrasDelegate<out T>(private val extraName: String, private val defaultValue: T) {

    private var extra: T? = null

    operator fun getValue(thisRef: AppCompatActivity, property: KProperty<*>): T {
        extra = getExtra(extra, extraName, thisRef)
        return extra ?: defaultValue
    }

    operator fun getValue(thisRef: Fragment, property: KProperty<*>): T {
        extra = getExtra(extra, extraName, thisRef)
        return extra ?: defaultValue
    }

}

fun <T> extraDelegate(extra: String, default: T) = ExtrasDelegate(extra, default)

fun extraDelegate(extra: String) = extraDelegate(extra, null)

@Suppress("UNCHECKED_CAST")
private fun <T> getExtra(oldExtra: T?, extraName: String, thisRef: AppCompatActivity): T? =
        oldExtra ?: thisRef.intent?.extras?.get(extraName) as T?

@Suppress("UNCHECKED_CAST")
private fun <T> getExtra(oldExtra: T?, extraName: String, thisRef: Fragment): T? =
        oldExtra ?: thisRef.arguments?.get(extraName) as T?
複製程式碼

封裝完之後,在MainActivity中傳遞引數跳轉到其他到Activity。

import android.content.Intent
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import com.safframework.delegate.R
import com.safframework.delegate.domain.User
import com.safframework.ext.click
import kotlinx.android.synthetic.main.activity_main.*

/**
 *
 * @FileName:
 *          com.safframework.delegate.activity.MainActivity.java
 * @author: Tony Shen
 * @date: 2018-06-13 12:03
 * @version V1.0 <描述當前版本功能>
 */
class MainActivity:AppCompatActivity() {

    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        initViews()
    }

    private fun initViews() {

        text1.click{

            val intent = Intent(this@MainActivity, Demo4ExtrasDelegateActivity::class.java)
            val u = User("Tony","123456")
            intent.putExtra("user",u)
            intent.putExtra("string","just a test")
            startActivity(intent)
        }

        text2.click {

            val intent = Intent(this@MainActivity, Demo4PrefsDelegateActivity::class.java)
            startActivity(intent)
        }
    }
}
複製程式碼

這裡的click函式,在使用Kotlin高效地開發Android App(二)中已經講述過,就不在重複講述。

Demo4ExtrasDelegateActivity接受從MainActivity中傳遞過來的引數。

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import com.safframework.delegate.domain.User
import com.safframework.delegate.extras.extraDelegate
import com.safframework.log.L

/**
 *
 * @FileName:
 *          com.safframework.delegate.activity.Demo4ExtrasDelegateActivity.java
 * @author: Tony Shen
 * @date: 2018-06-13 17:42
 * @version V1.0 <描述當前版本功能>
 */
class Demo4ExtrasDelegateActivity: AppCompatActivity() {

    private val user: User? by extraDelegate("user")
    private val s:String? by extraDelegate("string")

    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        L.json(user)
        L.i(s)
    }
}
複製程式碼

所傳遞過來的任何物件型別,都可以使用如下的方式獲取Extras。只要保證,extra的key正確即可。

    private val user: User? by extraDelegate("user")
    private val s:String? by extraDelegate("string")
複製程式碼

與Extra類似,SharedPreferences也可以使用屬性委託的方式進行封裝。

三. infix

中綴表示式是一種通用的算術或邏輯公式表示方法,操作符以中綴形式處於運算元的中間。中綴表示式允許我們使用一個單詞或字母來當運算子用(其本質還是函式呼叫),忽略呼叫的點和圓括號。

Kotlin的中綴表示式,需要滿足以下條件:

  1. 使用infix修飾
  2. 只有一個引數
  3. 其引數不得接受可變數量的引數且不能有預設值。

例如:

infix fun Int.add(i:Int):Int = this + i

infix fun Int.加(i:Int):Int = this + i

fun main(args: Array<String>) {

    println(5 add 10)
    println(510)
}
複製程式碼

執行結果:

15
15
複製程式碼

在 Kotlin 中,使用中綴表示式最經典的例子,莫過於使用kxdate來操作日期。
kxdate github地址:https://github.com/yole/kxdate

val twoMonthsLater = 2.months.fromNow
val yesterday = 1.days.ago
複製程式碼

等價於:

val twoMonthsLater = 2 months fromNow
val yesterday = 1 days ago
複製程式碼

由此可見,中綴表示式能讓程式碼看起來更加接近於自然語言。

四. inline

Kotlin 天生支援函數語言程式設計,高階函式和 lambda 是其一大特色。

使用高階函式會帶來一些執行時間效率的損失:每一個函式都是一個物件,並且都會捕獲一個閉包。 即那些在函式體內會被訪問的變數。 記憶體分配(對於函式物件和類)和虛擬呼叫會引入執行時間開銷。

使用 inline 修飾的函式,可以從編譯器角度***將函式的函式體複製到呼叫處實現內聯。***

在很多情況下,通過將 Lambda 表示式內聯在使用處, 可以消除執行時消耗。

翻看 Kotlin 的 Standard.kt 可以發現它裡面的函式 with、apply、run、let 等都使用了 inline。

再舉一個例子,對 Closeable 進行擴充套件,讓它支援Java的try-with-resources特性。

inline fun <T : Closeable?, R> T.use(block: (T) -> R): R {
    var closed = false
    try {
        return block(this)
    } catch (e: Exception) {
        closed = true
        try {
            this?.close()
        } catch (closeException: Exception) {
        }
        throw e
    } finally {
        if (!closed) {
            this?.close()
        }
    }
}
複製程式碼

該方法已經在 https://github.com/fengzhizi715/SAF-Kotlin-Utils 中

總結

本文是該系列最後一篇文章,未來不會整理零碎的開發細節,轉而會以體系化形式進行整理。

該系列的相關文章:

使用Kotlin高效地開發Android App(四)

使用Kotlin高效地開發Android App(三)

使用Kotlin高效地開發Android App(二)

使用Kotlin高效地開發Android App(一)


Java與Android技術棧:每週更新推送原創技術文章,歡迎掃描下方的公眾號二維碼並關注,期待與您的共同成長和進步。

使用Kotlin高效地開發Android App(五)完結篇

相關文章