安卓架构组件-kb88凯时官网登录

来自:
时间:2024-06-07
阅读:
免费资源网,https://freexyz.cn/

什么是依赖注入

依赖注入(di,dependency injection)是一种广泛的编程技术。把依赖(所需对象)传递给其它对象创建,好处是类的耦合更加松散,遵循依赖倒置的原则。

类获取所需对象

class engine {  
    fun start() {  
        println("engine start")  
    }  
}
class car {  
    private val engine: engine = engine()  
  
    fun start(){  
        engine.start()  
    }  
}

car对engine强依赖,如果需要其它类型的engine,需要增加一个新的engine,必须对car进行改动。

依赖注入获取所需对象

interface engine {  
    fun start()  
}  
  
class vengine : engine{  
    override fun start() {  
        println("vengine start")  
    }  
}  
  
class wengine : engine{  
    override fun start() {  
        println("wengine start")  
    }  
}
class car(private val engine: engine) {  
    fun start(){  
        engine.start()  
    }  
}

在构造函数中接收engine对象作为参数,而不是初始化时构造自己的engine对象,这就叫做依赖注入。
依赖注入还有很多其它的方式,如变量的get/set,就一一不介绍了。

安卓中手动实现依赖注入

手动实现依赖注入,就是依赖注入的原理。依赖注入框架会生成同样功能的样板代码。
假设有个登录场景,流程大概是这样:
loginactivity->loginviewmodel->userrepository

class userrepository(
	private val localdatasource: userlocaldatasource,
    private val remotedatasource: userremotedatasource  
)
class userlocaldatasource()
class userremotedatasource(
	private val loginservice: loginretrofitservice  
)

在需要的位置手动注入

在需要的地方创建依赖,缺点比较明显:

  1. 大量的样板代码
  2. 必须需要按照顺序声明依赖
  3. 复用困难。
/**
* 在loginactivity的oncreate函数里:
*/
//创建userremotedatasource需要的依赖
val retrofit = retrofit.builder()
.base
.build()
.create(loginservice::class.java)
//创建repository需要的依赖
val remotedatasource = userremotedatasource(retrofit)
val localdatasource = userlocaldatasource()
//创建viewmodel需要的依赖
val userrepository = userrepository(localdatasource, remotedatasource)
//创建viewmodel
loginviewmodel = loginviewmodel(userrepository)

使用容器管理依赖

如果想要复用对象,可以创建一个类来初始化所需的依赖。

class appcontainer {
	private val retrofit = retrofit.builder()
	.base.build()
	.create(loginservice::class.java)
	private val remotedatasource = userremotedatasource(retrofit)
	private val localdatasource = userlocaldatasource()
	val userrepository = userrepository(localdatasource, remotedatasource)  
}

如果需要在整个app中使用,可以放到application中:

class myapplication : application(){
	val appcontainer = appcontainer()
}
/**
* 在loginactivity的oncreate函数里:
*/
val appcontainer = (application as myapplication).appcontainer  
loginviewmodel = loginviewmodel(appcontainer.userrepository)

使用容器来管理依赖还是有样板代码,且需要手动为依赖项创建实例对象。

管理依赖项的声明周期

之前把userrepository的生命周期放在了application,在app被关闭前永远不会被释放。如果数据非常大,会导致内存占用过高。
假如,只有在loginactivity需要依赖,其它位置不需要依赖:

  1. appcontainer 内部需要新增一个logincontainer,用来存放userrepository。
  2. 在loginactivity:oncreate时手动创建logincontainer并放到appcontainer,ondestory时把appcontainer设置为null,主动释放引用。
    根据生命周期来管理依赖项,这样时比较合理的。

dagger实现依赖注入

什么是dagger

dagger是一个依赖注入的库,通过自动生成代码的方式,实现依赖注入。由于是在编译时生成的代码,性能会高于基于反射的方案。dagger生成的代码和手动实现依赖注入生成的代码相似。

  • 每次调用函数都重新创建对象
