Application是Android的又一大組件,在App運行過程中,有且僅有一個Application對象貫穿應用的整個生命周期,所以適合在Application中保存應用運行時的全局變數。而開展該工作的基礎,是必須獲得Application對象的唯一實例,也就是將Application單例化。 ...
Application是Android的又一大組件,在App運行過程中,有且僅有一個Application對象貫穿應用的整個生命周期,所以適合在Application中保存應用運行時的全局變數。而開展該工作的基礎,是必須獲得Application對象的唯一實例,也就是將Application單例化。獲取一個類的單例對象,需要運用程式設計中常見的單例模式,倘若通過Java編碼實現單例化,想必早已是大家耳熟能詳的了。下麵便是個Application單例化的Java代碼例子:
public class MainApplication extends Application { private static MainApplication mApp; public static MainApplication getInstance() { return mApp; } @Override public void onCreate() { super.onCreate(); mApp = this; } }
從上可見這個單例模式的實現過程主要有三個步驟,說明如下:
1、在自定義的Application類內部聲明一個該類的靜態實例;
2、重寫onCreate方法,把自身對象賦值給第一步聲明的實例;
3、提供一個供外部調用的靜態方法getInstance,該方法返回第一步聲明的Application類實例;
不管是代碼還是步驟,這個單例化的實現都還蠻簡單的。同樣的單例化過程通過Kotlin編碼實現的話,靜態屬性和靜態方法可利用伴生對象來實現,這樣就形成了Kotlin單例化的第一種方式:手工聲明屬性的單例化,具體描述見下。
一、手工聲明屬性的單例化
該方式與Java的常見做法一致,也是手工聲明自身類的靜態實例,然後通過靜態方法返回自身實例。與Java的不同之處在於:Kotlin引入了空安全機制,故而靜態屬性要聲明為可空變數、然後獲得實例時要在末尾加上雙感嘆號表示非空,當然也可事先將自身實例聲明為延遲初始化屬性。總之,兩種聲明手段都是為了確保一個目的,即Application類提供給外部訪問的自身實例必須是非空的。
下麵是手工單例化的Kotlin代碼例子:
class MainApplication : Application() { override fun onCreate() { super.onCreate() instance = this } //單例化的第一種方式:聲明一個簡單的Application屬性 companion object { //情況一:聲明可空的屬性 private var instance: MainApplication? = null fun instance() = instance!! //情況二:聲明延遲初始化屬性 //private lateinit var instance: MainApplication //fun instance() = instance } }
二、藉助Delegates的委托屬性單例化
第一種方式的單例化,雖然提供了兩種屬性的聲明手段,但只是為了保證自身實例的非空性。如果僅僅是確保屬性非空,其實Kotlin已經提供了一個系統工具進行自動校驗,這個工具便是Delegates的notNull方法。該方法返回非空校驗的行為,只要將指定屬性的讀寫行為委托給這個非空校驗行為,那麼開發者就無需手工進行非空判斷了。利用Delegates工具的屬性代理功能,就構成了Kotlin的第二種單例化方式;有關委托屬性和屬性代理的介紹,可參見前面的博文《Kotlin入門(25)共用參數模板》。
下麵是系統委托屬性單例化的Kotlin代碼例子:
class MainApplication : Application() { override fun onCreate() { super.onCreate() instance = this } //單例化的第二種方式:利用系統自帶的Delegates生成委托屬性 companion object { private var instance: MainApplication by Delegates.notNull() fun instance() = instance } }
第二種方式的委托屬性單例化,在App代碼中獲取Application實例與第一種方式是一樣的,都是調用“MainApplication.instance()”這個函數獲得Application的自身實例。
三、自定義委托行為的單例化
前兩種單例化都只完成了非空校驗,還不是嚴格意義上的單例化。真正的單例化是有且僅有一次賦值操作,儘管前兩種的單例化並未實現唯一賦值功能,但是在大多數場合已經夠用了。可是作為孜孜不倦的開發者,務必要究根問底,到底能不能實現唯一賦值情況下的單例化。顯然系統自帶的Delegates工具沒有提供大家期待的校驗行為,於是開發者必須自己寫一個能夠校驗賦值次數的行為類,目的是接管委托屬性的讀寫行為。自定義接管行為的實現,前面的博文《Kotlin入門(25)共用參數模板》即已給出了Preference<T>的完整源碼,其中關鍵是重寫了讀方法getValue和寫方法setValue,因此在這裡可借鑒Preference<T>完成自定義的委托行為編碼。
下麵是自定義委托行為的單例化代碼:
class MainApplication : Application() { override fun onCreate() { super.onCreate() instance = this } //單例化的第三種方式:自定義一個非空且只能一次性賦值的委托屬性 companion object { private var instance: MainApplication by NotNullSingleValueVar() fun instance() = instance } //定義一個屬性管理類,進行非空和重覆賦值的判斷 private class NotNullSingleValueVar<T>() : ReadWriteProperty<Any?, T> { private var value: T? = null override fun getValue(thisRef: Any?, property: KProperty<*>): T { return value ?: throw IllegalStateException("application not initialized") } override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { this.value = if (this.value == null) value else throw IllegalStateException("application already initialized") } } }
由上述代碼看到,自定義的委托行為在getValue方法中進行非空校驗,在setValue方法中進行重覆賦值的校驗,從而按照要求接管了委托屬性的讀寫行為。