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

水邊騎馬.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(5 加 10)
}

執行結果:

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(一)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章