Dagger 2 在 activity 中注入 DialogUtils class
Dagger 2 Inject DialogUtils class in activity
我正在尝试将我的 DialogUtils
class 注入到我的 LoginActivity
.下面的代码正在运行,但我只是在初始化 DialogUtils
而不是注入它。
DialogUtils.kt
class DialogUtils constructor(context: Context) {
private val dialog: AlertDialog
init {
dialog = AlertDialog.Builder(context)
.setPositiveButton(R.string.ok) { dialog, _ ->
dialog.dismiss()
}
.create()
}
fun showDialog(title: String, msg: String) {
dialog.setTitle(title)
dialog.setMessage(msg)
dialog.show()
}
fun clear() {
if (dialog.isShowing) {
dialog.dismiss()
}
}
}
LoginActivity.kt
class LoginActivity : DaggerAppCompatActivity() {
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
private val loginViewModel by lazy {
ViewModelProviders.of(this, viewModelFactory).get(LoginViewModel::class.java)
}
private lateinit var dialogUtils: DialogUtils
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
dialogUtils = DialogUtils(this)
}
override fun onPause() {
super.onPause()
dialogUtils.clear()
}
}
我想做的是在我的 LoginActivity
中 @Inject dialogUtils: DialogUtils
但为此我需要 activity 上下文。我怎样才能做到这一点,请记住 DialogUtils
用于许多活动。
你的设计有一个缺陷,它甚至与 DI 或 Dagger 无关:你的 DialogUtils
class 将使用普通的 AlertDialog
s 而不是 DialogFragment
秒。这意味着您的 class 生成的对话框不会持续显示在屏幕上(例如,在 activity 重新启动期间,如设备旋转时),这会带来糟糕的用户体验。
所以我要做的是编写一个自定义 AlertDialogFragment
实现,通过回调与其主机(可能是 Activity
或 Fragment
)进行通信,这意味着如果您的对话框片段实现找到一个实现其回调并回传用户操作(例如按钮点击等)的主机,它会在某些地方查找。这是我通常在我的项目中用于此目的的一些实用代码:
inline fun <reified T> Fragment.requireCallback(): T =
findCallback() ?: error("No parent / target found or parent / target does not implement " + T::class.java)
inline fun <reified T> Fragment.findCallback(): T? {
val callback: Any? = parentFragment ?: targetFragment ?: context
return callback as? T
}
这可以像这样进入您的 AlertDialogFragment
实现:
class AlertDialogFragment : DialogFragment() {
private lateinit var listener: Listener
interface Listener {
fun onDialogResult(requestCode: Int, result: AlertDialogResult)
}
override fun onAttach(context: Context) {
super.onAttach(context)
listener = requireCallback()
}
...
}
现在,当您在父级 Activity
或 Fragment
中实例化对话框时,您只需让该父级实现 AlertDialogFragment.Listener
接口,然后将其结果转发给您的ViewModel
- 好处是:这将在 activity 重新启动后起作用!
您可能想知道是否需要在主机上为每个对话实现回调;我个人的规则是:对于主要确认操作的一键式对话框(如错误对话框),我不需要回调存在(即使用普通的 findCallback()
),而对于特殊的 DecisionDialogFragment
实现有多个按钮可以点击 我通常需要回调,所以如果回调没有在主机上实现,我的应用程序实际上会崩溃,因为这通常意味着我这边缺少一段代码。
您可以将 LoginActivity
(和所有其他)绑定为模块中的 Context
,然后将该模块添加到用于注入 Activity 的组件中。然后您的实用程序可以 inject/use Context
没有问题。
例如在您的登录模块中
@Module
interface LoginModule {
@Binds
bindActivityContext(activity: Context) : Context
}
...然后将模块添加到您的组件...
@(Sub)Component(modules=[..., LoginModule::class])
interface LoginComponent {
// ...
}
如果您已经绑定了一个上下文,您可以使用 @Named(..)
或任何其他限定符注释来绑定另一个上下文。您也可以直接将其绑定为 Activity
。
我正在尝试将我的 DialogUtils
class 注入到我的 LoginActivity
.下面的代码正在运行,但我只是在初始化 DialogUtils
而不是注入它。
DialogUtils.kt
class DialogUtils constructor(context: Context) {
private val dialog: AlertDialog
init {
dialog = AlertDialog.Builder(context)
.setPositiveButton(R.string.ok) { dialog, _ ->
dialog.dismiss()
}
.create()
}
fun showDialog(title: String, msg: String) {
dialog.setTitle(title)
dialog.setMessage(msg)
dialog.show()
}
fun clear() {
if (dialog.isShowing) {
dialog.dismiss()
}
}
}
LoginActivity.kt
class LoginActivity : DaggerAppCompatActivity() {
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
private val loginViewModel by lazy {
ViewModelProviders.of(this, viewModelFactory).get(LoginViewModel::class.java)
}
private lateinit var dialogUtils: DialogUtils
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
dialogUtils = DialogUtils(this)
}
override fun onPause() {
super.onPause()
dialogUtils.clear()
}
}
我想做的是在我的 LoginActivity
中 @Inject dialogUtils: DialogUtils
但为此我需要 activity 上下文。我怎样才能做到这一点,请记住 DialogUtils
用于许多活动。
你的设计有一个缺陷,它甚至与 DI 或 Dagger 无关:你的 DialogUtils
class 将使用普通的 AlertDialog
s 而不是 DialogFragment
秒。这意味着您的 class 生成的对话框不会持续显示在屏幕上(例如,在 activity 重新启动期间,如设备旋转时),这会带来糟糕的用户体验。
所以我要做的是编写一个自定义 AlertDialogFragment
实现,通过回调与其主机(可能是 Activity
或 Fragment
)进行通信,这意味着如果您的对话框片段实现找到一个实现其回调并回传用户操作(例如按钮点击等)的主机,它会在某些地方查找。这是我通常在我的项目中用于此目的的一些实用代码:
inline fun <reified T> Fragment.requireCallback(): T =
findCallback() ?: error("No parent / target found or parent / target does not implement " + T::class.java)
inline fun <reified T> Fragment.findCallback(): T? {
val callback: Any? = parentFragment ?: targetFragment ?: context
return callback as? T
}
这可以像这样进入您的 AlertDialogFragment
实现:
class AlertDialogFragment : DialogFragment() {
private lateinit var listener: Listener
interface Listener {
fun onDialogResult(requestCode: Int, result: AlertDialogResult)
}
override fun onAttach(context: Context) {
super.onAttach(context)
listener = requireCallback()
}
...
}
现在,当您在父级 Activity
或 Fragment
中实例化对话框时,您只需让该父级实现 AlertDialogFragment.Listener
接口,然后将其结果转发给您的ViewModel
- 好处是:这将在 activity 重新启动后起作用!
您可能想知道是否需要在主机上为每个对话实现回调;我个人的规则是:对于主要确认操作的一键式对话框(如错误对话框),我不需要回调存在(即使用普通的 findCallback()
),而对于特殊的 DecisionDialogFragment
实现有多个按钮可以点击 我通常需要回调,所以如果回调没有在主机上实现,我的应用程序实际上会崩溃,因为这通常意味着我这边缺少一段代码。
您可以将 LoginActivity
(和所有其他)绑定为模块中的 Context
,然后将该模块添加到用于注入 Activity 的组件中。然后您的实用程序可以 inject/use Context
没有问题。
例如在您的登录模块中
@Module
interface LoginModule {
@Binds
bindActivityContext(activity: Context) : Context
}
...然后将模块添加到您的组件...
@(Sub)Component(modules=[..., LoginModule::class])
interface LoginComponent {
// ...
}
如果您已经绑定了一个上下文,您可以使用 @Named(..)
或任何其他限定符注释来绑定另一个上下文。您也可以直接将其绑定为 Activity
。