相关文章推荐
重感情的蛋挞  ·  Tutorial: Write and ...·  1 年前    · 
飞奔的企鹅  ·  mysql ...·  1 年前    · 

DialogFragment中的getViewLifecycleOwner()导致崩溃

35 人关注

我使用 DialogFragment (onCreateDialog) 和ViewModel来实现。但是,当我试图将 getViewLifecycleOwner() 传递给 LiveData::observe 方法时,我得到了以下错误。

java.lang.IllegalStateException: Can't access the Fragment View's LifecycleOwner when getView() is null i.e., before onCreateView() or after onDestroyView().

是否可以在DialogFragment内使用getViewLifecycleOwner()

1 个评论
ADM
onCreateDialog 中对话框正在创建,尚未创建。请在 onViewCreated() 中尝试。我自己不大使用 LifecycleOwner
android
viewmodel
dialogfragment
android-livedata
Alex.Marynovskyi
Alex.Marynovskyi
发布于 2019-02-19
6 个回答
Ionut Negru
Ionut Negru
发布于 2021-07-20
已采纳
0 人赞同

发生这种情况是因为DialogFragment是如何创建的。 如果你使用 onCreateDialog() ,那么对于这种类型的Fragment就会使用一个稍微不同的生命周期。替换代码1】将不被使用,因此这个Fragment的 viewLifecycleOwner 将不会被初始化。

作为一种变通方法,你可以使用Fragment实例作为观察者的所有者。 .observe(this, Observer {...} 。尽管你会因为使用 this 而不是 viewLifecycleOwner 而得到一个警告。

我使用 this (MyDialogFragment)似乎没有得到警告。这种方法是否比使用 getActivity() requireActivity() 更好?
@ban-geoengineering 只要你不 show DialogFragment多次使用同一个片段实例。如果你这样做,观察者将被第二次订阅,你可能会因为双重观察而出现奇怪的问题。这是因为通过使用 this 作为生命周期的所有者,观察者只有在 onDestroy 被调用时才会被移除,而不是通过简单地解散对话框来调用它。
cactustictacs
cactustictacs
发布于 2021-07-20
0 人赞同

这是官方建议 for DialogFragment s:

Note: 当订阅生命周期感知组件(如 LiveData )时,你不应该将 viewLifecycleOwner 作为使用 Dialogs LifecycleOwner 。相反,请使用 DialogFragment 本身,或者如果你使用Jetpack Navigation,请使用 NavBackStackEntry

所以你可以像平常一样观察事物,但是你要传递 viewLifecycleOwner ,或者当前的backstack条目(例如 findNavController().currentBackStackEntry ),而不是 this 。不需要覆盖 onCreateView 来强制创建一个 viewLifecycleOwner 或其他任何东西。

Kamyar Miremadi
Kamyar Miremadi
发布于 2021-07-20
0 人赞同

你的情况略有不同,但我认为概念是相同的。只要用 this.getActivity() 在你的对话框类中,将其作为 LifeCycleOwner .我有同样的问题,因为我用 LiveData Retrofit LiveData needs a reference. The DialogFragment sets its LifeCycleOwner 在某些时候,但它不是在上面提到的任何方法。通过使用 getActivity() 你可以早在onCreateDialog方法中使用你的观察器。下面是我的部分代码,起初当我试图传递一个null引用时,引起了一些问题 this.getViewLifecycleOwner() 而不是活动。

@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
       FragmentActivity activity = this.getActivity();
       binding = DialogSelectIssuesBinding.inflate(LayoutInflater.from(getContext()));
       RetroRepository.
            getDefault().
            getAllIssues().
            observe(this.getActivity(), listLiveDataResponse -> {
                //ToDo Check for errors and Bind the data here 
       AlertDialog alertDialog = new AlertDialog.Builder(activity)
                            .setView(binding.getRoot())
                            .setTitle("Please select issues from the list below:")
                            .setNegativeButton("CANCEL", null)
                            .setPositiveButton("ADD", null)
                            .create();
       alertDialog.setCanceledOnTouchOutside(false);
       return alertDialog;
    
如果片段被破坏后又被重新创建,但活动没有被破坏怎么办?旧的观察者仍然引用旧的片段,你就得到了一个内存泄漏! viewLifecycleOwner 的全部意义在于当片段被销毁时,移除观察者。替换代码0】的全部意义在于当片段被销毁时删除观察者。
Eric
Eric
发布于 2021-07-20
0 人赞同

发生这种情况是因为 DialogFragment 的生命周期与 Fragment 不同; onCreateDialog onCreateView 之前被调用,所以 viewLifecycleOwner 不可用。我通过以下方式解决了这个问题。

  • implemeting onCreateView instead of onCreateDialog
  • can access viewLifecycleOwner from within onCreateView
  • view returned from onCreateView is put into a dialog for us by DialogFragment ...
  • you will need to create your own buttons, and titles in the dialog...
  • 补充代码。

    class TextInputDialogFragment : DialogFragment() {
        override fun onCreateView(
            inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
        ): View? {
            val viewBinding = FragmentDialogTextInputBinding.inflate(layoutInflater, container, false)
            val titleText = params.title.localize(requireContext())
            viewBinding.toolbar.isVisible = titleText.isNotBlank()
            if (titleText.isNotBlank()) {
                viewBinding.toolbar.title = titleText
            viewBinding.recyclerview.adapter = ListItemAdapter(
                viewLifecycleOwner, requireContext().app.nowFactory, viewModel.fields
            viewBinding.buttonAffirm.setOnClickListener {
                listener.onOkPressed(viewModel.userInputtedText.value)
                dismiss()
            viewBinding.buttonReject.setOnClickListener {
                dismiss()
            viewModel.enablePositiveButton.observe(viewLifecycleOwner) { isEnabled ->
                viewBinding.buttonAffirm.isEnabled = isEnabled
            return viewBinding.root
    

    使用的布局文件

    <?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">
            <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:minHeight="?attr/actionBarSize"
                tools:title="Title" />
            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/recyclerview"
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
            <LinearLayout
                style="?buttonBarStyle"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:clickable="false"
                android:gravity="end"
                android:orientation="horizontal"
                android:padding="@dimen/min_touch_target_spacing_half">
                <Button
                    android:id="@+id/button_reject"
                    style="?buttonBarButtonStyle"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_margin="@dimen/min_touch_target_spacing_half"
                    android:text="@android:string/cancel" />
                <Button
                    android:id="@+id/button_affirm"
                    style="?buttonBarButtonStyle"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_margin="@dimen/min_touch_target_spacing_half"
                    android:text="@android:string/ok" />
            </LinearLayout>
        </LinearLayout>
    </layout>
        
    Delark
    Delark
    发布于 2021-07-20
    0 人赞同

    我的解决方案有点古怪......。

    我的组件在使用getViewLifecycleOwnerLiveData() ....,所以。

    private final MyLifeCycleOwner owner = new MyLifeCycleOwner();
    private final MutableLiveData<LifecycleOwner> result = new MutableLiveData<>();
    @NonNull
    @Override
    public LiveData<LifecycleOwner> getViewLifecycleOwnerLiveData() {
        return result;
    @Override
    public void onDestroyView() {
        super.onDestroyView();
        owner.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
        result.setValue(null);
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        result.setValue(owner);
        owner.getLifecycle();
        owner.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
        return super.onCreateView(inflater, container, savedInstanceState);
    

    因为FragmentViewLifecycleOwner是打包私有的...这就是MyLifeCycleOwner类的原因。

    我不会因为安卓架构上的一些管理不善而改变我的组件......。

    SLH
    SLH
    发布于 2021-07-20
    0 人赞同