看源码修BUG

3,529 阅读2分钟

问题描述

crash,小同学不知道如何修复,通过wx发我帮忙看看. 通过这个demo,帮他演示下修复过程和利用Git.

  Process: com.eno.enotest, PID: 20975
    java.lang.IllegalStateException: Fragment no longer exists for key f0: unique id e1e49495-203d-445f-941a-53f0d4256e05
        at androidx.fragment.app.FragmentManagerImpl.getFragment(FragmentManagerImpl.java:365)
        at androidx.fragment.app.FragmentStatePagerAdapter.restoreState(FragmentStatePagerAdapter.java:309)
        at androidx.viewpager.widget.ViewPager.onRestoreInstanceState(ViewPager.java:1461)
        at android.view.View.dispatchRestoreInstanceState(View.java:19830)
        at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3858)
        at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3864)
        at android.view.View.restoreHierarchyState(View.java:19808)
        at androidx.fragment.app.Fragment.restoreViewState(Fragment.java:548)

详见: github.com/zhouruikevi…

解决办法(2种)

  1. 在HomeFragment的89行后添加一个函数
        @Nullable
        @Override
        public Parcelable saveState() {
            return null;
        }

  1. 把HomeFragment的FragmentStatePagerAdapter修改成FragmentPagerAdapter

解决思路

Fragment嵌套的时候出现了状态恢复错误.

  1. 一般这种问题考虑怎么让解决异常代码不被执行即可.

( at androidx.fragment.app.FragmentManagerImpl.getFragment(FragmentManagerImpl.java:365) SDK29 中

    @Nullable
    public Fragment getFragment(Bundle bundle, String key) {
        String who = bundle.getString(key);
        if (who == null) {
            return null;
        }
        Fragment f = mActive.get(who);
        if (f == null) {
            throwException(new IllegalStateException("Fragment no longer exists for key "
                    + key + ": unique id " + who));
        }
        return f;
    }

在 if (f == null) 的后面发生了异常,看函数在 if (who == null) 的时候会直接跳出去,问题转化为怎么让who为null.

  1. 往上层调用栈看

at androidx.fragment.app.FragmentStatePagerAdapter.restoreState(FragmentStatePagerAdapter.java:309) 中

    @Override
    public void restoreState(@Nullable Parcelable state, @Nullable ClassLoader loader) {
        if (state != null) {
            Bundle bundle = (Bundle)state;
            bundle.setClassLoader(loader);
            Parcelable[] fss = bundle.getParcelableArray("states");
            mSavedState.clear();
            mFragments.clear();
            if (fss != null) {
                for (int i=0; i<fss.length; i++) {
                    mSavedState.add((Fragment.SavedState)fss[i]);
                }
            }
            Iterable<String> keys = bundle.keySet();
            for (String key: keys) {
                if (key.startsWith("f")) {
                    int index = Integer.parseInt(key.substring(1));
                    Fragment f = mFragmentManager.getFragment(bundle, key);
                    if (f != null) {
                        while (mFragments.size() <= index) {
                            mFragments.add(null);
                        }
                        f.setMenuVisibility(false);
                        mFragments.set(index, f);
                    } else {
                        Log.w(TAG, "Bad fragment at key " + key);
                    }
                }
            }
        }
    }

如何让 Fragment f = mFragmentManager.getFragment(bundle, key);中的bundle中不包含fragment.分析函数发现bundle的赋值是在 Parcelable[] fss = bundle.getParcelableArray("states");中问题也就转化成了怎么让states的Parcelable中不包含值.

  1. 在FragmentStatePagerAdapter类中搜索关键字states,发现函数saveState()

于是覆盖这个函数返回null即可.

结论

  1. 给新同学示范演示下如何通过源码追踪问题.
  2. 利用Git,交流表达.