@Singleton能保證單例嗎

scope裏有個@Singleton,它能保證單例嗎?

答案是不徹底能(或者是說是有條件的能)java

當你不使用@Singleton時,在同一個宿主類裏,注入兩次同一個類的對象你會發現,兩個對象的地址不同bash

當你使用了@Singleton,在同一個宿主類裏,注入兩次同一個類的對象你會發現,兩個對象的地址變得同樣了ide

可是使用了@Singleton後,此時你在另外一個宿主內,再次注入兩次同一個類的對象你會發現,兩個對象的地址在本宿主內是同樣的,可是與以前的那個宿主裏的對象地址是不一樣的ui

爲何會這樣的呢,答案是當你使用了@Singleton後,你所注入的對象是經過Component管理的,只要是同一個Component管理到的,且通過@Singleto註解後的對象,不管注入幾個都是同一個地址(也就是單例)this

可是上面咱們在新的宿主裏,又從新new了個Component,因此新宿主裏的兩個對象是在新的Component所管理的,他們地址是同樣的,而他們與第一個宿主以前的Component是不一樣的,因此地址會不同spa

因此,結論來了,在同一個Component管理的對象,若是沒了@Singleton註解了,那麼他仍是單例,不一樣Component所管理的對象,即便是@Singleton註解過了,依然不是單例3d

來看看源碼

1、沒使用Singleton註解的

這是DaggerPetComponent類code

先獲得providesPetProvider實例,而後在不一樣的宿主類(本例是Main2Activity,Main3Activity,BaseActivity)經過providesPetProvider,獲得相應的main2ActivityMembersInjector,main3ActivityMembersInjector,baseActivityMembersInjectorcomponent

而後實現了PetComponen接口裏的注入方法,這裏會經過上面獲得的main2ActivityMembersInjector,main3ActivityMembersInjector,baseActivityMembersInjector去實現cdn

可見,每次構建新的DaggerPetComponent,都會有新的providesPetProvider產生,致使main2ActivityMembersInjector,main3ActivityMembersInjector,baseActivityMembersInjector裏所保存的注入實例是不一樣的,致使在不一樣的DaggerPetComponent所管理的對象之間不是單例

2、使用了Singleton後註解的,

  1. 這是DaggerPetComponent類

  2. 咱們點擊DoubleCheck看看

    咱們看到,經過DoubleCheck保證了providesPetProvider是單例,而後使用同一個providesPetProvider,產生的對應的main2ActivityMembersInjector,main3ActivityMembersInjector,baseActivityMembersInjector裏面實際保存的是相同的對象實例,從而實現了跨DaggerPetComponent間的單例

問題來了,如何保證全局單例呢

答案:全局保證明例化一個Component,而後全部的注入對象都是經過這個Component來管理,方法有二:

1、在Application裏實例化一個Component

由於Application執行一次,從而保證裏全局只有一個Component

class MyApplication :Application(){
    companion object{
        val petComponent = DaggerPetComponent.create()//初始化petComponent
    }

    override fun onCreate() {
        super.onCreate()
    }
}

複製代碼
class Main2Activity : BaseActivity() {
    @Inject
    lateinit var pet: Pet
    @Inject
    lateinit var pet1: Pet

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main2)
//        DaggerPetComponent.builder().petModule(PetModule()).build().inject(this)//不要這樣注入,這樣是new了新的petComponent,就不是全局單例了
        MyApplication.petComponent.inject(this)//要使用MyApplication裏的petComponent
        bt.setOnClickListener {

            Log.e("ccc", pet.toString())
            Log.e("ccc", pet1.toString())

        }
        bt1.setOnClickListener {

            startActivity(Intent(this, Main3Activity::class.java))
        }
    }
}
複製代碼
class Main3Activity : BaseActivity() {
    @Inject
    lateinit var pet: Pet
    @Inject
    lateinit var pet1: Pet

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

//       DaggerPetComponent.builder().petModule(PetModule()).build().inject(this)//不要這樣注入,這樣是new了新的petComponent,就不是全局單例了
        MyApplication.petComponent.inject(this)//要使用MyApplication裏的petComponent
        Log.e("ccc", "pet${pet.toString()}")
        Log.e("ccc", "pet${pet1.toString()}")
    }
}

複製代碼

結果是全局單例

2、Component再也不是一個接口了,把它改形成一個抽象類,在抽象類裏實現本類的單例

@Component(modules = [PetModule::class])
@Singleton

abstract class PetComponent {
    companion object {
        private  var mComponent: PetComponent? =null
        fun getInstance(): PetComponent? {
            if (mComponent == null) {
                synchronized(PetComponent::class.java) {
                    if (mComponent == null) {
                        mComponent = DaggerPetComponent.create()
                    }
                }
            }
            return mComponent
        }
    }
    abstract fun inject(activity: Main2Activity)
    abstract fun inject(activity: Main3Activity)
}
複製代碼
class Main2Activity : BaseActivity() {
    @Inject
    lateinit var pet: Pet
    @Inject
    lateinit var pet1: Pet

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

        PetComponent.getInstance()!!.inject(this)//單例注入

        bt.setOnClickListener {

            Log.e("ccc", pet.toString())
            Log.e("ccc", pet1.toString())

        }
        bt1.setOnClickListener {

            startActivity(Intent(this, Main3Activity::class.java))
        }
    }
}
複製代碼
class Main3Activity : BaseActivity() {
    @Inject
    lateinit var pet: Pet
    @Inject
    lateinit var pet1: Pet

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

//       DaggerPetComponent.builder().petModule(PetModule()).build().inject(this)
//        MyApplication.petComponent.inject(this)
        PetComponent.getInstance()!!.inject(this)//單例注入


        Log.e("ccc", "pet${pet.toString()}")
        Log.e("ccc", "pet${pet1.toString()}")
    }
}

複製代碼

說說kotlin的單例模式

其實在kotlin裏單例模式只需一個object便可

即把class改成object,kotlin內部就會把這個類改成單例 可是惋惜,我們的PetComponent必須包含抽象方法,那麼這個類必須是abstract的,可是 abstract object不能同時使用因此使用了上面的兩次判空的經典單例寫法

參考

詳解 Dagger2 的 @Scope 和 @Subcomponent

相關文章
相關標籤/搜索