使用ViewPager的Android java.lang.IllegalStateException

时间:2018-03-17 06:21:06

标签: android android-viewpager illegalstateexception

我使用ViewPager制作了一个非常简单的Android益智应用程序,让用户可以浏览一系列谜题。我发现生产中存在错误,我不知道如何重现或调试:

kotlin.Long

我该怎么做才能弄清楚如何自己重现这个错误,并弄清楚如何修复它?我的android编程技巧相当有限,上面的堆栈跟踪对我来说非常神秘。

如果它有用,这里是第二个看似相关的堆栈跟踪:

java.lang.IllegalStateException: 
  at android.support.v4.view.ViewPager.a (ViewPager.java:204)
  at android.support.v4.view.ViewPager.c (ViewPager.java:2)
  at android.support.v4.view.ViewPager.onMeasure (ViewPager.java:207)
  at android.view.View.measure (View.java:22002)
  at android.view.ViewGroup.measureChildWithMargins (ViewGroup.java:6580)
  at android.widget.FrameLayout.onMeasure (FrameLayout.java:185)
  at android.view.View.measure (View.java:22002)
  at android.view.ViewGroup.measureChildWithMargins (ViewGroup.java:6580)
  at android.support.v7.internal.widget.ActionBarOverlayLayout.onMeasure (ActionBarOverlayLayout.java:257)
  at android.view.View.measure (View.java:22002)
  at android.view.ViewGroup.measureChildWithMargins (ViewGroup.java:6580)
  at android.widget.FrameLayout.onMeasure (FrameLayout.java:185)
  at android.view.View.measure (View.java:22002)
  at android.view.ViewGroup.measureChildWithMargins (ViewGroup.java:6580)
  at android.widget.LinearLayout.measureChildBeforeLayout (LinearLayout.java:1514)
  at android.widget.LinearLayout.measureVertical (LinearLayout.java:806)
  at android.widget.LinearLayout.onMeasure (LinearLayout.java:685)
  at android.view.View.measure (View.java:22002)
  at android.view.ViewGroup.measureChildWithMargins (ViewGroup.java:6580)
  at android.widget.FrameLayout.onMeasure (FrameLayout.java:185)
  at com.android.internal.policy.DecorView.onMeasure (DecorView.java:721)
  at android.view.View.measure (View.java:22002)
  at android.view.ViewRootImpl.performMeasure (ViewRootImpl.java:2414)
  at android.view.ViewRootImpl.performTraversals (ViewRootImpl.java:2159)
  at android.view.ViewRootImpl.doTraversal (ViewRootImpl.java:1390)
  at android.view.ViewRootImpl$TraversalRunnable.run (ViewRootImpl.java:6762)
  at android.view.Choreographer$CallbackRecord.run (Choreographer.java:966)
  at android.view.Choreographer.doCallbacks (Choreographer.java:778)
  at android.view.Choreographer.doFrame (Choreographer.java:713)
  at android.view.Choreographer$FrameDisplayEventReceiver.run (Choreographer.java:952)
  at android.os.Handler.handleCallback (Handler.java:789)
  at android.os.Handler.dispatchMessage (Handler.java:98)
  at android.os.Looper.loop (Looper.java:169)
  at android.app.ActivityThread.main (ActivityThread.java:6595)
  at java.lang.reflect.Method.invoke (Method.java)
  at com.android.internal.os.Zygote$MethodAndArgsCaller.run (Zygote.java:240)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:767)

我应该发布我的ViewPager代码的缩短版/简化版吗?

编辑:这是完成大部分工作的班级:

