美文网首页素心若雪-146号消零专题
android遇坑-requestFeature() must

android遇坑-requestFeature() must

作者: return_toLife | 来源:发表于2021-07-13 18:14 被阅读0次

    问题现象:

    在调试app的时候点击弹窗DialogFragment突然崩溃,而且诡异的是线上又没有出现,代码也没改动

      android.util.AndroidRuntimeException: requestFeature() must be called before adding content
    

    问题分析:

    崩溃日志指向下面的requestFeature

       @Override
        public void onActivityCreated(Bundle savedInstanceState) {
            Window window = getDialog().getWindow();
            WindowManager.LayoutParams windowParams = window.getAttributes();
            window.requestFeature(Window.FEATURE_NO_TITLE);
            window.setWindowAnimations(R.style.dialog_theme_intro_style);
            super.onActivityCreated(savedInstanceState);
            }
        }
    

    通过源码看到异常抛出原因如下(api 28) :

        //PhoneWindow.class
        @Override
        public boolean requestFeature(int featureId) {
            if (mContentParentExplicitlySet) {
                throw new AndroidRuntimeException("requestFeature() must be called before adding content");
            }
         ...
         ...
         ...
       }
    

    而将mContentParentExplicitlySet设置为true的地方有两处

        //PhoneWindow.class
       @Override
        public void setContentView(int layoutResID) {
             ...
             ...
            mContentParentExplicitlySet = true;
        }
    
        @Override
        public void setContentView(View view, ViewGroup.LayoutParams params) {
            ...
            ...
            mContentParentExplicitlySet = true;
        }
    

    那么问题很明显了,就是在调用requestFeature之前,setContentView先调用了就会发生崩溃


    那么回到前面的问题出现的位置,super.onActivityCreated内部会调用setContentView,那我已经在他前面了啊,为什么还会出错,说明还有其他地方调用了,那搜索一下DialogFragment中调用的setContentView地方,发现只有一个地方调用,就是onActivityCreated

        //DialogFragment
    
        @Override
        public void onActivityCreated(@Nullable Bundle savedInstanceState) {
            ...
            ...
            mDialog.setContentView(view);
            ...
            ...
        }
    
    image.png

    问题解决:

    通过AndroidStudio中的调试工具profiler,发现setContentView的调用链如下:

    image.png

    然后我搜索了DialogFragment中的onChange方法,发现并没有这个方法

    image.png

    然后通过搜索DialogFragment中的成员变量dialog引用发现,项目存在多个fragment的版本


    image.png

    而在最新的1.3.0-rc01版本中发现了如下代码:

     private Observer<LifecycleOwner> mObserver = new Observer<LifecycleOwner>() {
            @SuppressLint("SyntheticAccessor")
            @Override
            public void onChanged(LifecycleOwner lifecycleOwner) {
                if (lifecycleOwner != null && mShowsDialog) {
                    View view = requireView();
                    if (view.getParent() != null) {
                        throw new IllegalStateException(
                                "DialogFragment can not be attached to a container view");
                    }
                    if (mDialog != null) {
                        if (FragmentManager.isLoggingEnabled(Log.DEBUG)) {
                            Log.d(TAG, "DialogFragment " + this + " setting the content view on "
                                    + mDialog);
                        }
                        mDialog.setContentView(view);
                    }
                }
            }
        };
    
      @MainThread
        @Override
        public void onAttach(@NonNull Context context) {
            super.onAttach(context);
            getViewLifecycleOwnerLiveData().observeForever(mObserver);
            if (!mShownByMe) {
                // If not explicitly shown through our API, take this as an
                // indication that the dialog is no longer dismissed.
                mDismissed = false;
            }
        }
    

    最后通过调试验证证实,就是这里提前触发调用了setContentView,导致后面调用requestFeature发生崩溃,而因为存在多个版本的fragment,线上使用的和debug使用的版本不一样,所以线上没出现崩溃,debug出现了崩溃

    总结

    1. 查看源码的时候一定要注意多个版本的问题
    2. fragment中尽量不要使用requestFeature去对window做处理,应该使用style或者放到activity中处理

    相关文章

      网友评论

        本文标题:android遇坑-requestFeature() must

        本文链接:https://www.haomeiwen.com/subject/wtyspltx.html