起因
在学习安卓网络通信时翻阅了一下《第三行代码》,决定试着用一下Retrofit,实现了HttpService
与UserService
object HttpService {
private const val BASE_URL = "http://10.0.2.2/"
private val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
fun <T> create(serviceClass: Class<T>): T = retrofit.create(serviceClass)
inline fun <reified T> create(): T = create(T::class.java)
}
interface UserService {
@GET("user")
fun getUser(
@Query("userId") userId: Long,
) : Call<User>
}
在使用UserService
时就只需要这样调用
HttpService.create<UserService>().getUser().await().let {
//do something
}
虽然已经很简便了,但实际使用的时候需要每次都通过HttpService
这个单例类来构造Service,感觉还是有一点麻烦。
灵感
我想到了前段时间看到的一个开源项目ViewBindingKTX,它可以在使用视图绑定时方便很多,这是它样例代码中给出的例子
class MainActivity : AppCompatActivity() {
private val binding: ActivityMainBinding by binding()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding.tvHelloWorld.text = "Hello Android!"
}
}
可以看到他使用了by关键词,也就是kotlin的语言特性属性委托。再看看binding()
的具体实现
inline fun <reified VB : ViewBinding> ComponentActivity.binding() = lazy {
inflateBinding<VB>(layoutInflater).also {
setContentView(it.root)
if (this is ViewDataBinding) lifecycleOwner = this@binding
}
}
在具体实现时使用了延迟属性来实现在调用时才实例化对象。这样一来在我们使用Activity对应的binding时就会通过得到ActivityMainBinding
对象实例,像极了依赖注入。
尝试
仔细一想,我们也可以为Service实现通过属性委托得到实例,来实现依赖注入的功能。先照着binding()
来实现一个inject()
inline fun <reified T: BaseHttpService> inject() = lazy {
HttpService.create<T>()
}
再在Activity中使用inject()
class MainActivity : BaseActivity<ActivityMainBinding>() {
private val userService: UserService by inject()
//other code
}
这样一来我们就不需要在Activity中显式地访问HttpService
,通过inject()
就可以在运行时第一次访问userService
时得到对应实例。运行项目,运行成功。
总结
不得不感叹kotlin的强大之处,可以如此轻松地实现依赖注入。并且因为kotlin在实现动态代理时会转为静态代理调用而不是使用反射调用,效率会优于使用java实现。理论上这个用法不止可以在安卓中使用,在其他kotlin项目中也可以轻松实现,在降低耦合的同时简化代码,提高可读性。
原文链接:http://www.cnblogs.com/xuankaicat/p/15657484.html