Computer >> Máy Tính >  >> Hệ thống >> Android

Cách triển khai Dependency Injection trong ứng dụng của bạn với Dagger 2

Loạt ứng dụng Kriptofolio - Phần 4

Việc tiêm phụ thuộc sẽ cải thiện đáng kể mã của bạn. Nó làm cho mã của bạn trở nên mô-đun, linh hoạt và có thể kiểm tra được. Thực ra cái tên của nó nghe phức tạp hơn ý tưởng đằng sau nó.

Trong phần này của loạt bài, chúng ta sẽ tìm hiểu về việc tiêm phụ thuộc. Sau đó, chúng tôi sẽ triển khai nó trong ứng dụng “Kriptofolio” (trước đây là “My Crypto Coins”). Chúng tôi sẽ sử dụng Dagger 2. Dagger 2 là khuôn khổ tiêm phụ thuộc nguồn mở phổ biến nhất cho Android. Đây là một kỹ năng có giá trị để tạo ra các ứng dụng hiện đại, ngay cả khi nghĩ rằng đường cong học tập đã đủ khó.

Nội dung sê-ri

  • Giới thiệu:Lộ trình xây dựng ứng dụng Android hiện đại trong năm 2018–2019
  • Phần 1:Giới thiệu về các nguyên tắc SOLID
  • Phần 2:Cách bắt đầu xây dựng ứng dụng Android của bạn:tạo Mockups, giao diện người dùng và bố cục XML
  • Phần 3:Tất cả về Kiến trúc đó:khám phá các mẫu kiến ​​trúc khác nhau và cách sử dụng chúng trong ứng dụng của bạn
  • Phần 4:Cách triển khai Dependency Injection trong ứng dụng của bạn với Dagger 2 (bạn đang ở đây)
  • Phần 5:Xử lý các Dịch vụ Web RESTful bằng cách sử dụng Retrofit, OkHttp, Gson, Glide và Coroutines

Dependency Injection là gì?

Để giải thích việc tiêm phụ thuộc trước tiên chúng ta phải hiểu phụ thuộc có nghĩa là gì trong lập trình. Phụ thuộc là khi một trong các đối tượng phụ thuộc vào việc triển khai cụ thể của một đối tượng khác. Bạn có thể xác định một phần phụ thuộc trong mã của mình bất cứ khi nào bạn khởi tạo một đối tượng trong một đối tượng khác. Hãy xem một ví dụ thực tế.

