Computer >> คอมพิวเตอร์ >  >> ระบบ >> Android

วิธีการใช้ Dependency Injection ในแอปของคุณด้วย Dagger 2

ชุดแอป Kriptofolio - ตอนที่ 4

การพึ่งพาการฉีดจะช่วยปรับปรุงโค้ดของคุณได้อย่างมาก ทำให้โค้ดของคุณเป็นแบบโมดูลาร์ ยืดหยุ่น และสามารถทดสอบได้ อันที่จริงชื่อมันดูซับซ้อนกว่าความคิดที่อยู่ข้างหลังอีก

ในส่วนนี้ของชุดข้อมูล เราจะเรียนรู้เกี่ยวกับการฉีดพึ่งพา จากนั้นเราจะนำไปใช้ในแอป “Kriptofolio” (ก่อนหน้านี้คือ “My Crypto Coins”) เราจะใช้ Dagger 2 Dagger 2 เป็นเฟรมเวิร์กการพึ่งพาโอเพนซอร์สที่ได้รับความนิยมมากที่สุดสำหรับ Android นี่เป็นทักษะอันล้ำค่าที่ต้องมีสำหรับการสร้างแอปสมัยใหม่ แม้จะคิดว่าช่วงการเรียนรู้นั้นยากพอ

เนื้อหาซีรีส์

  • บทนำ:แผนงานในการสร้างแอป Android ที่ทันสมัยในปี 2018–2019
  • ส่วนที่ 1:การแนะนำหลักการ SOLID
  • ส่วนที่ 2:วิธีเริ่มสร้างแอป Android:การสร้าง Mockups, UI และเลย์เอาต์ XML
  • ส่วนที่ 3:ทั้งหมดเกี่ยวกับสถาปัตยกรรมนั้น:สำรวจรูปแบบสถาปัตยกรรมต่างๆ และวิธีใช้งานในแอปของคุณ
  • ส่วนที่ 4:วิธีใช้งาน Dependency Injection ในแอปของคุณด้วย Dagger 2 (คุณอยู่ที่นี่)
  • ส่วนที่ 5:จัดการ RESTful Web Services โดยใช้ Retrofit, OkHttp, Gson, Glide และ Coroutines

การฉีดการพึ่งพาคืออะไร

เพื่ออธิบายการแทรกการพึ่งพาก่อนอื่น เราต้องเข้าใจความหมายของการพึ่งพาในการเขียนโปรแกรม การพึ่งพาอาศัยกันคือเมื่อวัตถุตัวใดตัวหนึ่งขึ้นอยู่กับการใช้งานวัตถุอื่นอย่างเป็นรูปธรรม คุณสามารถระบุการขึ้นต่อกันในโค้ดของคุณได้ทุกครั้งที่คุณสร้างอินสแตนซ์อ็อบเจ็กต์ภายในโค้ดอื่น มาดูตัวอย่างการใช้งานจริงกัน

class MyAppClass() {

    private val library: MyLibrary = MyLibrary(true)
    ...
}

class MyLibrary(private val useSpecialFeature: Boolean) {
    
    ...
}

ดังที่คุณเห็นจากตัวอย่างนี้ ชั้นเรียนของคุณ MyAppClass จะขึ้นอยู่กับการกำหนดค่าที่เป็นรูปธรรมและการใช้งานคลาสไลบรารีของคุณโดยตรง MyLibrary . จะทำอย่างไรถ้าคุณต้องการใช้ห้องสมุดบุคคลที่สามแทนสักวันหนึ่ง จะเป็นอย่างไรถ้าคุณต้องการมีคลาสอื่นที่คุณต้องการใช้การกำหนดค่าไลบรารีเดียวกันทุกประการ ทุกครั้งที่คุณจะต้องค้นหารหัสของคุณ ค้นหาตำแหน่งที่แน่นอนและเปลี่ยนรหัส เป็นเพียงตัวอย่างบางส่วนเท่านั้น

แนวคิดก็คือการเชื่อมต่อที่แน่นแฟ้นระหว่างส่วนประกอบของแอปพลิเคชันจะทำให้การพัฒนาของคุณทำงานหนักขึ้นเมื่อโครงการของคุณเติบโตขึ้น เพื่อหลีกเลี่ยงปัญหา ลองใช้การเติมการพึ่งพาเพื่อคลายการมีเพศสัมพันธ์ตามที่อธิบายไว้

