当我尝试关闭一个DialogFragment时,我在crashlitycs中得到了相当多的错误。这是我得到的错误:
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,我无法复制它,所以我不能给出关于这个问题的更多细节。
发布于 2021-01-29 22:41:55
不应该
viewLifecycleOwner.observe(viewModel.showErrorAndExit, {
showGenericError { activity?.onBackPressed() }
})实际上是
viewModel.showErrorAndExit.observe(viewLifecycleOwner, Observer {
showGenericError { activity?.onBackPressed() }
})发布于 2021-01-31 20:00:04
我在crashlitycs中遇到了很多错误
我想你不知道重现坠机的确切步骤,对吧?在DialogFragment仍显示的情况下重新创建活动后,可能会发生崩溃。例如: open对话框-> rotate screen -> dismiss对话框。
屏幕旋转后会发生什么?将创建新活动,DialogFragment将从旧活动分离并附加到新活动。但是您的DialogFragment在其dismissListener回调中保留了对旧活动的引用。因此,您尝试在生命周期阶段被“销毁”的活动上调用.onBackPressed()。这在Android框架中是被禁止的,这可能是你使用NullPointerException的原因。
那么,你能用它做什么呢?
我在这里看到了三种不同的解决方案:
viewModel.closeScreen()这样的东西,ViewModel会找到当前的activity并对其调用.onBackPressed()。override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val dialog = childFragmentManager.findFragmentByTag(TAG)
if (dialog != null) {
dialog.setOnDismissListener {
activity?.onBackPressed()
}
}
...
},
android:configChanges="orientation"即可。但不要忘记,活动可以重新创建的原因有很多,屏幕旋转只是其中之一!发布于 2021-02-02 04:39:49
一种快速的解决方法是使用接口/契约,而不是直接访问托管活动,这在SOLID世界中是被禁止的操作。因此,不是:
viewLifecycleOwner.observe(viewModel.showErrorAndExit, {
showGenericError { activity?.onBackPressed() }})使用
viewLifecycleOwner.observe(viewModel.showErrorAndExit, {
showGenericError { loosedCopuledActivity?.onBackPressTriggered() }})然后在父活动中实现接口。
class SomeHostActivity: AppCompatActivity(), OnBackPressCallback{}然后在某个地方定义你的界面:
interface OnBackPressCallback{
fun onBackPressTriggered()
}此外,您还需要在对话框中的某个位置定义您的loosedCoupledActivity,因此我们可以这样做:
fun onActivityCreate(bluhbluh: BluhBluh){
super.onActivityCreate(bluhbluh)
if(requireActivity() is OnBackPressCallback){
loosedCoupledActivity = requireActivity()
}https://stackoverflow.com/questions/65581485
复制相似问题