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 将使用普通的 AlertDialogs 而不是 DialogFragment秒。这意味着您的 class 生成的对话框不会持续显示在屏幕上(例如,在 activity 重新启动期间,如设备旋转时),这会带来糟糕的用户体验。

所以我要做的是编写一个自定义 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 接口,然后将其结果转发给您的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