current position:Home>Android dependency injection (hilt, Koin)
Android dependency injection (hilt, Koin)
2022-01-27 03:50:43 【prime】
λ:
Warehouse address : github.com/lzyprime/an…
Development branch dev
Joined the compose
, The picture library consists of glide
Replace with coil
, DataStore
Instead of SharedPreference
. At the same time, eliminate LiveData
, use Flow
Instead of .
I wanted to use it completely compose
complete UI Realization . But at the moment compose
Poor components , The cooperation with other libraries is not stable . It takes a lot of effort to realize in some scenes . So open two branches :
dev
:compose
Only do part of the control implementation , The main body still retains the traditional library and other ways .dev_compose
:view
The layer is completelycompose
Realization , Including routing and navigation . Deletelayout, navgation, menu
Etc , Deletecompose outside
Dependence .
hilt
perhaps koin
Dependency injection runs through the whole situation . So you have to know this first
About documents , Try to read the original English version . The original document itself has a certain delay , The translation of Chinese documents will be delayed for a period . It leads to... In the document api May be out of date , Content obsolescence and other issues . such as hilt
stay android Documents on the official website . The Chinese version is still alpha
Version of ,@ViewModelInject
The interface has been abandoned, etc .
Of course, the introduction and principles in Chinese documents can still be referred to , Even if api Changed the , This kind of thing usually doesn't change .
Generality :
No matter which library , The principle is the same :
-
stay
Application, Activity, Fragment
Open a containerContainer
-
The dependent instance is from
Container
gain , And exist as a single example inContainer
in
//example
class MyApplication : Application() {
val appContainer = AppContainer()
...
}
class LoginActivity: Activity() {
private lateinit var loginViewModel: LoginViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val appContainer = (application as MyApplication).appContainer
loginViewModel = LoginViewModel(appContainer.userRepository)
}
}
Copy code
Manual dependency injection problem :
- It has to be managed by itself
Container
, Manually create instances for dependencies , Remove instances based on lifecycle - Sample code
ViewModel
Dependency injection , Depend onViewModelProvider.Factory
To reuseJetpack
The content in . Or put it in a container , Self maintenanceViewModel
Life cycle .
What are the benefits of dependency injection . Of course not , General object
, Just a single case , Can be used everywhere .
Dependency injection advantage , The summary given by the official website :
- Reusing classes and separating dependencies : It's easier to replace the implementation of dependencies . Because of the reversal of control , Code reuse is improved , And classes no longer control how their dependencies are created , It supports any configuration .
- Easy to refactor : The dependency becomes API Surface The verifiable part of , So you can check when you create an object or when you compile , Instead of hiding as implementation details .
- Easy to test : Class does not manage its dependencies , So when testing , You can pass in different implementations to test all the different use cases .
Hilt
Hilt
The approach is to change and expand the base class , For example, inherited from Application
Of , By generated Hilt_XXApplication
, Implement container maintenance inside . You can see the generated code .
Open the container
adopt @HiltAndroidApp, @AndroidEntryPoint
Mark , Generate the corresponding base class , Implement the maintenance logic of the container in the base class .
@HiltAndroidApp
class UnsplashApplication : Application()
@AndroidEntryPoint
class MainActivity : AppCompatActivity()
Copy code
@Inject The tag needs to be injected
class UnsplashRepository @Inject constructor(private val service: UnsplashService)
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject lateinit var service: XXService
...
}
Copy code
How to obtain the registered instance
establish @Module
, adopt @Provides
and @Binds
How to get the tag instance , And pass @InstallIn
Mark the container in which the instance is placed .
@Binds
interface XXService { ... }
class XXServiceImpl @Inject constructor(
...
) : XXService { ... }
@Module
@InstallIn(ActivityComponent::class)
abstract class XXModule {
@Binds
abstract fun bindAnalyticsService( xxServiceImpl: XXServiceImpl ): XXService
}
Copy code
@Provides
interface XXService {
...
companion object : XXService {
...
}
}
@Module
@InstallIn(ActivityComponent::class)
object XXModule {
@Provides
fun provideXXService(): XXService = XXService
}
Copy code
Both methods can mark the acquisition method of an instance .@InstallIn(ActivityComponent)
The marks are stored in Activity The container of Container
in . Reference table :
component | Injection into | create at | destroyed at |
---|---|---|---|
SingletonComponent | Application | Application#onCreate() | Application#onDestroy() |
ActivityRetainedComponent | N/A | Activity#onCreate() | Activity#onDestroy() |
ViewModelComponent | ViewModel | ViewModel created | ViewModel destroyed |
ActivityComponent | Activity | Activity#onCreate() | Activity#onDestroy() |
FragmentComponent | Fragment | Fragment#onAttach() | Fragment#onDestroy() |
ViewComponent | View | View#super() | View destroyed |
ViewWithFragmentComponent | View annotated with @WithFragmentBindings | View#super() | View destroyed |
ServiceComponent | Service | Service#onCreate() | Service#onDestroy() |
besides , You can also mark the scope , Mark in which container , Such as :
@Singleton // Put in Application In the container , Equivalent to global singleton
class UserRepository @Inject constructor(...)
Copy code
Comparison table :
class | component | Scope |
---|---|---|
Application | SingletonComponent | @Singleton |
Activity | ActivityRetainedComponent | @ActivityRetainedScoped |
ViewModel | ViewModelComponent | @ViewModelScoped |
Activity | ActivityComponent | @ActivityScoped |
Fragment | FragmentComponent | @FragmentScoped |
View | ViewComponent | @ViewScoped |
View annotated with @WithFragmentBindings | ViewWithFragmentComponent | @ViewScoped |
Service | ServiceComponent | @ServiceScoped |
The final impact of these frills is just the life cycle of the instance , And single case range , If marked Activity Inside , Then each Activity Each will maintain a single instance . And so on
Provide multiple access methods for instances
// qualifiers
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class DebugService
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class ReleaseService
Copy code
// module
@Module
@InstallIn(ApplicationComponent::class)
object NetworkModule {
@DebugService
@Provides
fun provideDebugService(): XXService = XXService(debug_url)
@ReleaseService
@Provides
fun provideReleaseService(): XXService = XXService(release_url)
}
Copy code
// use
class UserRepository @Inject constructor(
@DebugService service: XXService
)
// or
class Example {
@Release
@Inject lateinit var okHttpClient: OkHttpClient
}
Copy code
Default qualifier :@ApplicationContext
and @ActivityContext
class AnalyticsAdapter @Inject constructor(
@ActivityContext private val context: Context,
private val service: AnalyticsService
) { ... }
Copy code
ViewModel
Use @HiltViewModel
Mark , stay Activity You can still get through by viewModels()
To get .
@HiltViewModel
class ExampleViewModel @Inject constructor(
private val savedStateHandle: SavedStateHandle,
private val repository: ExampleRepository
) : ViewModel() {
...
}
@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() {
private val exampleViewModel: ExampleViewModel by viewModels()
...
}
Copy code
Learn how to do it , The first thing to know is by viewModel()
How to achieve . The complete function is to pass ViewModel Factory
How to get , The default value is empty , When you don't wear anything , Would call Activity#getDefaultViewModelProviderFactory
. A default factory will be created .
and Hilt
Injection method of implementation , Is to change and expand the base class . So in the generated Activity In the base class ,override
Drop this function , Go first Hilt In looking for , If there is no match, the default behavior is returned .
This is a Koin
And Hilt
One of the obvious differences . Koin
It is similar to encapsulating the process of manual injection , therefore by viewModel()
yes koin
An additional version of the library , With the same name , Are two different functions .
Navigation
You can share a navigation map as a unit ViewModel
val viewModel: ExampleViewModel by hiltNavGraphViewModels(R.id.my_graph)
Copy code
gradle plugin The generated code
Thanks to the Hilt
adopt Gradle Plugin
The generated code , Or we'll :
@HiltAndroidApp(Application.class)
class FooApplication : Hilt_FooApplication
@AndroidEntryPoint(FragmentActivity.class)
class FooActivity : Hilt_FooActivity
...
Copy code
Navigation SafeArgs
It's the same thing Gradle Plugin
Library for generating code , So it's not there dependencies{ implementation "xxx" }
But in plugins{ id "xxx" }
Koin
When you understand the concept of container , Look again. Koin
, Or other dependencies are easy to understand .Koin
The source code in github
There are , You can pick it yourself . Or guess how to achieve , See if the library is the same as yourself . Or think the implementation of the library is not elegant or good , Is there a better plan or writing .
Open the container
Koin
adopt startKoin
class MainApplication : Application() {
override fun onCreate() {
super.onCreate()
startKoin {
// Koin Android logger
androidLogger()
//inject Android context
androidContext(this@MainApplication)
// use modules
modules(myAppModules)
}
}
}
Copy code
The tag needs to be injected
by inject()
class MySimpleActivity : AppCompatActivity() {
val firstPresenter: MySimplePresenter by inject()
}
Copy code
get()
class MySimpleActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val firstPresenter: MySimplePresenter = get()
}
}
Copy code
How to obtain the registered instance
val appModule = module {
// single instance of HelloRepository
single<HelloRepository> { HelloRepositoryImpl() }
// Simple Presenter Factory
factory { MySimplePresenter(get()) }
}
Copy code
startKoin {
...
// use modules
modules(myAppModules)
}
Copy code
module
There are detailed documents . As for the life cycle and scope of the instance , It must depend module
and startKoin
Registration method maintenance .
ViewModel
class MyViewModel(val repo : HelloRepository) : ViewModel() {...}
Copy code
val appModule = module {
// single instance of HelloRepository
single<HelloRepository> { HelloRepositoryImpl() }
// MyViewModel ViewModel
viewModel { MyViewModel(get()) }
}
Copy code
Since the injection method is equivalent to manual maintenance of the container , therefore ViewModel
You also need to register to get the method .
class MyViewModelActivity : AppCompatActivity() {
val myViewModel: MyViewModel by viewModel()
}
Copy code
call Koin
In the database viewModel function
, Get instance .
~λ:
See the documentation and source code for other details . The two libraries have their own advantages and disadvantages , comparison Koin
, Hilt
Expand the base class , The way the code is generated may be more comfortable .
copyright notice
author[prime],Please bring the original link to reprint, thank you.
https://en.cdmana.com/2022/01/202201270350369428.html
The sidebar is recommended
- Spring IOC container loading process
- [thinking] the difference between singleton mode and static method - object-oriented programming
- Hadoop environment setup (MySQL environment configuration)
- 10 minutes, using node JS creates a real-time early warning system for bad weather!
- Git tool
- Force deduction algorithm - 92 Reverse linked list II
- What is the sub problem of dynamic programming?
- C / C + +: static keyword summary
- Idea does not have the artifacts option when configuring Tomcat
- Anaconda can't open it
guess what you like
-
I don't know how to start this
-
Matlab simulation of transportation optimization algorithm based on PSO
-
MySQL slow log optimization
-
[Vue] as the window is stretched (larger, smaller, wider and higher), the text will not be displayed
-
Popular Linux distributions for embedded computing
-
Suzhou computer research
-
After installing SSL Certificate in Windows + tomcat, the domain name request is not successful. Please answer!!
-
Implementation time output and greetings of jQuery instance
-
The 72 year old uncle became popular. Wu Jing and Guo fan made his story into a film, which made countless dreamers blush
-
How to save computer research
Random recommended
- Springboot implements excel import and export, which is easy to use, and poi can be thrown away
- The final examination subjects of a class are mathematical programming, and the scores are sorted and output from high to low
- Two pronged approach, Tsinghua Professor Pro code JDK and hotspot source code notes, one-time learning to understand
- C + + recursive knapsack problem
- The use of GIT and GitHub and the latest git tutorial are easy to understand -- Video notes of crazy God speaking
- PostgreSQL statement query
- Ignition database test
- Context didn't understand why he got a high salary?, Nginxfair principle
- Bootstrap switch switch control user's guide, springcloud actual combat video
- A list that contains only strings. What other search methods can be used except sequential search
- [matlab path planning] multi ant colony algorithm grid map path planning [including GUI source code 650]
- [matlab path planning] improved genetic algorithm grid map path planning [including source code phase 525]
- Iinternet network path management system
- Appium settings app is not running after 5000ms
- Reactnative foundation - 07 (background image, status bar, statusbar)
- Reactnative foundation - 04 (custom rpx)
- If you want an embedded database (H2, hsql or Derby), please put it on the classpath
- When using stm32g070 Hal library, if you want to write to flash, you must perform an erase. If you don't let it, you can't write continuously.
- Linux checks where the software is installed and what files are installed
- SQL statement fuzzy query and time interval filtering
- 69. Sqrt (x) (c + + problem solving version with vs runnable source program)
- Fresh students are about to graduate. Do you choose Java development or big data?
- Java project: OA management system (java + SSM + bootstrap + MySQL + JSP)
- Titanic passenger survival prediction
- Vectorization of deep learning formula
- Configuration and use of private image warehouse of microservice architect docker
- Relearn JavaScript events
- For someone, delete return 1 and return 0
- How does Java dynamically obtain what type of data is passed? It is used to judge whether the data is the same, dynamic data type
- How does the database cow optimize SQL?
- [data structure] chain structure of binary tree (pre order traversal) (middle order traversal) (post order traversal) (sequence traversal)
- Webpack packaging optimization solution
- 5. Operation element
- Detailed explanation of red and black trees
- redhat7. 9 install database 19C
- Blue Bridge Cup notes: (the given elements are not repeated) complete arrangement (arrangement cannot be repeated, arrangement can be repeated)
- Detailed explanation of springboot default package scanning mechanism and @ componentscan specified scanning path
- How to solve the run-time exception of test times
- Detailed explanation of k8s management tool kubectl
- Android system view memory command