class MyAppClass(private val library: MyLibrary) {
    
    ...
}

class MyLibrary(private val useSpecialFeature: Boolean) {
    
    ...
}

แค่นั้นแหละ นั่นคือตัวอย่างการฉีดพึ่งพาดั้งเดิมมาก แทนที่จะสร้างและกำหนดค่าใหม่ MyLibrary วัตถุคลาสภายในคลาสของคุณ MyAppClass คุณเพียงแค่ผ่านหรือฉีดเข้าไปในตัวสร้าง ดังนั้น MyAppClass สามารถขาดความรับผิดชอบโดยสิ้นเชิงสำหรับ MyLibrary .

Dagger 2 คืออะไร

Dagger เป็นเฟรมเวิร์กการพึ่งพาโอเพนซอร์สแบบสแตติก เวลาคอมไพล์ เวลาคอมไพล์สำหรับทั้ง Java และ Android ในบทความนี้ฉันจะพูดถึงเวอร์ชันที่สองที่ Google ดูแลรักษา Square สร้างเวอร์ชันก่อนหน้า

Dagger 2 ถือเป็นหนึ่งในเฟรมเวิร์กการพึ่งพาอาศัยที่มีประสิทธิภาพที่สุดที่สร้างขึ้นมาจนถึงปัจจุบัน ที่จริงแล้วถ้าคุณเปรียบเทียบ Dagger 1, Dagger 2 และ Dagger 2.10 คุณจะพบว่าการใช้งานแต่ละครั้งต่างกัน คุณต้องเรียนรู้ใหม่ทุกครั้งเนื่องจากมีการเปลี่ยนแปลงที่สำคัญโดยผู้เขียน เมื่อเขียนบทความนี้ ฉันใช้เวอร์ชัน Dagger 2.16 และเราจะเน้นที่มันเท่านั้น

ตามที่คุณเข้าใจเกี่ยวกับการฉีดการพึ่งพาแล้ว คลาสของเราไม่ควรสร้างหรือมีการพึ่งพา แต่พวกเขาจำเป็นต้องได้รับทุกอย่างจากภายนอก ดังนั้นเมื่อใช้ Dagger 2 เฟรมเวิร์กนี้จะจัดเตรียมการพึ่งพาที่จำเป็นทั้งหมด

ทำได้โดยการสร้างรหัสสำเร็จรูปจำนวนมากสำหรับเรา รหัสที่สร้างขึ้นนั้นจะสามารถตรวจสอบย้อนกลับได้อย่างสมบูรณ์และจะเลียนแบบรหัสที่ผู้ใช้อาจเขียนด้วยมือ Dagger 2 เขียนด้วย Java และโค้ดที่สร้างโดยตัวประมวลผลคำอธิบายประกอบจะเป็นโค้ด Java ด้วย

อย่างไรก็ตาม มันใช้งานได้กับ Kotlin โดยไม่มีปัญหาหรือการปรับเปลี่ยนใดๆ โปรดจำไว้ว่า Kotlin สามารถทำงานร่วมกับ Java ได้อย่างสมบูรณ์ หากเปรียบเทียบกับเฟรมเวิร์กที่คล้ายคลึงกัน Dagger 2 นั้นไดนามิกน้อยกว่า มันทำงานในเวลารวบรวมมากกว่าเวลาทำงานที่มีการสะท้อนกลับ ไม่มีการใช้การสะท้อนเลย นั่นหมายความว่าเฟรมเวิร์กนี้จะตั้งค่าและเรียนรู้ได้ยากขึ้น มันจะเพิ่มประสิทธิภาพด้วยความปลอดภัยในการคอมไพล์

การฉีดการพึ่งพาแบบแมนนวลโดยไม่ต้องใช้เครื่องมือ

คุณอาจสังเกตเห็นในซอร์สโค้ดของแอป My Crypto Coins จากส่วนก่อนหน้านี้ว่ามีโค้ดบางส่วนสำหรับการฉีดออบเจ็กต์โดยไม่ต้องใช้เครื่องมือฉีดพึ่งพา มันใช้งานได้ดี และโซลูชันนี้จะดีพอสำหรับแอปขนาดเล็กเช่นนี้ ดูแพ็คเกจยูทิลิตี้:

/**
 * Static methods used to inject classes needed for various Activities and Fragments.
 */
