我不太清楚向静态方法助手类(比如自定义类)注入的最佳方法是什么。
我对Kotlin有点陌生,正如我所了解的,我们可以通过两种方式静态地访问一个方法:
首先,我不确定哪一个是最推荐的(如果有关于此的最佳实践),但是当需要将依赖项注入静态方法类时,我的“问题”就出现了。
让我们来举一个简单的例子:
我有一个名为AWUtils的静态方法类(虽然还没有决定它应该是对象类还是有伴生对象的类,这很可能取决于推荐的注入机制),接下来的方法如下:
fun setAmpersand2Yellow(text2Replace: String, target: String): String {
return text2Replace.replace(
target, "<span style=\"color:" +
app.drawerFooterColor + ";\">" + target + "</span>"
)
}
在这里,app是我的AppSettings类的实例,它保存了所有的应用程序配置,所以,正如您所看到的,setAmpersand2Yellow需要AppSettings,当然,我不会以任何方式将它作为参数传递,因此它是AWUtils依赖的。
为静态方法使用AWUtils作为伴随对象的类,据我所知,我不能直接将AppSettings注入公司对象(至少我不能进行构造函数注入,如果我错了请告诉我),如果我注入伙伴对象父类(AWUtils)构造函数,那么我不知道如何从伙伴对象本身(子构造器)访问这些依赖项。
如果我在AWUtils中使用字段注入作为类,那么它会比后发字段更抱怨没有初始化,而且我不知道如何处理这个问题,因为据我所知,延迟字段是在onCreate中初始化的,这在这类类中并不存在。
另一种可能是在调用方法之前使用带有字段的对象并以静态方式设置调用方的依赖项值,例如:
object AWUtils {
var app: AppSettings? = null
fun setAmpersand2Yellow(text2Replace: String, target: String): String {
return text2Replace.replace(
target, "<span style=\"color:" +
app.drawerFooterColor + ";\">" + target + "</span>"
)
}
}
@AndroidEntryPoint
class OtherClass
@Inject constructor(private val app: AppSettings) {
fun AnyFunction() {
var mystr = "whatever"
AWUtils.app = app
var yellowStr = AWUtils.setAmpersand2Yellow(myStr)
}
}
最后,我不确定如何向静态方法类提供依赖关系,以及应该选择哪种形式的“静态”类。
编辑1:
除了我的ApSettings类之外,我还需要一个上下文,例如在下一个isTablet方法中:
val isTablet: String
get() {
return ((context.resources.configuration.screenLayout
and Configuration.SCREENLAYOUT_SIZE_MASK)
>= Configuration.SCREENLAYOUT_SIZE_LARGE)
}
最后,我需要一个上下文和我的AppSettings (或任何其他自定义类)被注入到一个带有静态方法的类中。
编辑2:
我可以(从活动中)做:
AWUtils.context = this
AWUtils.app = app
var isTablet = AWUtils.isTablet
而且它可以工作,但是需要为两个字段(或更多的字段)分配值,每次我需要调用静态方法时,我更希望以任何方式注入字段。
这就是依赖注入的目的,不是吗?
编辑3:我开始对Hilt感到厌烦了,我们应该创造的东西是为了简化我们的生活,只会使我们的编程生活变得更加复杂。
发布于 2022-07-30 13:58:34
正如您在注释中所阐明的那样,您希望您的utils类可以轻松地通过代码库访问,因此这个答案将集中在这一点和您最初的问题上。
我对Kotlin有点陌生,正如我所了解的,我们可以通过两种方式静态地访问一个方法: object类或class +伴侣对象。
科特林没有Java风格的静力学。其背后的一个理由是鼓励更可维护的编码实践。静态方法和静态类也是测试代码的噩梦。
在Kotlin中,您可以使用一个对象(但是类+伙伴对象将以同样的方式工作)
object AWUtils {
lateinit var appContext: Context
lateinit var appSettings: AppSettings
fun initialize(
appContext: Context,
appSettings: AppSettings,
// more dependencies go here
) {
this.appContext = appContext
this.appSettings = appSettings
// and initialize them here
}
val isTablet: Boolean
get() = ((appContext.resources.configuration.screenLayout
and Configuration.SCREENLAYOUT_SIZE_MASK)
>= Configuration.SCREENLAYOUT_SIZE_LARGE)
fun setAmpersand2Yellow(text2Replace: String, target: String): String {
return text2Replace.replace(
target, "<span style=\"color:" +
appSettings.drawerFooterColor + ";\">" + target + "</span>"
)
}
}
由于这个对象应该可以在整个应用程序中访问,所以应该尽快初始化它,因此在Application.onCreate
中是如此。
@HiltAndroidApp
class Application : android.app.Application() {
// you can inject other application-wide dependencies here
// @Inject
// lateinit var someOtherDependency: SomeOtherDependency
override fun onCreate() {
super.onCreate()
// initialize the utils singleton object with dependencies
AWUtils.initialize(applicationContext, AppSettings())
}
现在,在应用程序代码中的任何地方,您都可以使用AWUtils
和AppSettings
class OtherClass { // no need to inject AppSettings anymore
fun anyFunction() {
val mystr = "whatever"
val yellowStr = AWUtils.setAmpersand2Yellow(myStr)
// This also works
if (AWUtils.isTablet) {
// and this as well
val color = AWUtils.appSettings.drawerFooterColor
}
}
}
Kotlin中还有另一种编写助手/util函数的方法,称为扩展函数。
您的isTablet
检查可以编写为如下扩展函数
// This isTablet() can be called on any Configuration instance
// The this. part can also be omitted
fun Configuration.isTablet() = ((this.screenLayout
and Configuration.SCREENLAYOUT_SIZE_MASK)
>= Configuration.SCREENLAYOUT_SIZE_LARGE)
// This isTablet() can be called on any Resources instance
fun Resources.isTablet() = configuration.isTablet()
// This isTablet() can be called on any Context instance
fun Context.isTablet() = resources.isTablet()
有了上述扩展函数之后,AWUtils
内部的实现将简化为
val isTablet: Boolean
get() = appContext.isTablet()
在(或引用)实现Context
的任何类中,例如Application
、Activity
、Service
等,您可以简单地调用isTablet()
class SomeActivity : Activity() {
fun someFunction() {
if (isTablet()) {
// ...
}
}
}
在其他以某种方式可用Context
或Resources
的地方,您可以简单地调用resources.isTablet()
class SomeFragment : Fragment() {
fun someFunction() {
if (resources.isTablet()) {
// ...
}
}
}
编辑3:我开始对Hilt感到厌烦了,我们应该创造的东西是为了简化我们的生活,只会使我们的编程生活变得更加复杂。
是的,希尔特专注于构造函数注入,并且只能在非常有限的情况下,在非常有限的情况下,只在带有@AndroidEntryPoint
注解的Android类中,以及在用@HiltAndroidApp
注释时在类内扩展Application
类。
@AndroidEntryPoint
文档
标记一个Android组件类,以便使用标准的Hilt组件进行注入。目前,这支持活动、片段、视图、服务和广播接收器。
如果您觉得需要大量的字段注入,因为您正在使用Kotlin中的“静态”-like对象,请考虑在下一个项目中使用Koin而不是Hilt。
https://stackoverflow.com/questions/73176180
复制相似问题