【发布时间】:2021-04-11 08:33:01
【问题描述】:
当我尝试关闭 DialogFragment 时,我在 crashlititycs 中遇到很多错误。这是我得到的错误:
Attempt to invoke virtual method 'android.os.Handler android.app.FragmentHostCallback.getHandler()' on a null object reference
我得到的线路是这个showGenericError { activity?.onBackPressed() }
viewLifecycleOwner.observe(viewModel.showErrorAndExit, {
showGenericError { activity?.onBackPressed() }
})
这是初始化对话框的方法:
fun showGenericError(actionOnDismiss: (() -> Unit)? = null) {
val manager = childFragmentManager
if (popUpErrorCard == null) {
popUpErrorCard = PopupCard.Builder(R.string.button_try_later)?.apply {
setDescription(R.string.error_card_description_text)
setTitle(R.string.subscribe_error_dialog_title)
setImage(R.drawable.channels_error_popup)
}.build()?.apply {
setDismissListener(object : PopupCard.DismissListener {
override fun onDismiss() {
actionOnDismiss?.invoke()
}
})
}
}
if (popUpErrorCard?.isAdded == false && popUpErrorCard?.isVisible == false && manager.findFragmentByTag(ERROR_DIALOG_TAG) == null) {
popUpErrorCard?.show(manager, ERROR_DIALOG_TAG)
manager.executePendingTransactions()
}
}
我收到错误的行是actionOnDismiss?.invoke()
最后的 DialogFragment 就是这个:
class PopupCard private constructor() : DialogFragment() {
private lateinit var dialog: AlertDialog
private var negativeListener: View.OnClickListener? = null
private var positiveListener: View.OnClickListener? = null
private var dismissLitener: DismissListener? = null
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val builder = AlertDialog.Builder(requireActivity())
val inflater = requireActivity().layoutInflater
val view = inflater.inflate(R.layout.popup_card, null)
@Suppress("UNCHECKED_CAST")
arguments?.let args@{ bundle ->
val negativeText: Int? = bundle.getInt(NEGATIVE_BUTTON_TEXT)
if (negativeText != null && negativeText != 0) {
view.negativeButton.setText(negativeText)
} else {
view.negativeButton.visibility = View.GONE
}
val image: Int? = bundle.getInt(IMAGE_RESOURCE)
image?.let {
view.imageHeader.setImageResource(it)
} ?: run {
view.imageHeader.visibility = View.GONE
}
val titleRes: Int? = bundle.getInt(TITLE_RES)
val titleText: String? = bundle.getString(TITLE)
when {
!titleText.isNullOrBlank() -> {
view.title.text = titleText
}
titleRes != null && titleRes != 0 -> {
view.title.setText(titleRes)
}
else -> view.title.visibility = View.GONE
}
val descriptionRes: Int? = bundle.getInt(DESCRIPTION_RES)
val descriptionText: String? = bundle.getString(DESCRIPTION)
when {
!descriptionText.isNullOrBlank() -> {
view.description.text = descriptionText
}
descriptionRes != null && descriptionRes != 0 -> {
view.description.setText(descriptionRes)
}
else -> view.description.visibility = View.GONE
}
val actionPair = bundle.getInt(POSITIVE_BUTTON_TEXT)
view.positiveButton.setText(actionPair)
}
builder.setView(view)
dialog = builder.create()
view.positiveButton.setOnClickListener {
positiveListener?.onClick(it)
dialog.dismiss()
}
view.negativeButton.setOnClickListener {
negativeListener?.onClick(it)
dialog.dismiss()
}
return dialog
}
fun setOnPositiveClickListener(listener: View.OnClickListener) {
this.positiveListener = listener
}
fun setOnNegativeClickListener(listener: View.OnClickListener) {
this.negativeListener = listener
}
fun setDismissListener(listener: DismissListener) {
this.dismissLitener = listener
}
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
dismissLitener?.onDismiss()
}
interface DismissListener {
fun onDismiss()
}
companion object {
private const val NEGATIVE_BUTTON_TEXT = "PopupCard#NEGATIVE_BUTTON_TEXT"
private const val IMAGE_RESOURCE = "PopupCard#IMAGE_RESOURCE"
private const val TITLE = "PopupCard#TITLE"
private const val TITLE_RES = "PopupCard#TITLE_RES"
private const val DESCRIPTION = "PopupCard#DESCRIPTION"
private const val DESCRIPTION_RES = "PopupCard#DESCRIPTION_RES"
private const val POSITIVE_BUTTON_TEXT = "PopupCard#POSITIVE_BUTTON_TEXT"
}
class Builder(
@StringRes private val positiveText: Int
) {
private var negativeText: Int? = null
@DrawableRes
private var image: Int? = null
@StringRes
private var titleRes: Int? = null
private var titleText: String? = null
@StringRes
private var descriptionRes: Int? = null
private var descriptionText: String? = null
fun setTitle(@StringRes title: Int): Builder {
this.titleRes = title
return this
}
fun setTitle(title: String): Builder {
this.titleText = title
return this
}
fun setDescription(@StringRes description: Int): Builder {
this.descriptionRes = description
return this
}
fun setDescription(description: String): Builder {
this.descriptionText = description
return this
}
fun setNegativeText(@StringRes negativeText: Int): Builder {
this.negativeText = negativeText
return this
}
fun setImage(@DrawableRes image: Int): Builder {
this.image = image
return this
}
fun build(): PopupCard {
val bundle = Bundle().apply {
negativeText?.let {
putInt(NEGATIVE_BUTTON_TEXT, it)
}
image?.let {
putInt(IMAGE_RESOURCE, it)
}
titleRes?.let {
putInt(TITLE_RES, it)
}
titleText?.let {
putString(TITLE, it)
}
descriptionRes?.let {
putInt(DESCRIPTION_RES, it)
}
descriptionText?.let {
putString(DESCRIPTION, it)
}
putInt(POSITIVE_BUTTON_TEXT, positiveText)
}
return PopupCard().apply {
arguments = bundle
}
}
}
}
在 DialogFragment 中出现错误dismissLitener?.onDismiss()
正如您在所有导致错误的行中看到的那样,都有安全调用 (?) 所以我不知道为什么我会收到 NullPointerException 并且我无法重现它,所以我无法提供有关该问题的更多详细信息.
【问题讨论】:
-
这可能是
onBackPressed()调用本身中的某些内容。考虑检查完整的堆栈跟踪,而不仅仅是最上面的行。 -
onBackPressed 是 android sdk 的一部分的方法,不是自定义方法。
-
好吧,发生 NPE 的
android.app.FragmentHostCallback.getHandler调用也可能来自某些平台代码。请注意,它有已弃用的 android.app 片段,而不是 jetpack androidx.app 片段。 -
这很奇怪,因为我使用的是 androidx.app 片段。我不知道谁(库或其他)在使用 android.app 片段,如果您认为它可以帮助找到错误,我可以向您展示我正在使用的导入。
-
你怎么打电话给
showGenericError?你在actionOnDismiss中传递了什么?
标签: android kotlin nullpointerexception android-dialogfragment