object InjectorUtils {

    private fun getCryptocurrencyRepository(context: Context): CryptocurrencyRepository {
        return CryptocurrencyRepository.getInstance(
                AppDatabase.getInstance(context).cryptocurrencyDao())
    }

    fun provideMainViewModelFactory(
            application: Application
    ): MainViewModelFactory {
        val repository = getCryptocurrencyRepository(application)
        return MainViewModelFactory(application, repository)
    }

    fun provideAddSearchViewModelFactory(
            context: Context
    ): AddSearchViewModelFactory {
        val repository = getCryptocurrencyRepository(context)
        return AddSearchViewModelFactory(repository)
    }
}

อย่างที่คุณเห็นคลาสนี้จะทำทุกอย่าง จะสร้างโรงงาน ViewModel สำหรับกิจกรรมหรือชิ้นส่วนที่ต้องการ

/**
 * Factory for creating a [MainViewModel] with a constructor that takes a
 * [CryptocurrencyRepository].
 */
class MainViewModelFactory(private val application: Application, private val repository: CryptocurrencyRepository) : ViewModelProvider.NewInstanceFactory() {

    @Suppress("UNCHECKED_CAST")
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        return MainViewModel(application, repository) as T
    }

}

จากนั้นคุณใช้ InjectorUtils คลาสเช่นนี้ที่คุณต้องได้รับโรงงาน ViewModel เฉพาะ:

/**
 * A placeholder fragment containing a simple view.
 */
class MainListFragment : Fragment() {

    ...

    private lateinit var viewModel: MainViewModel

    ...

    override fun onActivityCreated(savedInstanceState: Bundle?) {

        super.onActivityCreated(savedInstanceState)

        setupList()
        ...
    }

    ...

    private fun subscribeUi(activity: FragmentActivity) {

        // This is the old way how we were injecting code before using Dagger.
        val factory = InjectorUtils.provideMainViewModelFactory(activity.application)

        // Obtain ViewModel from ViewModelProviders, using parent activity as LifecycleOwner.
        viewModel = ViewModelProviders.of(activity, factory).get(MainViewModel::class.java)

        ...
    }

}

ตามที่คุณเห็น MainListFragment . ของเรา ชั้นเรียนไม่รู้เรื่อง CryptocurrencyRepository หรือ AppDatabase . ได้โรงงานที่สร้างสำเร็จจากคลาส InjectorUtils อันที่จริง นี่เป็นวิธีง่ายๆ วิธีหนึ่งที่จะทำ เราจะกำจัดมันและเรียนรู้วิธีตั้งค่าเครื่องมือ Dagger 2 สำหรับการฉีดพึ่งพาขั้นสูง หากแอปนี้จะขยายการทำงานและโค้ด ฉันไม่สงสัยเลยว่าเราจะเริ่มเห็นประโยชน์อย่างรวดเร็วจากการใช้เฟรมเวิร์กการพึ่งพาแบบมืออาชีพแทนโซลูชันแบบแมนนวล

เรามาลบ InjectorUtils . กันเถอะ เรียนตอนนี้และเรียนรู้วิธีตั้งค่า Dagger 2 ในซอร์สโค้ดของแอป My Crypto Coins

Dependency Injection สำหรับ MVVM ด้วย Kotlin

วิธีตั้งค่า Dagger 2 ด้วย ViewModels, กิจกรรม และ Fragments

ตอนนี้เราจะพูดถึงการตั้งค่า Dagger 2 ทีละขั้นตอนในโครงการแอป My Crypto Coins

ในการเริ่มต้น คุณควรเปิดใช้งานเครื่องมือประมวลผลคำอธิบายประกอบของ Kotlin (kapt) จากนั้นเพิ่มการพึ่งพา Dagger 2 พิเศษ

คุณสามารถทำได้โดยเพิ่มบรรทัดเหล่านี้ในไฟล์ gradle ของคุณ:

apply plugin: 'kotlin-kapt' // For annotation processing

...

implementation "com.google.dagger:dagger:$versions.dagger"
implementation "com.google.dagger:dagger-android:$versions.dagger"
implementation "com.google.dagger:dagger-android-support:$versions.dagger"
kapt "com.google.dagger:dagger-compiler:$versions.dagger"
kapt "com.google.dagger:dagger-android-processor:$versions.dagger"
โมดูล:แอป