//@inject 注解会告诉dagger,需要注入.
class userrepository @inject constructor(  
    private val localdatasource: userlocaldatasource,  
    private val remotedatasource: userremotedatasource  
){  
    init {  
        println("userrepository created")  
    }  
}
class userlocaldatasource @inject constructor() {  
    init {  
        println("userlocaldatasource created")  
    }  
}
class userremotedatasource @inject constructor(){  
    init {  
        println("userremotedatasource created")  
    }  
}
//daggerapplicationgraph.create() 会创建新对象  
private val applicationgraph:applicationgraph = daggerapplicationgraph.create()  
//applicationgraph.repository() 会创建新对象  
private val repository1 = applicationgraph.repository()  
private val repository2 = applicationgraph.repository()
/**
输出如下:
userlocaldatasource created
userremotedatasource created
userrepository created
userlocaldatasource created
userremotedatasource created
userrepository created
*/
  • 首次创建对象,之后全局复用这个单例对象.
@singleton  
class userrepository @inject constructor(  
    private val localdatasource: userlocaldatasource,  
    private val remotedatasource: userremotedatasource  
){  
    init {  
        println("userrepository created")  
    }  
}
class userlocaldatasource @inject constructor() {  
    init {  
        println("userlocaldatasource created")  
    }  
}
class userremotedatasource @inject constructor(){  
    init {  
        println("userremotedatasource created")  
    }  
}
/**  
 * @component 注解,用于 interface
 * dagger会生成一个对应的类,以dagger开头,applicationgraph就是daggerapplicationgraph
 * 调用函数会返回对应的对象, dagger会自动添加依赖
 * @singleton 注解,用于标识为全局单例
 */
@singleton  
@component  
interface applicationgraph {  
    fun repository(): userrepository  
}
@singleton  
@component  
interface logingraph {  
    fun repository(): userrepository  
}
class one{  
    //daggerapplicationgraph.create() 会创建新对象  
    private val applicationgraph:applicationgraph = daggerapplicationgraph.create()  
    //applicationgraph.repository() 会创建新对象  
    private val repository1 = applicationgraph.repository()  
    private val repository2 = applicationgraph.repository()  
    init {  
        println("one created")  
    }  
}
class two{  
    private val logingraph:logingraph = daggerlogingraph.create()  
    private val repository1 = logingraph.repository()  
    private val repository2 = logingraph.repository()  
    init {  
        println("two created")  
    }  
}
/**
one 和 two 使用同一个对象,因为@singleton注解是全局单例
输出如下:
userlocaldatasource created
userremotedatasource created
userrepository created
one created
userlocaldatasource created
userremotedatasource created
userrepository created
two created
*/

在安卓中使用dagger

  • 对activity中的字段进行注入
/**  
 * 为了演示方便,这里没有继承viewmodel  
 */
class loginviewmodel @inject constructor(  
    private val userrepository: userrepository  
) {  
    init {  
        println("loginviewmodel created")  
    }  
}
/**  
 * activity 是用安卓系统实例化的,所以无法被dagger创建  
 * 初始化的代码需要放在oncreate方法中  
 * 使用手动调用inject的方式,对字段进行注入,需要被注入的字段必须有@inject注解  
 */  
class loginactivity : appcompatactivity() {  
    @inject lateinit var loginviewmodel: loginviewmodel  
  
    override fun oncreate(savedinstancestate: bundle?) {  
        //调用inject,告诉dagger,可以对当前对象的@inject字段进行注入了  
        (applicationcontext as myapplication).applicationgraph.inject(this)  
        //调用完成,loginviewmodel可以使用了  
  
        super.oncreate(savedinstancestate)  
        setcontentview(r.layout.activity_login)  
    }  
}
/**  
 * @component 注解,用于 interface * dagger会生成一个对应的类  
 * 调用函数会返回对应的对象, dagger会自动添加依赖  
 * @singleton 注解,用于标识为全局单例  
 * inject 调用函数手动注入带有@inject注解的字段,函数名称是任意的,参数是需要注入的对象.
 */
