当前位置:网站首页>Yyds dry inventory jetpack hit dependency injection framework Getting Started Guide

Yyds dry inventory jetpack hit dependency injection framework Getting Started Guide

2022-07-05 01:29:00 Petterpx

#yyds Dry inventory # JetPack | Hilt- A guide to dependency injection framework _android

Hilt yes Google The latest dependency injection framework , It is based on Dagger Research and development , But it's different from Dagger. about Android For developers ,Hilt It can be said that it is specially for Android make , Provides a way to Dagger Dependency injection into Android Standard approach to applications , And created a standard set of components and scopes , These components are automatically integrated into Android Throughout the life cycle of the application , To make it easier for developers to get started .

Before learning this article , Suppose you already know what dependency injection is , If I don't know , You can understand the concept first .Hilt The aim is to reduce Android The developer's initial cost of using the dependency injection framework , But we still need to understand the basic idea .

Some corresponding notes are as follows :

  • @HiltAndroidApp

    Trigger Hilt Code generation , Include base classes for applications , You can use dependency injection , The application container is the parent container of the application , This means that other containers can access the dependencies they provide .

  • @AndroidEntryPoint

    It creates a dependent container , The container follows Android The life cycle of a class

  • @Inject

    Fields used for Injection , Its type cannot be Private

    If you want to tell Hilt How to provide instances of corresponding types , Need to put @Inject Add to the constructor of the class to be injected .

    Hilt Information about how to provide different types of instances is also known as binding **.**

  • @Install(xx)

    Install To tell Hilt Which component will this module be installed on .

Components (Compenent)

Hitl The following standard components are available by default , Just add... To the class  @AndroidEntryPoint You can support the injection of the following classes

CompenentInjector for
ApplicationComponentApplication
ActivityRetainedComponentViewModel( see also  JetPack-ViewModel Expand
ActivityComponentActivity
FragmentComponentFragment
ViewComponentView
ViewWithFragmentComponentView And @WithFragmentBindings
ServiceComponentService

It should be noted that ,Hilt Only extensions are supported  FragmentActivity( Such as  AppCompatActivity) Activities and extensions Jetpack Fragments of the library Fragment, Without support FragmentAndroid platform ( It has been abandoned ) Of fragment .

Components (Compenent) Life cycle of

  • It limits the life cycle of binding in the scope of creating and generating components
  • It indicates the appropriate values that can be injected with members .( for example : When @Inject Field is not null when )
Component Scope of action Created atDestroyed at
ApplicationComponent@SingletonApplication#onCreate()Application#onDestroy()
ActivityRetainedComponent@ActivityRetainedScopeActivity#onCreate() 1Activity#onDestroy() 1
ActivityComponent@ActivityScopedActivity#onCreate()Activity#onDestroy()
FragmentComponent@FragmentScopedFragment#onAttach()Fragment#onDestroy()
ViewComponent@ViewScopedView#super()View destroyed
ViewWithFragmentComponent@ViewScopedView#super()View destroyed
ServiceComponent@ServiceScopedService#onCreate()Service#onDestroy()

By default , All bindings are scopeless , in other words , Every time you bind , Will create a new binding instance ;

however ,Dagger Allow binding scopes to specific components , As shown in the table above , Within the scope of the specified component , Instances are created only once , And all requests for the binding will share the same instance .

for example :

@Singletion
class TestCompenent @Inject constructor()

     
  • 1.
  • 2.

among @Singleton On behalf of TestComponent Instances throughout app Is the only one , When a subsequent class wants to inject it , Will share this instance .

How to use ?

Import dependencies first

implementation 'com.google.dagger:hilt-android:2.28-alpha'
kapt 'com.google.dagger:hilt-android-compiler:2.28-alpha'

     
  • 1.
  • 2.
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.28-alpha'

     
  • 1.

Corresponding model Add... Below

apply plugin: 'dagger.hilt.android.plugin'

     
  • 1.

Take up a :

We have a NetDataSource Of Remote data class , Then we may need to Activity Call in , The code is as follows


class NetDataSource{
    fun test(){
        println(" I'm just a test method ")   
    }
}

      
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
class MainActivity : AppCompatActivity() {
    lateinit var netDataSource: NetDataSource
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        netDataSource = NetDataSource()
    }
}

      
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

There's no problem with this , That's what we do most of the time , Of course. kt Can also be used in by lazy, But it depends on your own scene . But how to use the above code Hilt Transformation ?

After transformation

class NetDataSource @Inject constructor(){
    fun test(){
        println(" I'm just a test method ")
    }
}

      
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    @Inject
    lateinit var netDataSource: NetDataSource
    override fun onCreate(savedInstanceState: Bundle?) {
       	...
        netDataSource.test()
    }
}

      
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

Is that the end , If used in this way , Then it will report the error directly , because Hilt All modules need to be accessed during code generation , So you have to use @HiltAndroidApp Label your base class Application. It's like this :

@HiltAndroidApp
class KtxApplication : Application()

      
  • 1.
  • 2.

