Dagger 2在活动中注入DialogUtils类

时间:2019-10-12 21:20:03

标签: android kotlin android-alertdialog dagger-2

我正试图将其构造函数中需要活动上下文DialogUtils类注入我的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()
    }

}

我想做的是在我的@Inject dialogUtils: DialogUtilsLoginActivity,但是为此,我需要活动上下文。请记住在许多活动中都使用DialogUtils

2 个答案:

答案 0 :(得分:1)

您的设计中存在一个缺陷,甚至与DI或Dagger也无关:这是因为您的DialogUtils类将使用普通AlertDialog而不是DialogFragment。这意味着您的班级生成的对话框不会在屏幕上持久显示(例如,在活动重启时(例如设备旋转时)),这会带来糟糕的用户体验。

因此,我要做的是编写一个自定义AlertDialogFragment实现,该实现通过回调与其主机(可能是ActivityFragment)进行通信,这意味着您的如果对话框片段实现找到了实现其回调并传达回用户操作(例如按钮单击等)的主机,则它将在某些地方查找。这是我通常在项目中为此使用的一些实用程序代码:


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()
    }
    ...
}

现在,当您在父级ActivityFragment中实例化对话框时,只需让此父级实现AlertDialogFragment.Listener接口,然后将其结果转发到{{ 1}}-很好的是:这将在重新启动活动时起作用!

您可能想知道是否要为每个对话框要求在主机上实现回调?我的个人规则是:对于主要确认操作的一键对话框(例如错误对话框),我不需要回调(即使用普通的ViewModel),而对于特殊的findCallback()实现具有多个按钮的单击通常使我需要回调,所以如果未在主机上实现回调,则我的应用实际上崩溃了,因为这通常意味着我这一边缺少代码。

答案 1 :(得分:0)

您可以在模块中将display(以及所有其他)绑定为LoginActivity,然后将该模块添加到用于注入Activity的组件中。这样,您的utils就可以毫无问题地注入/使用Context

例如在您的LoginModule中

Context

...然后将模块添加到您的组件中...

@Module
interface LoginModule {
  @Binds
  bindActivityContext(activity: Context) : Context
}

如果您已经绑定了上下文,则可以使用@(Sub)Component(modules=[..., LoginModule::class]) interface LoginComponent { // ... } 或任何其他限定符注释来绑定另一个上下文。您也可以将其直接绑定为@Named(..)