java.lang.IllegalArgumentException: Wrong state class, expecting View State but received class an...

记一次id重复引发的闪退。


    java.lang.IllegalArgumentException: Wrong state class, expecting View State but received class androidx.recyclerview.widget.RecyclerView$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/0x1. Make sure other views do not use the same id.
        at android.view.View.onRestoreInstanceState(View.java:21045)
        at android.view.View.dispatchRestoreInstanceState(View.java:21017)
        at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:4000)
        at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:4006)
        at android.view.View.restoreHierarchyState(View.java:20995)
        at androidx.fragment.app.Fragment.restoreViewState(Fragment.java:548)
        at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:907)
        at androidx.fragment.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManagerImpl.java:1238)
        at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:434)
        at androidx.fragment.app.FragmentManagerImpl.executeOps(FragmentManagerImpl.java:2079)
        at androidx.fragment.app.FragmentManagerImpl.executeOpsTogether(FragmentManagerImpl.java:1869)
        at androidx.fragment.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManagerImpl.java:1824)
        at androidx.fragment.app.FragmentManagerImpl.execPendingActions(FragmentManagerImpl.java:1727)
        at androidx.fragment.app.FragmentManagerImpl$2.run(FragmentManagerImpl.java:150)
        at android.os.Handler.handleCallback(Handler.java:938)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loopOnce(Looper.java:201)
        at android.os.Looper.loop(Looper.java:288)
        at android.app.ActivityThread.main(ActivityThread.java:7838)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)

页面复现在ReactFragment里面,来回切换fragment导致闪退,发现问题步骤:
一、在fragment内的声明周期打印日志,看是在哪个声明周期出现问题
二、打印fragment内部所有view或者viewgroup的id,可以看到是哪两个id重复

一、声明周期

1、通过打印reactfragment的声明周期,发现fragment切换时,会走 onDestroyView ,切回reactfragment,会重新走onViewCreated。
2、问题解决步骤,不让ReactFragment走onDestroyView,FragmentTabHost 重写 doTabChanged 里的 detach和attach,改成hide和show

二、打印fragment所有view的id

1、在onViewCreated 打印view和viewgroup的id


  @Override
  public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    Log.e("东方不败", "ReactFragment-onViewCreated-2, " + view.getId());
    getId(view, "0");
  }
// 获取id
  private void getId(View view, String index) {
    if (view instanceof ViewGroup) {
      ViewGroup vg = (ViewGroup) view;
      getString(view, 1, index);
      for (int i = 0; i < vg.getChildCount(); i++) {
        View vc = vg.getChildAt(i);
        getString(vc, 3, index);
        getId(vc, "" + index + "-" + i);
      }
    } else {
      getString(view, 2, index);
    }
  }
// 如果是textview 获取text内容,其他直接打印view
  private void getString(View view, int tag, String index) {
    String vs = view.toString();
    StringBuilder sb = new StringBuilder();
    sb.append(index);
    sb.append(":");
    if (tag == 2) {
      if (view instanceof ReactTextView) {
        sb.append(((ReactTextView) view).getText());
      } else if (view instanceof ReactImageView) {
        sb.append("ReactImageView");
      } else {
        sb.append(vs);
      }
    } else {
      sb.append(vs);
    }
    sb.append(" ---- id:");
    sb.append(view.getId());
    Log.e("东方不败-" + tag, sb.toString());
  }

打印的日志如下:


东方不败: ReactFragment-onViewCreated-2, -1
东方不败-1: 0:android.widget.RelativeLayout{cb2969a V.E...... ......ID 0,0-1080,2152} ---- id:-1
东方不败-3: 0:com.facebook.react.ReactRootView{7093bcb V.E...... .......D 0,0-1080,2152 #1} ---- id:1
东方不败-1: 0-0:com.facebook.react.ReactRootView{7093bcb V.E...... .......D 0,0-1080,2152 #1} ---- id:1
东方不败-3: 0-0:com.facebook.react.views.view.ReactViewGroup{a4c2b50 V.E...... .......D 0,0-1080,2152 #31} ---- id:49
东方不败-1: 0-0-0:com.facebook.react.views.view.ReactViewGroup{a4c2b50 V.E...... .......D 0,0-1080,2152 #31} ---- id:49
......
......
东方不败-3: 0-0-0-0-0-0-0-0-0-0-0:com.reactnativepagerview.NestedScrollableHost{a48a774 V.E...... .......D 0,243-1080,2152 #a3} ---- id:163
东方不败-1: 0-0-0-0-0-0-0-0-0-0-0-3:com.reactnativepagerview.NestedScrollableHost{a48a774 V.E...... .......D 0,243-1080,2152 #a3} ---- id:163
东方不败-3: 0-0-0-0-0-0-0-0-0-0-0-3:androidx.viewpager2.widget.ViewPager2{be9e3f V.E...... .......D 0,0-1080,1909} ---- id:-1
东方不败-1: 0-0-0-0-0-0-0-0-0-0-0-3-0:androidx.viewpager2.widget.ViewPager2{be9e3f V.E...... .......D 0,0-1080,1909} ---- id:-1
东方不败-3: 0-0-0-0-0-0-0-0-0-0-0-3-0:androidx.viewpager2.widget.ViewPager2$RecyclerViewImpl{91ac30c VFED..... .......D 0,0-1080,1909 #1} ---- id:1
东方不败-1: 0-0-0-0-0-0-0-0-0-0-0-3-0-0:androidx.viewpager2.widget.ViewPager2$RecyclerViewImpl{91ac30c VFED..... .......D 0,0-1080,1909 #1} ---- id:1
东方不败-3: 0-0-0-0-0-0-0-0-0-0-0-3-0-0:android.widget.FrameLayout{f1ae455 V.E...... .......D 0,0-1080,1909} ---- id:-1
东方不败-1: 0-0-0-0-0-0-0-0-0-0-0-3-0-0-0:android.widget.FrameLayout{f1ae455 V.E...... .......D 0,0-1080,1909} ---- id:-1

可以看出 androidx.viewpager2.widget.ViewPager2$RecyclerViewImpl 和 com.facebook.react.ReactRootView 的id重复。
接着看为什么会重复?
ViewPager2里面的RecyclerViewImpl的id是通过view设置的

mRecyclerView = new RecyclerViewImpl(context);
mRecyclerView.setId(ViewCompat.generateViewId());

通过这种方式生成的id会自增,那就说明可能ReactRootView的id不是通过这种方法生成的,如果不是,那在ReactFragment里面手动设置下id就行,更或者,在ReactFragment里面直接调用下ViewCompat.generateViewId() 就行,调用一次sNextGeneratedId 就会自增,这样就不用出现重复id了。
2、再跟踪下ReactRootView的id生成方式
通过 npm start的终端日志可以看出 rootTag 是1
ReactRootView ---> runApplication ---> getRootViewTag,在看设置tag的地方
反查引用,setRootViewTag ---> attachRootViewToInstance


    final int rootTag;

    if (reactRoot.getUIManagerType() == FABRIC) {
      rootTag =
          uiManager.startSurface(
              reactRoot.getRootViewGroup(),
              reactRoot.getJSModuleName(),
              initialProperties == null
                  ? new WritableNativeMap()
                  : Arguments.fromBundle(initialProperties),
              reactRoot.getWidthMeasureSpec(),
              reactRoot.getHeightMeasureSpec());
      reactRoot.setRootViewTag(rootTag);
      reactRoot.setShouldLogContentAppeared(true);
    } else {
      rootTag =
          uiManager.addRootView(
              reactRoot.getRootViewGroup(),
              initialProperties == null
                  ? new WritableNativeMap()
                  : Arguments.fromBundle(initialProperties),
              reactRoot.getInitialUITemplate());
      reactRoot.setRootViewTag(rootTag);
      reactRoot.runApplication();
    }

继续跟踪反查引用地方
UIManagerModule --> addRootView --> final int tag = ReactRootViewTagGenerator.getNextRootViewTag();

public class ReactRootViewTagGenerator {

  // Keep in sync with ReactIOSTagHandles JS module - see that file for an explanation on why the
  // increment here is 10.
  private static final int ROOT_VIEW_TAG_INCREMENT = 10;

  private static int sNextRootViewTag = 1;

  public static synchronized int getNextRootViewTag() {
    final int tag = sNextRootViewTag;
    sNextRootViewTag += ROOT_VIEW_TAG_INCREMENT;
    return tag;
  }
}

可以看出确实ReactRootView的tag是1,但是不能说明id是1
继续看UIManagerModule --> addRootView的方法

mUIImplementation.registerRootView(rootView, tag, themedRootContext);

registerRootView中

mOperationsQueue.addRootView(tag, rootView);

UIViewOperationQueue ---> addRootView--->NativeViewHierarchyManager--->addRootView--->addRootViewGroup


  protected final synchronized void addRootViewGroup(int tag, View view) {
    if (DEBUG_MODE) {
      FLog.d(TAG, "addRootViewGroup[%d]: %s", tag, (view != null ? view.toString() : "<null>"));
    }
    if (view.getId() != View.NO_ID) {
      FLog.e(
        TAG,
        "Trying to add a root view with an explicit id ("
          + view.getId()
          + ") already "
          + "set. React Native uses the id field to track react tags and will overwrite this field. "
          + "If that is fine, explicitly overwrite the id field to View.NO_ID before calling "
          + "addRootView.");
    }

    mTagsToViews.put(tag, view);
    mTagsToViewManagers.put(tag, mRootViewManager);
    mRootTags.put(tag, true);
    view.setId(tag);
  }

由此可以看出tag就是id,最后会给view的id设置成tag

感兴趣的同学还可以看下uimanager的startSurface方法,请自行跟踪,同理,根ReactRootView的id也是1

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容