ปลั๊กอิน Kapt จะช่วยให้คอมไพเลอร์สร้างคลาส stub ที่จำเป็นสำหรับการทำงานร่วมกันระหว่าง Java และ Kotlin เพื่อความสะดวก เราจะกำหนดเวอร์ชันกริช 2 ที่เป็นรูปธรรมในไฟล์ gradle แยกต่างหาก เช่นเดียวกับที่เราทำกับการอ้างอิงทั้งหมดของเรา

def versions = [:]

versions.dagger = "2.16"

ext.versions = versions

หากต้องการค้นหาเวอร์ชันล่าสุดที่มีให้ตรวจสอบการวางจำหน่ายที่พื้นที่เก็บข้อมูลอย่างเป็นทางการของ Dagger 2 บน Github

ตอนนี้ สร้างแอปพลิเคชันของคุณ App ระดับ.

ข้ามสิ่งนี้ถ้าคุณมีชุดคลาสนี้อยู่แล้ว หลังจากที่คุณทำเสร็จแล้ว เราจะปล่อยให้มันเป็นไปสักพัก แต่กลับมาทีหลัง

class App : Application() {

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

}

สำหรับแอป My Crypto Coins เราได้สร้างคลาสแอปพลิเคชันก่อนหน้านี้แล้ว

ถัดไป อัปเดตไฟล์รายการของคุณเพื่อเปิดใช้งาน App ระดับ.

ข้ามสิ่งนี้หากคุณเคยทำมาก่อน

<manifest xmlns:android="https://schemas.android.com/apk/res/android"
    package="com.baruckis.mycryptocoins">

    <application
        android:name=".App"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        ...

สำหรับแอป My Crypto Coins เราได้ตั้งค่า App . แล้ว ในรายการก่อนหน้านี้

ตอนนี้เรามาสร้างแพ็คเกจใหม่ชื่อ dependencyinjection .

เราจะเก็บไฟล์ทั้งหมดที่เกี่ยวข้องกับการนำ Dagger ไปใช้งาน

สร้าง AppModule โมดูลคลาสที่จะให้การพึ่งพาทั่วทั้งแอปพลิเคชันของคุณ

/**
 * AppModule will provide app-wide dependencies for a part of the application.
 * It should initialize objects used across our application, such as Room database, Retrofit, Shared Preference, etc.
 */
@Module(includes = [ViewModelsModule::class])
class AppModule() {

    @Singleton // Annotation informs Dagger compiler that the instance should be created only once in the entire lifecycle of the application.
    @Provides // Annotation informs Dagger compiler that this method is the constructor for the Context return type.
    fun provideContext(app: App): Context = app // Using provide as a prefix is a common convention but not a requirement.

    @Singleton
    @Provides
    fun provideCryptocurrencyRepository(context: Context): CryptocurrencyRepository {
        return CryptocurrencyRepository.getInstance(AppDatabase.getInstance(context).cryptocurrencyDao())
    }
}

อย่างที่คุณเห็น ในการสร้างโมดูลกริช เราจำเป็นต้องใส่คำอธิบายประกอบด้วย @Module . พิเศษ คำอธิบายประกอบ โครงการมักจะมีโมดูลกริชหลายโมดูล เป็นเรื่องปกติสำหรับหนึ่งในนั้นที่จะให้การพึ่งพาทั่วทั้งแอป AppModuleนี้ จะถูกใช้เพื่อเริ่มต้นวัตถุที่ใช้ในแอปพลิเคชันของเรา เช่น ฐานข้อมูลห้อง ชุดติดตั้งเพิ่มเติม การตั้งค่าที่ใช้ร่วมกัน ฯลฯ

ตัวอย่างเช่น เราสามารถพูดคุยเกี่ยวกับสถานการณ์ทั่วไปสำหรับ AppModule เพื่อจัดเตรียมวัตถุบริบท ในกรณีที่เราต้องการให้เข้าถึงได้จากทุกที่ในแอปของเรา มาวิเคราะห์โค้ดกันเพื่อดูวิธีการทำ