Again , Added @HiltAndroidApp after , You can also use... Anywhere in the future @ApplicationContext

@Module

The module is used to Hlit Add binding , let me put it another way , Is to tell Hlit How to provide different types of instances .

Added @Module Annotated classes , It represents quite a module , And tell which container can be installed with binding through the specified component .

about Hilt Every... That can be injected Android class , There's a connection Hilt Component, for example ,Application The container is associated with it ApplicationComponent , also Fragmenet The container is associated with FragmentComonent, This is the component life cycle we talked about at the beginning

Take up a :

Create a module :
@InstallIn(ApplicationComponent::class)
@Module
object TestModule {
	
  // Every time it's a new example 
  @Provides
  fun bindBook():Book{
    	return Book()
  }
  
  // Reuse the same instance globally 
  @Provides
  @Singleton
  fun bindBook():BookSingle{
    	return Book()
  }
}

      
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

Install To tell hilt Which component will this module be installed on .

A common misconception is , All bindings declared in a module will act on the component that installs the module . however , This is not the case . Binding declarations that use only scope annotations will be limited to scope .

When to add an injection range ?

Scoping a binding comes at a cost in terms of the size of the generated code and its runtime performance , Therefore, use scope with caution . The general rule for determining whether a binding should restrict scope is , Scope the binding only if the binding scope is required for code correctness . If you think the binding is scoped for performance reasons only , Please first verify whether there is a performance problem , Then consider using @Reusable Instead of component scope .

Be careful : stay Kotlin in , Contains only @Provides The module of the function can be object class . such , The provider can be optimized , And it can almost be inlined in the generated code .

Use @Provides tell Hilt How to get specific examples

To tell Hilt How to provide types that cannot be injected by constructors

whenever Hilt When you need to provide an instance of this type , The body of the function that will execute the annotated function .@Provides Commonly used in modules

Take up a :

room General usage of

We use room, There is a database table and the corresponding Dao

@Entity(tableName = "book")
class Book(val name: String) {
	 @PrimaryKey(autoGenerate = true)
    var id: Long = 0
}

@Dao
interface BookDao {
    @Insert
    suspend fun insertAll(vararg books: Book)

    @Query("SELECT COUNT(*) FROM book")
    suspend fun queryBookAll(): Int
}

      
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

next , We have one more AppDataBase and A single example of roomDatabase

@Database(entities = [Book::class], version = 1)
abstract class AppDataBase : RoomDatabase() {
    abstract fun bookDao(): BookDao
}

object RoomSingle {
    lateinit var context: Context
    val roomDatabase by lazy {
        Room.databaseBuilder(
            context,
            AppDataBase::class.java,
            "logging.db"
        ).build()
    }
}

      
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

Generally, when we use it , They are written as follows :

class MainActivity : AppCompatActivity() {
    val bookDao by lazy {
        RoomSingle.roomDatabase.bookDao()
    }
}

      
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

How to use it Hilt Transformation ?

We create a BookModule, And use @Model Note that this is a module ,@InstallIn Declare that the life scope of this module is APP Level

@InstallIn(ApplicationComponent::class)
@Module
object BookModule {
 	@Provides
    @Singleton
    fun provideDatabase(@ApplicationContext appContext: Context): AppDatabase {
        return Room.databaseBuilder(
            appContext,
            AppDatabase::class.java,
            "book.db"
        ).build()
    }
  
    @Provides
    fun provideBookDao(database: AppDatabase): BookDao {
        return database.bookDao()
    }
}

      
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.

We are one of the provideBookDao Added @Provides, Its meaning is to tell Hilt Provide examples BookDao when , You need to perform database.bookDao(). Because we have AppDatabase Passing dependencies , So we also need to tell Hilt How to provide instances of this type .

because AppDatabase By Room Generated , So it's another class that the project doesn't own , Therefore, we can directly copy the original method , there @Singleton Flag whose method will only be called once , Similar to a single case .

change MainActivity The code in is as follows

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    @Inject
    lateinit var bookDao: BookDao
}

      
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

Up to now , Even if we finish the transformation , In this case , We can get this anywhere BookDao. And free from manual construction

Use @Binds Provide injection for the interface

For interfaces , Cannot inject using constructor , We need to tell Hilt Which implementation to use .Binds That's what it does .

Pay attention to the following service conditions :

  • Binds Must annotate an abstract function , The return value of an abstract function is the interface we provide for its implementation . Specify the implementation by adding a unique parameter with an interface implementation type .

Take up a :

We have a IBook Interface , Used to store and query book data

interface IBook {
    suspend fun saveBook(name: String)

    suspend fun getBookAllSum(): Int
}

      
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

Then, if we want to get the interface object elsewhere , The conventional implementation may be One of your concrete implementation classes implements its , And then where it needs to be used Again val iBook=xxxImpl()

If you use Hint Well ?, Continue the code demonstration