@singleton  
@component  
interface applicationgraph {  
    fun repository(): userrepository  
    fun inject(activity: loginactivity)  
}
class myapplication : application() {  
    val applicationgraph: applicationgraph = daggerapplicationgraph.create()  
}
  • 主动告知如何提供实例
//增加一个参数
class userremotedatasource @inject constructor(private val loginservice: loginservice){  
    init {  
        println("userremotedatasource created")  
    }  
    fun login(){  
        loginservice.login()  
    }  
}
//loginservice 只能通过builder.create()创建
interface loginservice {  
    private class loginserviceimpl : loginservice{  
        init {  
            println("loginserviceimpl created")  
        }  
    }  
    fun login() = println("login")  
    class builder{  
        fun create(): loginservice {  
            return loginserviceimpl()  
        }  
    }  
}
/**  
 * @disableinstallincheck 用于屏蔽绑定生命周期,这个是hilt的内容.  
 * @module 是dagger的注解,用来告诉dagger可以提供实例对象.  
 * @provides 用于@module注解内,提供对应类型的实例对象,函数名任意.也可以添加@singleton注解创建单例.
 */
@disableinstallincheck  
@module  
class loginmodule {  
    @provides  
    fun providerloginservice(): loginservice{  
        return loginservice.builder().create()  
    }  
}
/**  
 * @component 注解,用于 interface * dagger会生成一个对应的类,以dagger开头,applicationgraph就是daggerapplicationgraph  
 * modules 参数用来指定对象该如何提供,必须带有@module注解  
 * 调用函数会返回对应的对象, dagger会自动添加依赖  
 * @singleton 注解,用于标识为全局单例  
 * inject 调用函数手动注入带有@inject注解的字段,函数名称是任意的,参数是需要注入的对象.  
 */
@singleton  
@component(modules = [loginmodule::class])  
interface applicationgraph {  
    fun repository(): userrepository  
  
    fun inject(activity: loginactivity)  
}
  • 子组件和作用域,限定作用域为生命周期
class myapplication : application() {  
    val applicationgraph: applicationcomponent = daggerapplicationcomponent.create()  
}
@singleton  
@component(modules = [loginmodule::class, subcomponent::class])  
interface applicationcomponent {  
    fun logincomponent(): logincomponent.factory  
}
@scope  
@retention(value = annotationretention.runtime)  
annotation class activityscope
@activityscope  
class loginviewmodel @inject constructor(  
    private val userrepository: userrepository,  
) {  
    init {  
        println("loginviewmodel created")  
    }  
    fun login(){  
        userrepository.login()  
    }  
}
@activityscope  
@subcomponent  
interface logincomponent {  
  
    @subcomponent.factory  
    interface factory{  
        fun create(): logincomponent  
    }  
  
    fun inject(activity: loginactivity)  
    fun inject(fragment: loginfragment)  
  
    fun repository(): userrepository  
}
@disableinstallincheck  
@module  
class loginmodule {  
    @singleton  
    @provides    fun providerloginservice(): loginservice{  
        return loginservice.builder().create()  
    }  
}
@activityscope  
class userrepository @inject constructor(  
    private val localdatasource: userlocaldatasource,  
    private val remotedatasource: userremotedatasource  
){  
    init {  
        println("userrepository created")  
    }  
  
    fun login(){  
        remotedatasource.login()  
    }  
}
class userremotedatasource @inject constructor(  
    private val loginservice: loginservice,  
    private val loginservice2: loginservice,  
){  
    init {  
        println("userremotedatasource created")  
    }  
    fun login(){  
        loginservice.login()  
    }  
}
class userlocaldatasource @inject constructor() {  
    init {  
        println("userlocaldatasource created")  
    }  
}
class loginactivity : appcompatactivity() {  
    lateinit var logincomponent: logincomponent  
  