เราจำเป็นต้องใช้คำอธิบายประกอบกริชพิเศษ @Provides . มันบอกกริชว่าเมธอดจัดให้มีการพึ่งพาเฉพาะประเภท ในกรณีของเรา วัตถุบริบท ดังนั้นเมื่ออยู่ที่ไหนสักแห่งในแอปที่เราขอให้ใส่บริบท AppModule เป็นที่ที่ Dagger ค้นพบ และไม่สำคัญว่าชื่อวิธีการของเราจะเป็นอย่างไร เนื่องจากกริชสนใจเฉพาะประเภทการส่งคืนเท่านั้น เป็นวิธีปฏิบัติทั่วไปในการตั้งชื่อเมธอดโดยใช้คำนำหน้า แต่จะเป็นอะไรก็ได้ที่คุณต้องการ

@Singleton คำอธิบายประกอบที่คุณเห็นว่าใช้กับวิธีการเดียวกันนั้นไม่ได้เป็นส่วนหนึ่งของคำอธิบายประกอบกริช มีอยู่ภายในแพ็คเกจ javax คำอธิบายประกอบนี้บอก Dagger ว่าควรมีอินสแตนซ์ของการพึ่งพานั้นเพียงอินสแตนซ์เดียวเท่านั้น

คุณไม่จำเป็นต้องเขียนรหัสต้นแบบเพื่อตรวจสอบว่ามีอินสแตนซ์อื่นของวัตถุอยู่แล้วหรือไม่ เมื่อสร้างรหัส Dagger จะจัดการกับตรรกะทั้งหมดนั้นสำหรับคุณเนื่องจากคำอธิบายประกอบนี้ โปรดสังเกตว่า AppModule ของเรามีโมดูลอื่น ViewModelsModule มาสร้างมันกันเถอะ

สร้าง ViewModelsModule โมดูลคลาส โมดูลนี้จะรับผิดชอบในการจัดหา ViewModels ทั่วทั้งแอปพลิเคชันของคุณ

/**
 * Will be responsible for providing ViewModels.
 */
@Module
abstract class ViewModelsModule {

    // We'd like to take this implementation of the ViewModel class and make it available in an injectable map with MainViewModel::class as a key to that map.
    @Binds
    @IntoMap
    @ViewModelKey(MainViewModel::class) // We use a restriction on multibound map defined with @ViewModelKey annotation, and if don't need any, we should use @ClassKey annotation provided by Dagger.
    abstract fun bindMainViewModel(mainViewModel: MainViewModel): ViewModel

    @Binds
    @IntoMap
    @ViewModelKey(AddSearchViewModel::class)
    abstract fun bindAddSearchViewModel(addSearchViewModel: AddSearchViewModel): ViewModel

    @Binds
    abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
}

โมดูลนี้ใช้การผูกแบบหลายแมปคุณลักษณะของ Dagger 2 การใช้มันทำให้เรามีส่วนร่วมกับวัตถุที่เราเลือกไว้ในแผนที่ที่สามารถฉีดได้ทุกที่ในแอพของเรา การใช้คำอธิบายประกอบกริชร่วมกัน @Binds , @IntoMap และคำอธิบายประกอบที่กำหนดเองของเรา @ViewModelKey (อันนี้เราจะสร้าง) เราสร้างรายการภายในแผนที่ของเราด้วยรหัส MainViewModel::class และค่า MainViewModel ตัวอย่าง. เราผูกโรงงานเฉพาะด้วยความช่วยเหลือจาก ViewModelFactory common ทั่วไป ระดับ. เราต้องสร้างคลาสนี้ขึ้นมา

สร้างคลาสคำอธิบายประกอบที่กำหนดเอง ViewModelKey .

/**
 * An annotation class which tells dagger that it can be used to determine keys in multi bound maps.
 */
@MustBeDocumented
@Target(
        AnnotationTarget.FUNCTION,
        AnnotationTarget.PROPERTY_GETTER,
        AnnotationTarget.PROPERTY_SETTER
)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>) // We might use only those classes which inherit from ViewModel.

คลาสนี้ใช้สำหรับผูก ViewModels ใน ViewModelsModule . หมายเหตุเฉพาะ @ViewModelKey แสดงถึงกุญแจสำคัญของแผนที่ของเรา คีย์ของเราสามารถเป็นได้เฉพาะคลาสที่สืบทอดมาจาก ViewModel .

สร้าง ViewModelFactory ระดับ.

/**
 * Factory to auto-generate a Class to Provider Map.
 * We use Provider<T> to create an injectable object at a later time.
 */
