彻底解决Make sure other views do not use the same id问题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zh_qianwei/article/details/72466081

最近在维护别人写的代码发现了这样的一个错误
类似错误日志如下

错误1: Caused by java.lang.IllegalArgumentException: Wrong state class, expecting View State but received class android.os.Bundle instead. This usually happens when two views of different type have the same id in the same hierarchy. This view's id is id/progress_web_view. Make sure other views do not use the same id.
错误2:java.lang.IllegalArgumentException: Wrong state class, expecting View State but received class xxxxx.yourview$SavedState instead. This usually happens when two views of different type have the same id in the same hierarchy. This view's id is id/progress_web_view. Make sure other views do not use the same id.

这个错误最后都定位在 public void onRestoreInstanceState(Parcelable state) 这个方法内,
以前代码中有一个view重写了系统的这个方法。系统调用onSaveInstanceState 机制大家可以自己找一下
这里主要是怎么解决这个异常
最开始的错误是错误1:,原因分析是在重写的onSaveInstanceState 返回的是Bundle对象,和onRestoreInstanceState对象类型匹配,在调用 super.onRestoreInstanceState(state);是直接抛出异常
原因很明显修改代码如下:

    @Override
    public Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();
        SavedState ss = new SavedState(superState);
        ss.childrenStates = new SparseArray();
        //注释部分是代码中存的一些数据
     //  ss.childrenStates.put(INSTANCE_TEXT_COLOR, getTextColor());
     //  ss.childrenStates.put(INSTANCE_TEXT_SIZE, getProgressTextSize());
     //  ss.childrenStates.put(INSTANCE_REACHED_BAR_HEIGHT, getReachedBarHeight());
     //  ss.childrenStates.put(INSTANCE_TEXT_VISIBILITY, getProgressTextVisibility());
        return ss;
    }
      @Override
    public void onRestoreInstanceState(Parcelable state) {
        if (state == null) return ;
        if (state instanceof SavedState){
            SavedState ss = (SavedState) state;
                if (ss != null  && ss.childrenStates != null) {
                SparseArray childrenStates = ss.childrenStates;
              //  mTextColor = (int) childrenStates.get(INSTANCE_TEXT_COLOR);
              //  mTextSize = (float) childrenStates.get(INSTANCE_TEXT_SIZE);
                       }
            super.onRestoreInstanceState(state);
        }
    }

SavedState 代码如下

   static class SavedState extends BaseSavedState {
        SparseArray childrenStates;

        SavedState(Parcelable superState) {
            super(superState);
        }

        private SavedState(Parcel in, ClassLoader classLoader) {
            super(in);
            childrenStates = in.readSparseArray(classLoader);
        }

        @Override
        public void writeToParcel(Parcel out, int flags) {
            super.writeToParcel(out, flags);
            out.writeSparseArray(childrenStates);
        }

        public static final ClassLoaderCreator<SavedState> CREATOR
                = new ClassLoaderCreator<SavedState>() {
            @Override
            public SavedState createFromParcel(Parcel source, ClassLoader loader) {
                return new SavedState(source, loader);
            }

            @Override
            public SavedState createFromParcel(Parcel source) {
                return createFromParcel(null);
            }

            public SavedState[] newArray(int size) {
                return new SavedState[size];
            }
        };
    }

在对应的activity里面自己调用了这2个方法,代码如下

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_store_layout);
       //.....do something

        if (savedInstanceState != null) {
            webview.getWebViewProgressBar().onRestoreInstanceState(savedInstanceState.getParcelable("progressBar"));
        }
    }

    public void onSaveInstanceState(Bundle outState) {
        outState.putParcelable("progressBar", webview.getWebViewProgressBar().onSaveInstanceState());
        super.onSaveInstanceState(outState);
    }

修改完毕测试一切ok,提示这个可以再开发者模式里面打开 不保留活动,按home,在打开应用。
本以为搞定了,但是在其他的功能测试中又发现了新问题。如上面的错误2
这里定位到了MainActivity里面,这次查了一下网络发现很多类似错误,其中有这样的描述

同一个布局里面view的id重复,系统在恢复的时候无法找到对应关系,就会触发异常
activity 加载多个fragment,fragment有加载同样的布局也会出现类似问题

继续查看代码发现MainActivity加载了一个HomeFragmet,其中首页有4个tab用到了2个HomeFragmet实例里面加载的布局一样。这样正好满足2条件。经过修改copy了一分布局fragment_layout2,并把布局内部的id都修改了一般,同时在HomeFragmet view初始化中也做了相应修改。

修改后MainActivity的这个异常就没有了

上线后发现这个异常并没有消失,依然是错误2提示。在错误日志中发现错误机型都集中在5.0以前版本,6.0+的系统没出现过问题。
查看View的源码发现这2个版本出来方式是有差别的:
5.0及以前版本

    protected void onRestoreInstanceState(Parcelable state) {
        mPrivateFlags |= PFLAG_SAVE_STATE_CALLED;
        if (state != BaseSavedState.EMPTY_STATE && state != null) {
            throw new IllegalArgumentException("Wrong state class, expecting View State but "
                    + "received " + state.getClass().toString() + " instead. This usually happens "
                    + "when two views of different type have the same id in the same hierarchy. "
                    + "This view's id is " + ViewDebug.resolveId(mContext, getId()) + ". Make sure "
                    + "other views do not use the same id.");
        }
    }

6.0+

   protected void onRestoreInstanceState(Parcelable state) {
        mPrivateFlags |= PFLAG_SAVE_STATE_CALLED;
        if (state != null && !(state instanceof AbsSavedState)) {
            throw new IllegalArgumentException("Wrong state class, expecting View State but "
                    + "received " + state.getClass().toString() + " instead. This usually happens "
                    + "when two views of different type have the same id in the same hierarchy. "
                    + "This view's id is " + ViewDebug.resolveId(mContext, getId()) + ". Make sure "
                    + "other views do not use the same id.");
        }
        if (state != null && state instanceof BaseSavedState) {
            mStartActivityRequestWho = ((BaseSavedState) state).mStartActivityRequestWhoSaved;
        }
    }

从源码中可以看到5.0以前如果你重写了onSave的对应方法,在调用super的时候不满足
if (state != BaseSavedState.EMPTY_STATE && state != null) { 直接就会异常,这个和id重复没有关系,在6.0种判断是if (state != null && !(state instanceof AbsSavedState)) 这就是造成上面代码异常的主要原因

这里可以再super加上try catche,或者不重写这2个方法(5这个在低版本不会保存,要注意)

猜你喜欢

转载自blog.csdn.net/zh_qianwei/article/details/72466081
今日推荐