Then there is a concrete implementation class BookImpl , Here we use constructor Injection And injected BookDao Used to handle specific data storage . Here we use the suspend function , For students who are not familiar with this department , It can be understood as , It is equivalent to a marker bit , Prompt the compiler that there may be time-consuming operations , A suspend function is a logical process . For specific understanding, please refer to the explanation of big men such as throwing line , There's not much to explain here .

class BookImpl @Inject constructor() : IBook {
    @Inject
    lateinit var dao: BookDao
    override suspend fun saveBook(name: String) {
        // There may be some of your own processing ...
        dao.insertAll(Book(name))
    }
  
   override suspend fun getBookAllSum(): Int {
        return dao.queryBookAll()
    }
}

      
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

Then we need a new module , Used to implement IBook Interface Injection

@InstallIn(ApplicationComponent::class)
@Module
abstract class BookModel {
 @Binds
    abstract fun getIBook(impl: BookImpl): IBook
   }

      
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

In a certain Activity When using (Demo Example , In actual development, we prefer to put data processing in Repository in , from ViewModel management ):

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    @Inject
    lateinit var iBook: IBook

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        btnSave.setOnClickListener {
            lifecycleScope.launch(Dispatchers.IO) {
                iBook.saveBook("Android art ")
                val sum = iBook.getBookAllSum()
                Log.e("petterp", " Current length -----$sum")
            }
        }
    }
}

      
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

Qualifiers( limit )

In the example above , There is only one concrete implementation class , But often actual development , We have multiple concrete implementations . And their scopes are different , Some may just be Activity Use , Some are used globally , How can we solve this problem ?

We can define different modules for two concrete implementations and use Qualifers Regulations .

Take up a :

Still with the above Code continuation . At this point, there is another implementation , Want to achieve storage with special conditions .

Another implementation class is :BookConditionImpl

class BookConditionImpl @Inject constructor() : IBook {
    @Inject
    lateinit var dao: BookDao
    override suspend fun saveBook(name: String) {
        if (name == "Android art ") {
            dao.insertAll(Book("Petterp"))
        }
    }

    override suspend fun getBookAllSum(): Int {
        return dao.queryBookAll()
    }

}

      
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

And then we change BookModel, New in BookConditionModel modular

@InstallIn(ApplicationComponent::class)
@Module
abstract class BookModel {
    @Binds
    @Singleton
    abstract fun getIBook(impl: BookImpl): IBook
}

@InstallIn(ActivityComponent::class)
@Module
abstract class BookConditionModel {
    @Binds
    @ActivityScoped
    abstract fun getConditionIBook(impl: BookConditionImpl): IBook
}

      
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

Then add two comments , Which USES @Qualifier

@Qualifier
annotation class BookModelSingle

@Qualifier
annotation class BookModelCondition

@InstallIn(ApplicationComponent::class)
@Module
abstract class BookModel {
    @BookModelSingle
    @Binds
    @Singleton
    abstract fun getIBook(impl: BookImpl): IBook
}

// Its module range is Activity
@InstallIn(ActivityComponent::class)
@Module
abstract class BookConditionModel {
    @Binds
    @ActivityScoped
    @BookModelCondition
    abstract fun getConditionIBook(impl: BookConditionImpl): IBook
}

      
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.

Then change our Activity

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    @BookModelCondition
    @Inject
    lateinit var iBookCondition: IBook

    @BookModelSingle
    @Inject
    lateinit var iBook: IBook
   
}

      
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

And JetPack

As Google Recommended dependency injection components , at present Hilt It can be done with ViewModel In combination with

Import dependence

    implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha02'
    kapt 'androidx.hilt:hilt-compiler:1.0.0-alpha02'
		
		
		//viewModel Data recovery , You don't have to import , It's just for demonstration 
    implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.2.0"
		// Easy   Use ViewModel-ktx Expand 
    implementation 'androidx.activity:activity:1.1.0'
    implementation 'androidx.fragment:fragment-ktx:1.2.5'

     
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

Take up a

We create a MainActivty

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    private val viewModel by viewModels<TestViewModel>()

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

     
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

ViewModel

class TestViewModel @ViewModelInject constructor(
    private val repository: TestRepository,
    @Assisted val savedState: SavedStateHandle
) : ViewModel() {
    fun test() {
        repository.test()
    }
}

     
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

TestRepository

@ActivityScoped
class TestRepository @Inject constructor() {
    fun test() {
        Log.e("petterp", " A test method ")
    }
}

     
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

Add -Java In the injection ViewModel

public class TestViewModel extends ViewModel {
    @ViewModelInject
    public TestViewModel() {

    }
    public void test(){
        Log.e("petterp"," I'm the test method ");
    }
}

     
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

up to now ,Hilt The relevant content is basically over , For now Hilt There is less information about , And in alpha, If you want to use it in an actual project , It is recommended to try and read more documents , Make another decision .

Reference material , By Priority :

 new JetPack

 Hilt- Code lab

 DaggerDev

 Google-iosched

原网站

版权声明
本文为[Petterpx]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/02/202202141027396146.html