@Suppress("UNCHECKED_CAST")
@Singleton
class ViewModelFactory @Inject constructor(private val viewModelsMap: Map<Class<out ViewModel>,
        @JvmSuppressWildcards Provider<ViewModel>>) : ViewModelProvider.Factory {

    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        var creator: Provider<out ViewModel>? = viewModelsMap[modelClass]
        if (creator == null) {
            for (entry in viewModelsMap.entries) {
                if (modelClass.isAssignableFrom(entry.key)) {
                    creator = entry.value
                    break
                }
            }
        }
        if (creator == null) {
            throw IllegalArgumentException("Unknown model class $modelClass")
        }

        try {
            return creator.get() as T
        } catch (e: Exception) {
            throw RuntimeException(e)
        }
    }
}

ViewModelFactoryนี้ เป็นคลาสยูทิลิตี้ที่ช่วยให้คุณสร้าง ViewModels แบบไดนามิก ที่นี่คุณให้แผนที่ที่สร้างขึ้นเป็นอาร์กิวเมนต์ create() จะสามารถเลือกอินสแตนซ์ที่เหมาะสมจากแผนที่ได้

สร้าง ActivityBuildersModule โมดูลคลาส

/**
 * All activities intended to use Dagger @Inject should be listed here.
 */
@Module
abstract class ActivityBuildersModule {

    @ContributesAndroidInjector(modules = [MainListFragmetBuildersModule::class]) // Where to apply the injection.
    abstract fun contributeMainActivity(): MainActivity

    @ContributesAndroidInjector
    abstract fun contributeAddSearchActivity(): AddSearchActivity
}

โมดูลนี้มีหน้าที่รับผิดชอบในการสร้างกิจกรรมทั้งหมดของคุณ มันจะสร้าง AndroidInjector สำหรับกิจกรรมทั้งหมดที่กำหนดไว้ในคลาสนี้ จากนั้นนำวัตถุเข้าสู่กิจกรรมโดยใช้ AndroidInjection.inject(this) ใน onCreate ฟังก์ชันจากวงจรชีวิตกิจกรรม โปรดสังเกตว่าโมดูลนี้ยังใช้โมดูลอื่นแยกต่างหากที่รับผิดชอบส่วนย่อย เราจะสร้างโมดูลนี้ต่อไป

สร้าง MainListFragmetBuildersModule โมดูลคลาส

/**
 * All fragments related to MainActivity intended to use Dagger @Inject should be listed here.
 */
@Module
abstract class MainListFragmetBuildersModule {

    @ContributesAndroidInjector() // Attaches fragment to Dagger graph.
    abstract fun contributeMainListFragment(): MainListFragment
}

โมดูลนี้จะสร้างชิ้นส่วนทั้งหมดของคุณที่เกี่ยวข้องกับ MainActivity . มันจะสร้าง AndroidInjector สำหรับ Fragment ทั้งหมดที่กำหนดไว้ในคลาสนี้ วัตถุสามารถฉีดเข้าไปใน Fragment ได้โดยใช้ AndroidSupportInjection.inject(this) ใน onAttach ฟังก์ชันจากวงจรชีวิตของแฟรกเมนต์

สร้าง AppComponent ส่วนประกอบของคลาส

/**
 * Singleton component interface for the app. It ties all the modules together.
 * The component is used to connect objects to their dependencies.
 * Dagger will auto-generate DaggerAppComponent which is used for initialization at Application.
 */
@Singleton
@Component(
        modules = [
            // AndroidSupportInjectionModule is a class of Dagger and we don't need to create it.
            // If you want to use injection in fragment then you should use AndroidSupportInjectionModule.class else use AndroidInjectionModule.
            AndroidSupportInjectionModule::class,
            AppModule::class,
            ActivityBuildersModule::class
        ]
)
interface AppComponent {

    @Component.Builder // Used for instantiation of a component.
    interface Builder {

        @BindsInstance // Bind our application instance to our Dagger graph.
        fun application(application: App): Builder

        fun build(): AppComponent
    }

    // The application which is allowed to request the dependencies declared by the modules
    // (by means of the @Inject annotation) should be declared here with individual inject() methods.
    fun inject(app: App)
}