java.lang.IllegalStateException: 
  at android.support.v4.view.ViewPager.access$000 (ViewPager.java)
  or                     .access$200 (ViewPager.java)
  or                     .addNewItem (ViewPager.java)
  or                     .calculatePageOffsets (ViewPager.java)
  or                     .canScroll (ViewPager.java)
  or                     .completeScroll (ViewPager.java)
  or                     .determineTargetPage (ViewPager.java)
  or                     .distanceInfluenceForSnapDuration (ViewPager.java)
  or                     .executeKeyEvent (ViewPager.java)
  or                     .getChildRectInPagerCoordinates (ViewPager.java)
  or                     .infoForChild (ViewPager.java)
  or                     .initViewPager (ViewPager.java)
  or                     .isGutterDrag (ViewPager.java)
  or                     .onPageScrolled (ViewPager.java)
  or                     .onSecondaryPointerUp (ViewPager.java)
  or                     .populate (ViewPager.java)
  or                     .recomputeScrollPosition (ViewPager.java)
  or                     .scrollToItem (ViewPager.java)
  or                     .setCurrentItem (ViewPager.java)
  or                     .setCurrentItemInternal (ViewPager.java)
  or                     .smoothScrollTo (ViewPager.java)
  at android.support.v4.view.ViewPager.arrowScroll (ViewPager.java)
  or                     .populate (ViewPager.java)
  or                     .requestParentDisallowInterceptTouchEvent (ViewPager.java)
  at android.support.v4.view.ViewPager.onMeasure (ViewPager.java)
  at android.view.View.measure (View.java:19759)
  at android.view.ViewGroup.measureChildWithMargins (ViewGroup.java:6124)
  at android.widget.FrameLayout.onMeasure (FrameLayout.java:185)
  at android.view.View.measure (View.java:19759)
  at android.view.ViewGroup.measureChildWithMargins (ViewGroup.java:6124)
  at android.support.v7.internal.widget.ActionBarOverlayLayout.onMeasure (ActionBarOverlayLayout.java)
  at android.view.View.measure (View.java:19759)
  at android.view.ViewGroup.measureChildWithMargins (ViewGroup.java:6124)
  at android.widget.FrameLayout.onMeasure (FrameLayout.java:185)
  at android.view.View.measure (View.java:19759)
  at android.view.ViewGroup.measureChildWithMargins (ViewGroup.java:6124)
  at android.widget.LinearLayout.measureChildBeforeLayout (LinearLayout.java:1464)
  at android.widget.LinearLayout.measureVertical (LinearLayout.java:758)
  at android.widget.LinearLayout.onMeasure (LinearLayout.java:640)
  at android.view.View.measure (View.java:19759)
  at android.view.ViewGroup.measureChildWithMargins (ViewGroup.java:6124)
  at android.widget.FrameLayout.onMeasure (FrameLayout.java:185)
  at com.android.internal.policy.DecorView.onMeasure (DecorView.java:687)
  at android.view.View.measure (View.java:19759)
  at android.view.ViewRootImpl.performMeasure (ViewRootImpl.java:2283)
  at android.view.ViewRootImpl.performTraversals (ViewRootImpl.java:2036)
  at android.view.ViewRootImpl.doTraversal (ViewRootImpl.java:1258)
  at android.view.ViewRootImpl$TraversalRunnable.run (ViewRootImpl.java:6348)
  at android.view.Choreographer$CallbackRecord.run (Choreographer.java:871)
  at android.view.Choreographer.doCallbacks (Choreographer.java:683)
  at android.view.Choreographer.doFrame (Choreographer.java:619)
  at android.view.Choreographer$FrameDisplayEventReceiver.run (Choreographer.java:857)
  at android.os.Handler.handleCallback (Handler.java:751)
  at android.os.Handler.dispatchMessage (Handler.java:95)
  at android.os.Looper.loop (Looper.java:154)
  at android.app.ActivityThread.main (ActivityThread.java:6123)
  at java.lang.reflect.Method.invoke (Method.java)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:867)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:757)

如果需要更多解释,请告诉我。

2 个答案:

答案 0 :(得分:1)

