Application是Android的又一大組件,在App運行過程當中,有且僅有一個Application對象貫穿應用的整個生命週期,因此適合在Application中保存應用運行時的全局變量。而開展該工做的基礎,是必須得到Application對象的惟一實例,也就是將Application單例化。獲取一個類的單例對象,須要運用程序設計中常見的單例模式,假若經過Java編碼實現單例化,想必早已經是你們耳熟能詳的了。下面即是個Application單例化的Java代碼例子:java
public class MainApplication extends Application { private static MainApplication mApp; public static MainApplication getInstance() { return mApp; } @Override public void onCreate() { super.onCreate(); mApp = this; } }
從上可見這個單例模式的實現過程主要有三個步驟,說明以下:安全
一、在自定義的Application類內部聲明一個該類的靜態實例;
二、重寫onCreate方法,把自身對象賦值給第一步聲明的實例;
三、提供一個供外部調用的靜態方法getInstance,該方法返回第一步聲明的Application類實例;
不論是代碼仍是步驟,這個單例化的實現都還蠻簡單的。一樣的單例化過程經過Kotlin編碼實現的話,靜態屬性和靜態方法可利用伴生對象來實現,這樣就造成了Kotlin單例化的第一種方式:手工聲明屬性的單例化,具體描述見下。app
1、手工聲明屬性的單例化
該方式與Java的常見作法一致,也是手工聲明自身類的靜態實例,而後經過靜態方法返回自身實例。與Java的不一樣之處在於:Kotlin引入了空安全機制,故而靜態屬性要聲明爲可空變量、而後得到實例時要在末尾加上雙感嘆號表示非空,固然也可事先將自身實例聲明爲延遲初始化屬性。總之,兩種聲明手段都是爲了確保一個目的,即Application類提供給外部訪問的自身實例必須是非空的。
下面是手工單例化的Kotlin代碼例子:ide
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 } }
2、藉助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的自身實例。工具
3、自定義委託行爲的單例化
前兩種單例化都只完成了非空校驗,還不是嚴格意義上的單例化。真正的單例化是有且僅有一次賦值操做,儘管前兩種的單例化並未實現惟一賦值功能,可是在大多數場合已經夠用了。但是做爲孜孜不倦的開發者,務必要究根問底,到底能不能實現惟一賦值狀況下的單例化。顯然系統自帶的Delegates工具沒有提供你們期待的校驗行爲,因而開發者必須本身寫一個可以校驗賦值次數的行爲類,目的是接管委託屬性的讀寫行爲。自定義接管行爲的實現,前面的博文《Kotlin入門(25)共享參數模板》即已給出了Preference<T>的完整源碼,其中關鍵是重寫了讀方法getValue和寫方法setValue,所以在這裏可借鑑Preference<T>完成自定義的委託行爲編碼。
下面是自定義委託行爲的單例化代碼:this
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方法中進行重複賦值的校驗,從而按照要求接管了委託屬性的讀寫行爲。編碼