Why use dependency injection? Isn't it good to directly new objects? Why complicate a simple problem?

Author: newki

Why use dependency injection? Isn't it good to directly new objects? Why complicate a simple problem?

Are you showing off your skills, or pretending to be 13?

This is really not the case, if the Dagger2 I use is really a show off, NB. Dagger also has a lot of pitfalls. It is really awesome to use Dagger well in large projects, but we all use Hilt now. What can we install? It is really simple to use, and it is all scene-based things. Some fixed usage. No need no need.

Back to the topic, why use dependency injection? When is dependency injection recommended? Even if you want to use dependency injection, why is Hilt recommended for dependency injection?

1. Automatic management (flexibility and decoupling)

First of all, it does not mean that you must use dependency injection when writing a project. If your project is not a large project, there are only 5, 6, or more than 10 pages in total. You don’t need to use a dependency injection framework. If it is a large project , divide it into modules Components are jointly developed by multiple people, and the objects that may depend on them are very complicated, or object dependencies like nesting dolls, so it is very convenient to use Hilt. Developers of different modules/components directly define the object provision method under their own components/modules, and use it directly on the other side, without the complexity of relationship dependencies and implementation logic.

Let's take a look at some complex nested dependencies. For example, let's take a three-layer doll dependency:

@Singleton
class UserServer @Inject constructor(private val userDao: UserDao) {

    fun testUser() {
        YYLogUtils.w(userDao.printUser())
        toast(userDao.printUser())
    }

    fun getDaoContent():String{
        return userDao.printUser()
    }

}

@Singleton
class UserDao @Inject constructor(private val user: UserBean) {

    fun printUser(): String {
        return user.toString()
    }

}

data class UserBean(
    val name: String,
    val age: Int,
    val gender: Int,
    val languages: List<String>
)

The other three classes are all necessary. In fact, there is one more class that provides UserBean objects

@Module
@InstallIn(SingletonComponent::class)
class Demo10DIModule {

    @Singleton
    @Provides
    fun provideUser(): UserBean {
        return UserBean("newki", 18, 1, listOf("中文", "英文"))
    }

}

use:

@AndroidEntryPoint
class Demo10DIActivity : BaseVMActivity() {

    @Inject
    lateinit var userServer: UserServer

    override fun getLayoutIdRes(): Int = R.layout.activity_demo10_di

    override fun init() {
        findViewById<Button>(R.id.btn_01).click {
            YYLogUtils.w(userServer.toString())
            userServer.testUser()
        }
    }

If you don't use Hilt, you can also implement your own new object

class Demo10DIActivity : BaseVMActivity() {

    lateinit var userServer: UserServer

    override fun getLayoutIdRes(): Int = R.layout.activity_demo10_di

    override fun init() {
        //自己new三个对象
         userServer = UserServer(UserDao(UserBean("newki", 18, 1, listOf("中文", "英文"))))

        findViewById<Button>(R.id.btn_01).click {
            YYLogUtils.w(userServer.toString())
            userServer.testUser()
        }
    }

In this way, the new object, not to mention that the life cycle follows the page, cannot maintain a single instance. Let’s say that if the requirements change, UserBean and UserProfile are needed in UseDao. If you are a new object, you need to modify it everywhere , if it is the Hilt method, we only need to modify the structure of the UserDao object

@Singleton
class UserDao @Inject constructor(private val user: UserBean,private val profile:UserProfile) {

    fun printUser(): String {
        return user.toString()
    }

}

The above is just a simple example. To build an object, you need to build a bunch of other objects, and the construction of other objects is equally complicated and must be built in order, and the life cycles of the required objects are different. Some life cycles may be different from Like Activity, some may be singletons, so the object life cycle and the source of the object should also be considered when constructing.

Especially in large-scale projects, it is very painful, because the project is not written by one person, and everyone cooperates to develop it. Looking at other people's code is like reading a bible, and you don't know how the object of the colleague is created. If the construction method of an object occurs Changes will affect the entire construction process and associated codes, and affect the whole body.

At this time, the dependency injection framework comes in handy. We only need to focus on how to implement functions, object dependencies and life cycles, and let it manage for us. An Inject will help us inject what we need according to the dependencies. Object, and it will manage the life cycle of each object, and will not repeat new when the life cycle is not over.

Therefore, Hilt dependency injection is very suitable for large projects. Small project developers have not encountered these problems because of the low complexity of the project, so they don't understand why Hilt dependency injection is used, and they ask why simple new objects should be so complicated.

2. Life cycle control

The life cycle of the object mentioned here is actually the life cycle in a certain scope. If it is just a bit too shallow to say that the singleton is a singleton within a certain range.

We can't control the life cycle of the new object directly, unless we use the global singleton object, and we can easily realize the life cycle control of the object through Hilt dependency injection.

For example, the fast injection method of our ordinary objects, directly annotating Singleton will mark the global scope singleton

@Singleton
class UserServer @Inject constructor(private val userDao: UserDao) {