ส่วนประกอบเป็นคลาสที่สำคัญมาก จะทำให้ทุกอย่างข้างต้นเริ่มต้นทำงานร่วมกันได้ ทำได้โดยการเชื่อมต่ออ็อบเจ็กต์กับการพึ่งพา กริชจะใช้อินเทอร์เฟซนี้เพื่อสร้างโค้ดที่จำเป็นต่อการฉีดสารอ้างอิง

ในการสร้างคลาสส่วนประกอบ คุณจะต้องใช้คำอธิบายประกอบกริช @Component . ใช้รายการโมดูลเป็นอินพุต คำอธิบายประกอบอื่น @Component.Builder ทำให้เราสามารถผูกบางอินสแตนซ์กับส่วนประกอบได้

จากนั้นสร้างวัตถุกราฟ

ในขณะนี้ คุณมีโมดูลทั้งหมดและการตั้งค่าส่วนประกอบของคุณ คุณสามารถสร้างวัตถุกราฟได้โดยเลือก Build -> Make Module ภายใน Android Studio IDE ของคุณ เราต้องการคนรุ่นนี้เพื่อก้าวต่อไปในอนาคต

ตอนนี้สร้าง Injectable อินเทอร์เฟซ

/**
 * It is just a plain empty marker interface, which tells to automatically inject activities or fragments if they implement it.
 */
interface Injectable

สิ่งนี้จำเป็นสำหรับขั้นตอนในอนาคตด้วย Injectable อินเทอร์เฟซควรถูกใช้งานโดยกิจกรรมหรือชิ้นส่วนที่เราต้องการให้ฉีดโดยอัตโนมัติ

สร้างคลาสตัวช่วยใหม่ชื่อ AppInjector .

/**
 * It is simple helper class to avoid calling inject method on each activity or fragment.
 */
object AppInjector {
    fun init(app: App) {
        // Here we initialize Dagger. DaggerAppComponent is auto-generated from AppComponent.
        DaggerAppComponent.builder().application(app).build().inject(app)

        app.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks {
            override fun onActivityPaused(activity: Activity) {

            }

            override fun onActivityResumed(activity: Activity) {

            }

            override fun onActivityStarted(activity: Activity) {

            }

            override fun onActivityDestroyed(activity: Activity) {

            }

            override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle?) {

            }

            override fun onActivityStopped(activity: Activity) {

            }

            override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
                handleActivity(activity)
            }
        })
    }

    private fun handleActivity(activity: Activity) {
        if (activity is HasSupportFragmentInjector || activity is Injectable) {
            // Calling inject() method will cause Dagger to locate the singletons in the dependency graph to try to find a matching return type.
            // If it finds one, it assigns the references to the respective fields.
            AndroidInjection.inject(activity)
        }

        if (activity is FragmentActivity) {
            activity.supportFragmentManager.registerFragmentLifecycleCallbacks(object : FragmentManager.FragmentLifecycleCallbacks() {
                override fun onFragmentCreated(fragmentManager: FragmentManager, fragment: Fragment, savedInstanceState: Bundle?) {
                    if (fragment is Injectable) {
                        AndroidSupportInjection.inject(fragment)
                    }
                }
            }, true)
        }
    }

}

คลาสตัวช่วยง่ายๆ เพื่อหลีกเลี่ยงไม่ให้เรียกใช้เมธอด inject ในแต่ละกิจกรรมหรือแฟรกเมนต์

ต่อไป ตั้งค่า App คลาสที่เราสร้างไว้ก่อนหน้านี้แล้ว

class App : Application(), HasActivityInjector {

    @Inject // It implements Dagger machinery of finding appropriate injector factory for a type.
    lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Activity>

    override fun onCreate() {
        super.onCreate()

        // Initialize in order to automatically inject activities and fragments if they implement Injectable interface.
        AppInjector.init(this)

        ...
    }


    // This is required by HasActivityInjector interface to setup Dagger for Activity.
    override fun activityInjector(): AndroidInjector<Activity> = dispatchingAndroidInjector
}

เนื่องจากแอปพลิเคชันมีกิจกรรม เราจึงต้องติดตั้ง HasActivityInjector อินเตอร์เฟซ. หากคุณเห็นข้อผิดพลาดที่ Android Studio เรียกใช้ใน DaggerAppComponent เป็นเพราะคุณยังไม่ได้สร้างไฟล์ใหม่ดังที่ได้กล่าวไว้ในขั้นตอนก่อนหน้านี้