class MyAppClass() {

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

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

Như bạn thấy từ ví dụ này, lớp của bạn MyAppClass sẽ phụ thuộc trực tiếp vào cấu hình cụ thể và việc triển khai lớp thư viện của bạn MyLibrary . Điều gì sẽ xảy ra nếu bạn muốn một ngày nào đó sử dụng thư viện của bên thứ ba? Điều gì sẽ xảy ra nếu bạn muốn có một lớp khác mà bạn muốn sử dụng chính xác cùng một cấu hình thư viện? Mỗi lần bạn sẽ phải tìm kiếm thông qua mã của mình, tìm vị trí chính xác và thay đổi nó. Đó chỉ là một vài ví dụ.

Ý tưởng là sự kết hợp chặt chẽ này giữa các thành phần của ứng dụng sẽ làm cho quá trình phát triển của bạn làm việc chăm chỉ hơn khi dự án của bạn phát triển. Để tránh bất kỳ sự cố nào, hãy sử dụng phương pháp tiêm phụ thuộc để nới lỏng khớp nối được mô tả.

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

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

Vậy đó, đó là một ví dụ về tiêm phụ thuộc rất sơ khai. Thay vì tạo và định cấu hình một MyLibrary mới đối tượng lớp bên trong lớp của bạn MyAppClass , bạn chỉ cần truyền hoặc đưa nó vào hàm tạo. Vì vậy, MyAppClass có thể hoàn toàn vô trách nhiệm đối với MyLibrary .

Dagger 2 là gì?

Dagger là một khung công tác tiêm phụ thuộc mã nguồn mở hoàn toàn tĩnh, có thời gian biên dịch, dành cho cả Java và Android. Trong bài viết này, tôi sẽ nói về phiên bản thứ hai mà Google duy trì. Square đã tạo phiên bản trước đó của nó.

Dagger 2 được coi là một trong những khung công tác tiêm phụ thuộc hiệu quả nhất được xây dựng cho đến nay. Trên thực tế, nếu bạn so sánh Dagger 1, Dagger 2 và Dagger 2.10, bạn sẽ phát hiện ra mỗi cách triển khai là khác nhau. Bạn cần học lại nó mỗi lần vì đã có những thay đổi đáng kể do các tác giả thực hiện. Khi viết bài này, tôi đang sử dụng phiên bản Dagger 2.16 và chúng tôi sẽ chỉ tập trung vào nó.

Như bây giờ bạn đã hiểu về việc tiêm phụ thuộc, các lớp của chúng ta không nên tạo hoặc có các phụ thuộc. Thay vào đó họ cần lấy mọi thứ từ bên ngoài. Vì vậy, khi sử dụng Dagger 2, khung công tác này sẽ cung cấp tất cả các phụ thuộc cần thiết.

Nó thực hiện điều này bằng cách tạo ra rất nhiều mã soạn sẵn cho chúng tôi. Mã được tạo đó sẽ hoàn toàn có thể theo dõi được và sẽ bắt chước mã mà người dùng có thể viết bằng tay. Dagger 2 được viết bằng Java và mã do bộ xử lý chú thích của nó tạo ra cũng sẽ là mã Java.

Tuy nhiên nó hoạt động với Kotlin mà không có bất kỳ vấn đề hoặc sửa đổi nào. Hãy nhớ rằng Kotlin hoàn toàn có thể tương tác với Java. Nếu so với các framework tương tự, Dagger 2 là một framework kém năng động hơn. Nó hoạt động tại thời điểm biên dịch thay vì thời gian chạy với phản chiếu. Không có cách sử dụng phản chiếu nào cả. Tất cả điều đó có nghĩa là khung này sẽ khó thiết lập và khó học hơn. Nó sẽ tăng hiệu suất với sự an toàn trong thời gian biên dịch.

Chèn phụ thuộc thủ công không có công cụ

Bạn có thể đã nhận thấy trong mã nguồn ứng dụng My Crypto Coins từ phần trước rằng có một đoạn mã để tiêm các đối tượng mà không cần sử dụng bất kỳ công cụ tiêm phụ thuộc nào. Nó hoạt động tốt và giải pháp này sẽ đủ tốt cho một ứng dụng nhỏ như thế này. Hãy xem gói tiện ích:

/**
 * 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)
    }
}

Như bạn thấy lớp này sẽ làm tất cả công việc. Nó sẽ tạo các nhà máy ViewModel cho các hoạt động hoặc các phân đoạn yêu cầu chúng.

/**
 * 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
    }

}

Sau đó, bạn sử dụng InjectorUtils lớp như thế này, nơi bạn cần có một nhà máy ViewModel cụ thể:

/**
 * 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)

        ...
    }

}

Như bạn thấy MainListFragment của chúng tôi lớp thậm chí không biết về CryptocurrencyRepository hoặc AppDatabase . Nó nhận được một nhà máy được xây dựng thành công từ lớp InjectorUtils. Trên thực tế, đây là một trong những cách đơn giản để làm điều đó. Chúng tôi sẽ loại bỏ nó và tìm hiểu cách thiết lập công cụ Dagger 2 để tiêm phụ thuộc nâng cao. Nếu ứng dụng này mở rộng về chức năng và mã, tôi không nghi ngờ rằng chúng ta sẽ bắt đầu thấy những lợi ích thực sự nhanh chóng của việc sử dụng khung phụ thuộc chuyên nghiệp thay vì giải pháp thủ công.

Vì vậy, hãy xóa InjectorUtils lớp học ngay bây giờ và tìm hiểu cách thiết lập Dagger 2 trong mã nguồn ứng dụng My Crypto Coins.

Dependency Injection cho MVVM với Kotlin

Cách thiết lập Dagger 2 với ViewModels, Activity và Fragment

Bây giờ chúng ta sẽ đi qua từng bước thiết lập Dagger 2 trên dự án ứng dụng My Crypto Coins.

Để bắt đầu, bạn nên bật Công cụ xử lý chú thích của riêng Kotlin (kapt). Sau đó, thêm các phần phụ thuộc Dagger 2 đặc biệt.

Bạn có thể thực hiện việc này bằng cách thêm các dòng này vào tệp gradle của mình:

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"
Mô-đun:ứng dụng

Plugin Kapt sẽ cho phép trình biên dịch tạo ra các lớp sơ khai cần thiết cho khả năng tương tác giữa Java và Kotlin. Để thuận tiện, chúng tôi sẽ xác định phiên bản Dagger 2 cụ thể trong một tệp gradle riêng biệt, vì chúng tôi làm điều đó với tất cả các phụ thuộc của chúng tôi.

def versions = [:]

versions.dagger = "2.16"

ext.versions = versions

Để tìm phiên bản mới nhất hiện có, hãy kiểm tra các bản phát hành tại kho lưu trữ chính thức của Dagger 2 trên Github.

Bây giờ, hãy tạo ứng dụng của bạn App lớp học.

Bỏ qua điều này nếu bạn đã có tập hợp lớp này. Sau khi bạn làm xong việc đó, chúng tôi sẽ để nguyên như vậy một lúc nhưng quay lại sau.

class App : Application() {

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

}

Đối với ứng dụng My Crypto Coins, chúng tôi đã tạo lớp ứng dụng trước đó.

Tiếp theo, cập nhật tệp kê khai của bạn để kích hoạt App lớp học.

Bỏ qua điều này nếu bạn đã làm điều đó trước đây.

<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"
        ...

Đối với ứng dụng My Crypto Coins, chúng tôi cũng đã đặt App trong tệp kê khai trước đó.

Bây giờ chúng ta hãy tạo một gói mới có tên là dependencyinjection .

Ở đây, chúng tôi sẽ giữ tất cả các tệp liên quan đến việc triển khai Dagger.

Tạo AppModule mô-đun lớp sẽ cung cấp các phần phụ thuộc trên toàn bộ ứng dụng của bạn.

/**
 * 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())
    }
}

Như bạn thấy, để tạo mô-đun Dagger, chúng ta cần chú thích nó bằng @Module đặc biệt chú thích. Các dự án thường có nhiều mô-đun Dagger. Một trong số họ là điển hình để cung cấp các phụ thuộc trên toàn ứng dụng. AppModule này sẽ được sử dụng để khởi tạo các đối tượng được sử dụng trên ứng dụng của chúng tôi, chẳng hạn như cơ sở dữ liệu Phòng, Trang bị thêm, Sở thích dùng chung, v.v.

Ví dụ, chúng ta có thể thảo luận về một kịch bản rất phổ biến để AppModule cung cấp một đối tượng Context trong trường hợp chúng ta cần nó để truy cập vào nó ở bất kỳ đâu trong ứng dụng của chúng ta. Hãy phân tích mã để xem cách thực hiện điều đó.

Chúng tôi cần sử dụng chú thích Dagger đặc biệt @Provides . Nó cho Dagger biết rằng phương thức này cung cấp một kiểu phụ thuộc cụ thể, trong trường hợp của chúng ta là một đối tượng Context. Vì vậy, khi ở đâu đó trong ứng dụng mà chúng tôi yêu cầu đưa Ngữ cảnh vào, thì AppModule là nơi mà Dagger tìm thấy nó. Và nó không quan trọng tên của các phương thức của chúng tôi, vì Dagger chỉ quan tâm đến kiểu trả về. Cách đặt tên phương thức với tiền tố cung cấp chỉ là thông lệ, nhưng nó có thể là bất cứ thứ gì bạn muốn.

@Singleton chú thích mà bạn thấy được áp dụng cho cùng một phương pháp không phải là một phần của chú thích Dagger. Nó được chứa bên trong gói javax. Chú thích này cho Dagger biết rằng chỉ nên có một trường hợp phụ thuộc đó.

Bạn không cần phải viết mã soạn sẵn để kiểm tra xem một phiên bản khác của đối tượng đã có sẵn hay chưa. Khi tạo mã, Dagger sẽ xử lý tất cả logic đó cho bạn vì chú thích này. Lưu ý rằng AppModule của chúng tôi bao gồm một mô-đun ViewModelsModule khác. Hãy tạo nó ngay bây giờ.

Tạo ViewModelsModule mô-đun lớp. Mô-đun này sẽ chịu trách nhiệm cung cấp các ViewModels trên ứng dụng của bạn.

/**
 * 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
}

Mô-đun này sử dụng nhiều liên kết bản đồ tính năng Dagger 2. Sử dụng nó, chúng tôi đóng góp các đối tượng mà chúng tôi chọn vào một bản đồ để có thể đưa vào bất kỳ đâu trong ứng dụng của chúng tôi. Sử dụng kết hợp các chú thích của Dagger @Binds , @IntoMap và chú thích tùy chỉnh của chúng tôi @ViewModelKey (cái này chúng tôi sẽ tạo), chúng tôi tạo một mục nhập bên trong bản đồ của chúng tôi với khóa MainViewModel::class và giá trị MainViewModel ví dụ. Chúng tôi liên kết nhà máy cụ thể với sự trợ giúp của một số ViewModelFactory phổ biến lớp. Chúng ta cần tạo lớp này.

Tạo lớp chú thích tùy chỉnh 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.

Lớp này được sử dụng để liên kết các ViewModels trong ViewModelsModule . Chú thích cụ thể @ViewModelKey đại diện cho chìa khóa của bản đồ của chúng tôi. Khóa của chúng tôi chỉ có thể là một lớp kế thừa từ ViewModel .

Tạo ViewModelFactory lớp học.

/**
 * 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 này là một lớp tiện ích giúp bạn tạo các ViewModels một cách linh hoạt. Tại đây, bạn cung cấp bản đồ đã tạo như một đối số. create() phương thức sẽ có thể chọn đúng trường hợp từ bản đồ.

Tạo ActivityBuildersModule mô-đun lớp học.

/**
 * 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
}

Mô-đun này chịu trách nhiệm xây dựng tất cả các hoạt động của bạn. Nó sẽ tạo ra AndroidInjector cho tất cả các Hoạt động được xác định trong lớp này. Sau đó, các đối tượng có thể được đưa vào các hoạt động bằng cách sử dụng AndroidInjection.inject(this) trong onCreate chức năng từ vòng đời hoạt động. Lưu ý rằng mô-đun này cũng sử dụng một mô-đun riêng biệt khác chịu trách nhiệm về các phân đoạn. Chúng tôi sẽ tạo mô-đun này tiếp theo.

Tạo MainListFragmetBuildersModule mô-đun lớp học.

/**
 * 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
}

Mô-đun này sẽ xây dựng tất cả các phân đoạn của bạn liên quan đến MainActivity . Nó sẽ tạo ra AndroidInjector cho tất cả các Phân đoạn được xác định trong lớp này. Các đối tượng có thể được đưa vào các Phân đoạn bằng cách sử dụng AndroidSupportInjection.inject(this) trong onAttach chức năng từ vòng đời phân mảnh.

Tạo AppComponent thành phần lớp.

/**
 * 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)
}

Thành phần là một lớp rất quan trọng. Nó sẽ cho phép tất cả những điều trên bắt đầu hoạt động cùng nhau. Nó thực hiện điều này bằng cách kết nối các đối tượng với các phụ thuộc của chúng. Dagger sẽ sử dụng giao diện này để tạo mã cần thiết để thực hiện tiêm phụ thuộc.

Để tạo một lớp thành phần, bạn sẽ cần sử dụng chú thích Dagger @Component . Nó lấy một danh sách các mô-đun làm đầu vào. Một chú thích khác @Component.Builder cho phép chúng tôi liên kết một số phiên bản với thành phần.

Sau đó, tạo một đối tượng đồ thị.

Tại thời điểm này, bạn có tất cả các mô-đun và thiết lập thành phần của mình. Bạn có thể tạo đối tượng đồ thị của mình bằng cách chọn Xây dựng -> Tạo mô-đun bên trong IDE Android Studio của bạn. Chúng tôi sẽ cần thế hệ này cho những bước đi trong tương lai.

Bây giờ hãy tạo Injectable giao diện.

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

Điều này cũng sẽ cần thiết cho các bước trong tương lai. Injectable giao diện phải được triển khai bởi các hoạt động hoặc phân đoạn mà chúng tôi muốn đưa vào tự động.

Tạo lớp trợ giúp mới có tên 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)
        }
    }

}

Nó chỉ là một lớp trợ giúp đơn giản để tránh gọi phương thức tiêm trên mỗi hoạt động hoặc phân đoạn.

Tiếp theo, thiết lập App lớp mà chúng tôi đã tạo trước đây.

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
}

Vì ứng dụng có các hoạt động, chúng tôi cần triển khai HasActivityInjector giao diện. Nếu bạn thấy lỗi do Android Studio gọi ra trên DaggerAppComponent , đó là do bạn chưa tạo tệp mới, như đã được chỉ ra ở bước trước.

Vì vậy, hãy thiết lập MainActivity để đưa vào nhà máy ViewModel chính và thêm hỗ trợ cho việc tiêm phân đoạn.

// 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

    ...
}

Vì các hoạt động của chúng tôi có các phân đoạn con nên chúng tôi cần triển khai HasSupportFragmentInjector giao diện. Chúng tôi cũng cần điều này bởi vì chúng tôi dự định tiêm vào các mảnh vỡ của chúng tôi. Hoạt động của chúng ta không nên biết về cách nó được tiêm. Chúng tôi sử dụng AndroidInjection.inject(this) dòng mã bên trong ghi đè onCreate() phương pháp.

Gọi inject() phương thức này sẽ khiến Dagger 2 xác định vị trí các điểm đơn trong biểu đồ phụ thuộc để cố gắng tìm kiểu trả về phù hợp. Tuy nhiên, chúng tôi không cần phải viết bất kỳ mã nào ở đây vì mã đó đã được tạo cho chúng tôi bởi AppInjector đã tạo trước đó lớp trợ giúp mà chúng tôi đã khởi tạo bên trong lớp ứng dụng của mình.

Sau đó, thiết lập MainListFragment để đưa vào nhà máy ViewModel chính.

/**
 * 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)
        
        ...

    }

}

Tương tự như các hoạt động, nếu chúng ta muốn phân đoạn của mình có thể được đưa vào, thì hãy đưa vào onAttach của nó chúng ta nên viết mã AndroidSupportInjection.inject(this) . Nhưng một lần nữa đây là công việc được thực hiện bởi AppInjector người trợ giúp, vì vậy chúng tôi có thể bỏ qua điều đó. Chỉ cần lưu ý rằng chúng ta cần thêm Injectable giao diện mà chúng tôi đã tạo trước đó để trợ giúp hoạt động.

Xin chúc mừng, chúng tôi đã triển khai Dagger 2 trong dự án ứng dụng My Crypto Coins. Tất nhiên bài viết này là hướng dẫn nhanh chóng để triển khai Dagger 2 trong ứng dụng của bạn ngay lập tức, nhưng không bao gồm sâu về nó. Tôi khuyên bạn nên tiếp tục nghiên cứu chủ đề này nếu bạn cảm thấy mất kiến ​​thức cơ bản.

Kho lưu trữ

Kiểm tra mã nguồn của ứng dụng “Kriptofolio” (trước đây là “My Crypto Coins”) được cập nhật trên GitHub.

Xem nguồn trên GitHub

Ačiū! Cảm ơn vì đã đọc! Ban đầu tôi đã xuất bản bài đăng này cho blog cá nhân của mình www.baruckis.com vào ngày 7 tháng 10 năm 2018.