    @inject lateinit var loginviewmodel: loginviewmodel  
  
    override fun oncreate(savedinstancestate: bundle?) {  
        logincomponent = (application as myapplication).applicationgraph.logincomponent().create()  
        logincomponent.inject(this)  
  
        super.oncreate(savedinstancestate)  
        setcontentview(r.layout.activity_login)  
  
        findviewbyid

通过限定作用域的方式,把依赖注入和生命周期绑定,activity和activity中的fragment使用同一个viewmodel。
dagger虽然可以实现精细的依赖注入,但是使用起来非常繁琐。

hilt实现依赖注入

什么是hilt

hilt是基于dagger构建的用于安卓的依赖注入库,简化在安卓上实现依赖注入。

把依赖项注入安卓类

hilt可以很方便的注入到安卓类中
比如把viewmodel

class userrepository @inject constructor(  
    private val localdatasource: userlocaldatasource,  
    private val remotedatasource: userremotedatasource  
){  
    init {  
        println("userrepository created")  
    }  
    fun login(){  
        remotedatasource.login()  
    }  
}
class userlocaldatasource @inject constructor() {  
    init {  
        println("userlocaldatasource created")  
    }  
}
class userremotedatasource @inject constructor(){  
    init {  
        println("userremotedatasource created")  
    }  
    fun login(){  
        println("login")  
    }  
}
class loginviewmodel @inject constructor(  
    private val userrepository: userrepository,  
) {  
    init {  
        println("loginviewmodel created")  
    }  
    fun login(){  
        userrepository.login()  
    }  
}
/**  
 * @androidentrypoint 注解,可以被hilt注入  
 */  
@androidentrypoint  
class loginactivity : appcompatactivity() {  
    @inject lateinit var viewmodel: loginviewmodel  
  
    override fun oncreate(savedinstancestate: bundle?) {  
        super.oncreate(savedinstancestate)  
        setcontentview(r.layout.activity_login)  
  
        findviewbyid

hilt模块

模块的bind

可以被直接构造的接口实现可以通过bind注入

class userremotedatasource @inject constructor(  
    private val analyticsservice: analyticsservice  
){  
    init {  
        println("userremotedatasource created")  
    }  
    fun login(){  
        println("login")  
        analyticsservice.logevent("login")  
    }  
}
interface analyticsservice {  
    fun logevent(eventname: string)  
}
class analyticsserviceimpl @inject constructor(  
    @applicationcontext val context: context  
) : analyticsservice {  
    override fun logevent(eventname: string) {  
        println("context: $context logevent: $eventname")  
    }  
}
@module  
@installin(activitycomponent::class)  
abstract class analyticsservicemodule {  
    @binds  
    abstract fun bindanalyticsservice(analyticsservice: analyticsserviceimpl): analyticsservice  
}

模块的provider

无法被直接构造的接口实现可以通过provider注入

@module  
@installin(activitycomponent::class)  
class loginservicemodule {  
    @provides  
    fun provideloginservice(): loginservice {  
        return loginserviceimpl.builder().build()  
    }  
}
interface loginservice {  
    fun login()  
}
class loginserviceimpl private constructor(): loginservice {  
    class builder {  
        fun build(): loginservice {  
            return loginserviceimpl()  
        }  
    }  
    init {  
        println("loginserviceimpl created")  
    }  
    override fun login() {  
        println("login")  
    }  
}
class userremotedatasource @inject constructor(  
    private val analyticsservice: analyticsservice,  
    private val loginservice: loginservice  
){  
    init {  
        println("userremotedatasource created")  
    }  
    fun login(){  
        loginservice.login()  
        analyticsservice.logevent("login")  
    }  
}

同一类型提供多个绑定

provides如果不带限定标签,只会返回一种类型的实现。
可以通过限定标签来区分,返回对应的实现。
hilt限定符默认提供了 @applicationcontex 和 @activitycontext,用来提供两种不同类型的context

@qualifier  
@retention(annotationretention.binary)  
annotation class debuglog  
  
@qualifier  
@retention(annotationretention.binary)  
annotation class errorlog  
  
@module  
@installin(singletoncomponent::class)  
object logservicemodule {  
    @debuglog  
    @provides    
    fun providedebuglogger(): logservice {  
        return logservicedebugimpl()  
    }  
  
    @errorlog  
    @provides    
    fun provideerrorlogger(): logservice {  
        return logserviceerrorimpl()  
    }  
}
@module  
@installin(activitycomponent::class)  
class loginservicemodule {  
    @provides  
    fun provideloginservice(@debuglog logservice: logservice): loginservice {  
        return loginserviceimpl.builder().build(logservice)  
    }  
}
class loginserviceimpl private constructor(val logservice:logservice): loginservice {  
    class builder {  
        fun build(logservice:logservice): loginservice {  
            return loginserviceimpl(logservice)  
        }  
    }  
    init {  
        logservice.log("loginserviceimpl created")  
    }  
    override fun login() {  
        logservice.log("login")  
    }  
}
class analyticsserviceimpl @inject constructor(  
    @applicationcontext val context: context,  
    @errorlog val logservice: logservice  
) : analyticsservice {  
    override fun logevent(eventname: string) {  
        logservice.log("context: $context logevent: $eventname")  
    }  
}

hilt为安卓类生成的组件

组件生命周期和作用域

hilt组件 创建时机 销毁时机 作用域 备注
singletoncomponent application#oncreate() application被销毁 @singleton 相当于是单例的
activitycomponent activity#oncreate() activity#ondestroy() @activityscoped 会随着生命周期注入
activityretainedcomponent 首次activity#oncreate() 最后一次activity#ondestroy() @activityretainedscoped fragment的viewmodel会随着fragment回收,但activityretainedcomponent只会随着activity回收,比viewmodel生命周期更长。
viewmodelcomponent viewmodel 已创建 viewmodel 已销毁 @viewmodelscoped 和viewmodel的生命周期相同
viewcomponent view#super() view 已销毁 @viewscoped 和view的生命周期相同
viewwithfragmentcomponent view#super() view的拥有者被销毁 @viewscoped带有 @withfragmentbindings注解的view 比如fragment导航离开屏幕,fragment还在,但view被销毁时仍然保留。
fragmentcomponent fragment#onattach() fragment#ondestroy() @fragmentscoped 和fragment的生命周期相同
servicecomponent service#oncreate() service#ondestroy() @servicescoped 和service的生命周期相同

组件默认绑定

可以使用model安装到默认绑定,实现注入。比如上面提到的"同一类型提供多个绑定"

安卓组件 默认绑定
singletoncomponent application
activityretainedcomponent application
viewmodelcomponent savedstatehandle
activitycomponent application、activity
fragmentcomponent application、activity、fragment
viewcomponent application、activity、view
viewwithfragmentcomponent application、activity、fragment、view
servicecomponent application、service

在hilt不支持的类中注入依赖项

使用@entrypoint让任意接口可以被注入.
使用@entrypointaccessors获取被注入的对象.
因为是singletoncomponent,所以要使用application的context 。如果是activitycomponent就需要使用activity的context。

class examplecontentprovider : contentprovider() { 
	@entrypoint
	@installin(singletoncomponent::class)
	interface examplecontentproviderentrypoint { 
		fun analyticsservice(): analyticsservice 
	}
	fun dosomething(){
		val appcontext = context?.applicationcontext ?: throw illegalstateexception()
		val hiltentrypoint = entrypointaccessors.fromapplication(appcontext, examplecontentproviderentrypoint::class.java)
		val analyticsservice = hiltentrypoint.analyticsservice()
	}
}
免费资源网,https://freexyz.cn/
返回顶部
顶部
网站地图