ดังนั้น ตั้งค่า MainActivity เพื่อฉีดโรงงาน ViewModel หลักและเพิ่มการรองรับการฉีดชิ้นส่วน

// To support injecting fragments which belongs to this activity we need to implement HasSupportFragmentInjector.
// We would not need to implement it, if our activity did not contain any fragments or the fragments did not need to inject anything.
class MainActivity : AppCompatActivity(), HasSupportFragmentInjector {

    @Inject
    lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Fragment>

    @Inject
    lateinit var viewModelFactory: ViewModelProvider.Factory
    private lateinit var mainViewModel: MainViewModel


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

        // Obtain ViewModel from ViewModelProviders, using this activity as LifecycleOwner.
        mainViewModel = ViewModelProviders.of(this, viewModelFactory).get(MainViewModel::class.java)

        ...
    }

    ...

    override fun supportFragmentInjector(): AndroidInjector<Fragment> = dispatchingAndroidInjector

    ...
}

เนื่องจากกิจกรรมของเรามีชิ้นส่วนย่อย เราจึงต้องติดตั้ง HasSupportFragmentInjector อินเตอร์เฟซ. เราต้องการสิ่งนี้ด้วยเพราะเราวางแผนที่จะฉีดเข้าไปในชิ้นส่วนของเรา กิจกรรมของเราไม่ควรทราบเกี่ยวกับวิธีการฉีด เราใช้ AndroidInjection.inject(this) บรรทัดรหัสภายในแทนที่ onCreate() วิธีการ

กำลังโทร inject() วิธีจะทำให้กริช 2 ค้นหาซิงเกิลตันในกราฟการพึ่งพาเพื่อพยายามค้นหาประเภทการส่งคืนที่ตรงกัน อย่างไรก็ตาม เราไม่จำเป็นต้องเขียนโค้ดใดๆ ที่นี่ เพราะมันทำเพื่อเราโดย AppInjector ที่สร้างไว้ก่อนหน้านี้ คลาสตัวช่วยที่เราเริ่มต้นในคลาสแอปพลิเคชันของเรา

จากนั้น ตั้งค่า MainListFragment เพื่อฉีดโรงงาน ViewModel หลัก

/**
 * A placeholder fragment containing a simple view.
 */
class MainListFragment : Fragment(), Injectable {

    ...

    @Inject
    lateinit var viewModelFactory: ViewModelProvider.Factory
    private lateinit var viewModel: MainViewModel

    ...

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        ...
        subscribeUi(activity!!)
    }

    ...

    private fun subscribeUi(activity: FragmentActivity) {

        // Obtain ViewModel from ViewModelProviders, using parent activity as LifecycleOwner.
        viewModel = ViewModelProviders.of(activity, viewModelFactory).get(MainViewModel::class.java)
        
        ...

    }

}

คล้ายกับกิจกรรม หากเราต้องการให้ชิ้นส่วนของเราสามารถฉีดเข้าไปได้ ให้ใส่ onAttach วิธีที่เราควรเขียนโค้ด AndroidSupportInjection.inject(this) . แต่นี่เป็นงานที่ทำโดย AppInjector ผู้ช่วยเพื่อให้เราสามารถข้ามสิ่งนั้นได้ เพิ่งสังเกตว่าเราต้องเพิ่ม Injectable อินเทอร์เฟซที่เราสร้างขึ้นก่อนหน้านี้เพื่อให้ตัวช่วยทำงาน

ขอแสดงความยินดี เราได้ใช้งาน Dagger 2 ในโครงการแอป My Crypto Coins แน่นอน บทความนี้เป็นคู่มือฉบับย่อในการปรับใช้ Dagger 2 ในแอปของคุณทันที แต่ไม่ครอบคลุมถึงมันอย่างลึกซึ้ง ฉันแนะนำให้คุณค้นคว้าหัวข้อนี้ต่อไปหากคุณรู้สึกหลงทางในพื้นฐาน

ที่เก็บ

ตรวจสอบซอร์สโค้ดของแอป “Kriptofolio” (ก่อนหน้านี้คือ “My Crypto Coins”) ที่อัปเดตบน GitHub

ดูซอร์สบน GitHub

อาชิอู! ขอบคุณที่อ่าน! ฉันเผยแพร่โพสต์นี้สำหรับบล็อกส่วนตัวของฉัน www.baruckis.com เมื่อวันที่ 7 ตุลาคม 2018