我在模拟器中安装了您的应用,并且能够使用猴子工具(https://developer.android.com/studio/test/monkey.html)重现此崩溃。此工具可以高速模拟用户操作,例如点击,触摸,旋转等。我不能手动复制它。

你在问题​​中写道,你想知道如何找出问题所在,所以我会详细解释我的过程。对于实际解决方案,请跳至第5节。

<强> 1。如何知道IllegalStateException的消息

第一个堆栈跟踪表示异常是在ViewPager.java上抛出的,所以我搜索了字符串&#34;抛出新的IllegalStateException&#34;在那个文件中。有不同的版本,但我检查的少数只在 6个地方

中抛出此异常
  • 两个是关于程序化的ViewPager拖拽,但您的应用没有这样做,所以我放弃了这些
  • 两个即将在从ViewPager继承的类中调用超级方法,但由于在您的代码中您只是使用ViewPager,我也放弃了这些方法
  • 一个位于addView(),它没有出现在您的堆栈跟踪上,所以我也放弃了它。

剩下的唯一一个会在populate(int)处抛出,必须这个。但只是为了确定,我检查了第二个堆栈跟踪:

  • 第四个&#34; at&#34;行称ViewPager.onMeasure()被称为。没有&#34;或&#34;在这里划线,所以这是肯定的。

  • 第三个&#34; at&#34; line提供了三个选项。查看来源,populate()只调用onMeasure(),因此必须是populate()

  • 第二个&#34; at&#34; line提供了21个选项,但再次查看源代码,populate(int)只调用populate(int) ...与之前的方法相同,所以它都适合。

以下是throw new IllegalStateException("The application's PagerAdapter changed the adapter's" + " contents without calling PagerAdapter#notifyDataSetChanged!" + " Expected adapter item count: " + mExpectedAdapterCount + ", found: " + N + " Pager id: " + resName + " Pager class: " + getClass() + " Problematic adapter: " + mAdapter.getClass()); 中的投掷代码:

adb shell monkey -p atorch.statspuzzles -v --pct-rotation 20 100000

这就是我第一次回答此问题的原因。

<强> 2。重现错误

在模拟器中下载应用程序后,我从命令行运行了这个:

Log.d()

以非常高的速度发送模拟器100.000半随机触摸事件,旋转,音量增大/减小等,以在压力下测试应用程序。如果使用应用程序的调试版本运行此命令,您应该能够看到我在步骤1中获得的堆栈跟踪。

第3。获取有关错误的信息(主要是推测性的)

从这里开始,您可以在代码中添加很多monkey行,运行monkey,这样可以让您了解用户为了使您的应用崩溃所做的工作。当然我无法做到这一点,所以我用你提供的代码编写了一个小应用程序,并用indexFirstUnsolvedPuzzle()运行它,看看我是否能得到别的东西。我做的是:

  1. 删除了对您的R类的所有引用(例如,用我自己的一个替换您的布局,只使用ViewPager,并用简单的视图替换您的Fragment布局。
  2. onClickListener返回零,仅适用于Android Studio而不是打扰
  3. 在Fragment类中,我添加了一个PuzzleSelection,它启动了一个对话框,因为你的应用有对话框。
  4. 使用SolvePuzzle活动的模拟级别选择随机选择级别在onResume()启动onBackPressed()
  5. 在两个活动中添加Log.d(),只是为了能够记录背压。
  6. 我在每个方法上添加了monkey,以便能够在logcat中遵循该过程。
  7. 我用(section 1) SolvePuzzle@84e25c: back pressed ViewPager{9256292}: scrolled, offset: 9.2589855E-4 AppSectionsPagerAdapter@4b49965: getCount() invoked, we have 3 elements (section 2) Creating Activity PuzzleSelection@580af24. Orientation: portrait Starting puzzle for level 1 ViewPager{9256292}: scrolled, offset: 0.0 AppSectionsPagerAdapter@4b49965: getCount() invoked, we have 3 elements AppSectionsPagerAdapter@4b49965: getCount() invoked, we have 3 elements (section 3) Creating Activity SolvePuzzle@16d7aaffor level 3. Orientation: portrait Creating adapter AppSectionsPagerAdapter@958bebc with 2 elements. AppSectionsPagerAdapter@958bebc: getCount() invoked, we have 2 elements AppSectionsPagerAdapter@958bebc: getCount() invoked, we have 2 elements ViewPager{c002954}: scrollStateChanged to 0 AppSectionsPagerAdapter@4b49965: getCount() invoked, we have 2 elements 运行了这个应用程序,在崩溃之前我在日志中得到了这个:

    SolvePuzzle

    <强> 4。崩溃的原因

    请注意,当SolveActivity不再显示在屏幕上时,第1部分(@ 4b49965)中的适配器会在第2部分中被调用。在创建新的适配器之后,甚至在第3节中调用它。 getCount()的结果在第1节和第3节中与此适配器不同,这意味着适配器已更改其内容,因此抛出了异常。

    很可能这个适配器在其ViewPager完成后仍在使用,因为SolvePuzzle在变为隐形后正在做一些内务处理。问题是旧的Activity读取了一个刚刚由新Activity编写的String数组的引用,而只发生这种情况,因为数组是static的静态成员

    (作为旁注,那些静态数组确实会在你的应用中引起另一个错误:如果选择X级,在那里解决问题并通过祝贺对话框返回菜单,然后选择另一个级别Y,然后按返回两次,你最终在Y级,而不是X级。)

    <强> 5。溶液

    作为一般规则,只有在真正不可变时才使用静态字段,例如常量,或者至少在可以正确同步并发访问时。另一方面,静态类几乎总是比内部类更可取,因为内存泄漏。

    在您的特定情况下,您应该:

    1. 从您的所有阵列中删除puzzles关键字(实际上,从您的所有字段中删除)。

      AppSectionsPagerAdapter mAppSectionsPagerAdapter; ViewPager mViewPager; int级; 字符串图像[]; String levelDescriptions []; 字符串拼图[]; 字符串答案[]; 字符串提示[]; String congratulationsArray [];

    2. 您也可以将这些字段设为私有,但这并非绝对必要。

      1. 使您的适配器类也是静态的。 Android Studio会抱怨它无法找到public static class AppSectionsPagerAdapter extends FragmentPagerAdapter { private String images[]; private String puzzles[]; ... public AppSectionsPagerAdapter(FragmentManager fm, String[] puzzles, String[] images, ... ) { super(fm); this.puzzles = puzzles; this.images = images; ... } /* ... */ } ,因此您可以通过构造函数传递它,还可以传递其他以前的静态数组。

        In getItem()
      2. @Override public Fragment getItem(int puzzleIndex) { Fragment fragment = new SolvePuzzleFragment(); Bundle args = new Bundle(); args.putString(PUZZLE_CONTENT, puzzles[puzzleIndex]); args.putString(PUZZLE_IMAGE, images[puzzleIndex]); ... fragment.setArguments(args); return fragment; } ,不是传递Fragments数组索引,而是传递它们所需的特定值:

        {{1}}
      3. 在片段中,检索新参数而不是数组索引。

      4. 应该这样做。

答案 1 :(得分:1)

问题在于这些问题 static AppSectionsPagerAdapter mAppSectionsPagerAdapter; static ViewPager mViewPager;

为什么要将它静态