    fun testUser() {
        YYLogUtils.w(userDao.printUser())
        toast(userDao.printUser())
    }

    fun getDaoContent():String{
        return userDao.printUser()
    }

}

Another usage is that we use Module to define dependency injection, then using SingletonComponent + Singleton also means global scope singleton

@Module
@InstallIn(SingletonComponent::class)
class Demo10DIModule {

    @Singleton
    @Provides
    fun provideUser(): UserBean {
        return UserBean("newki", 18, 1, listOf("中文", "英文"))
    }

}

If we want a singleton in the Activity, we use ActivityComponent + ActivityScoped which is a singleton in the Activity scope.

@Module
@InstallIn(ActivityComponent::class)
class Demo10DIModule {

    @ActivityScoped
    @Provides
    fun provideUser(): UserBean {
        return UserBean("newki", 18, 1, listOf("中文", "英文"))
    }

}

The above two are relatively commonly used scopes. For others, we can also ensure that the singletons in Fragment, singletons in View, etc. are used in the same way.

Therefore, the life cycle control of dependent objects is also a very convenient feature of Hilt, which cannot be achieved by using new objects.

3. Compared with other dependency injection

At present, the mainstream of Android is three kinds of dependency injection Dagger2 Hilt Koin.

Compared with Koin before, it can only be used in the Kotlin language environment. And the performance will not be better than Hilt. The error message is not friendly either.

Dagger2 is not unusable. It was very popular in 2017 and 2018, but the learning cost is very high. Every time you create a UI dependency injection class, you have to mackproject, and the error prompt is not friendly.

In fact, I have been using Dagger2 for 17 years, and then I made the demo and framework package by myself, but I didn’t use it in the project later. One is that there are too many pitfalls, and the other is that my colleagues don’t know how to use it, and the learning cost is too high. Also give up using Dagger2.

Hilt is actually the Android scenario implementation of Daggert2. Dagger2 is encapsulated internally. It is easier to use Hilt in Android development, the learning cost is very low, and the error prompt is friendly. And it can also be injected into ViewModel. So it is designed for Android development.

Regarding whether the memory usage of injected objects is optimized, there are no articles or official documents pointing out that there is memory optimization. Only my own test shows that if there are multiple injected objects on the entire page compared with direct new objects , I feel that the injected object occupies a little less memory. I don’t know if it’s the fluctuation of the test. I don’t understand it.

Summarize

Summarize why you should use Hilt.

  1. Laziness; automatic management, automatic injection of multiple objects, in case there is a modification, there is no need to lie around in the mountain of corpses.
  2. Singleton; let the object have a life cycle, without our own manual singleton creation, and then manually logout.
  3. Decoupling; I don't need to introduce some unnecessary objects everywhere, especially for componentized projects, another component just injects, and I just reference in my component.

I think these are the three points that attract me the most when I use Hilt,

So it is now 2022, and I recommend Hilt for dependency injection. The key is easy to use. I also made some demos in the common scenarios of Android. There are so many fixed usages in total. I wrote a demo before that covers most of the usage scenarios of Android development. If you need it, you can take it directly. You can check it out My previous article.

By the way, this is a necessary interview skill for foreign programmers. Compared with domestic developers, foreign Android developers especially like to use Dagger2 and Hilt. Many old projects use Dagger2.

Android study notes

Android Performance Optimization: https://qr18.cn/FVlo89
Android Vehicle: https://qr18.cn/F05ZCM
Android Reverse Security Study Notes: https://qr18.cn/CQ5TcL
Android Framework Principles: https://qr18.cn/AQpN4J
Android Audio and Video: https://qr18.cn/Ei3VPD
Jetpack (including Compose): https://qr18.cn/A0gajp
Kotlin: https://qr18.cn/CdjtAF
Gradle: https://qr18.cn/DzrmMB
OkHttp Source Code Analysis Notes: https://qr18.cn/Cw0pBD
Flutter: https://qr18.cn/DIvKma
Android Eight Knowledge Body: https://qr18.cn/CyxarU
Android Core Notes: https://qr21.cn/CaZQLo
Android Past Interview Questions: https://qr18.cn/CKV8OZ
2023 Latest Android Interview Question Collection: https://qr18.cn/CgxrRy
Android Vehicle Development Job Interview Exercises: https://qr18.cn/FTlyCJ
Audio and Video Interview Questions:https://qr18.cn/AcV6Ap

Guess you like

Origin blog.csdn.net/weixin_61845324/article/details/132494653