IllegalArgumentException:pointerIndex超出了SwipeRefreshLayout的范围

我已经得到了一些IllegalArgumentException: pointerIndex out of range崩溃,我不明白发生了什么事情。 它不仅限于一个Android版本或设备,它发生在各种设备上的5.0.1,4.4.4,4.4.2,4.0.4,2.3.6。 以下是更多上下文的完整日志输出。

 java.lang.RuntimeException: Unable to destroy activity {com.mypackage.myapp/com.mypackage.myapp.MyListActivity}: java.lang.IllegalArgumentException: pointerIndex out of range at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:3671) at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:3689) at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:3889) at android.app.ActivityThread.access$900(ActivityThread.java:144) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1284) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:135) at android.app.ActivityThread.main(ActivityThread.java:5221) at java.lang.reflect.Method.invoke(Method.java) at java.lang.reflect.Method.invoke(Method.java:372) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:898) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:693) Caused by: java.lang.IllegalArgumentException: pointerIndex out of range at android.view.MotionEvent.nativeGetAxisValue(MotionEvent.java) at android.view.MotionEvent.getY(MotionEvent.java:1998) at android.support.v4.view.MotionEventCompatEclair.getY(MotionEventCompatEclair.java:35) at android.support.v4.view.MotionEventCompat$EclairMotionEventVersionImpl.getY(MotionEventCompat.java:95) at android.support.v4.view.MotionEventCompat.getY(MotionEventCompat.java:228) at android.support.v4.widget.SwipeRefreshLayout.onTouchEvent(SwipeRefreshLayout.java:772) at android.view.View.dispatchTouchEvent(View.java:8388) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2398) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2158) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2400) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2172) at android.view.ViewGroup.cancelTouchTarget(ViewGroup.java:2340) at android.view.ViewGroup.removeViewInternal(ViewGroup.java:4156) at android.view.ViewGroup.removeViewInternal(ViewGroup.java:4136) at android.view.ViewGroup.removeView(ViewGroup.java:4068) at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1045) at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1126) at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1108) at android.support.v4.app.FragmentManagerImpl.dispatchDestroy(FragmentManager.java:1954) at android.support.v4.app.FragmentActivity.onDestroy(FragmentActivity.java:313) at android.support.v7.app.ActionBarActivity.onDestroy(ActionBarActivity.java:169) at com.mypackage.myapp.BaseActivity.onDestroy(BaseActivity.java:105) at android.app.Activity.performDestroy(Activity.java:6112) at android.app.Instrumentation.callActivityOnDestroy(Instrumentation.java:1140) at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:3658) at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:3689) at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:3889) at android.app.ActivityThread.access$900(ActivityThread.java:144) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1284) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:135) at android.app.ActivityThread.main(ActivityThread.java:5221) at java.lang.reflect.Method.invoke(Method.java) at java.lang.reflect.Method.invoke(Method.java:372) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:898) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:693) 

这里是另一个来自android.view.MotionEvent.getY()相关崩溃报告。

 java.lang.RuntimeException: Unable to destroy activity {com.mypackage.myapp/com.mypackage.myapp.MyListActivity}: java.lang.ArrayIndexOutOfBoundsException at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:2683) at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:2701) at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:2817) at android.app.ActivityThread.access$1600(ActivityThread.java:117) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:946) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:130) at android.app.ActivityThread.main(ActivityThread.java:3733) at java.lang.reflect.Method.invokeNative(Method.java) at java.lang.reflect.Method.invoke(Method.java:507) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:931) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:689) at dalvik.system.NativeStart.main(NativeStart.java) Caused by: java.lang.ArrayIndexOutOfBoundsException at android.view.MotionEvent.getY(MotionEvent.java:903) at android.support.v4.view.MotionEventCompatEclair.d(MotionEventCompatEclair.java:35) at android.support.v4.view.MotionEventCompat$EclairMotionEventVersionImpl.d(MotionEventCompat.java:95) at android.support.v4.view.MotionEventCompat.d(MotionEventCompat.java:228) at android.support.v4.widget.SwipeRefreshLayout.onTouchEvent(SwipeRefreshLayout.java:772) at android.view.View.dispatchTouchEvent(View.java:3971) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:903) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:1154) at android.view.ViewGroup.removeViewInternal(ViewGroup.java:2201) at android.view.ViewGroup.removeViewInternal(ViewGroup.java:2187) at android.view.ViewGroup.removeView(ViewGroup.java:2135) at android.support.v4.app.FragmentManagerImpl.a(FragmentManager.java:1045) at android.support.v4.app.FragmentManagerImpl.a(FragmentManager.java:1126) at android.support.v4.app.FragmentManagerImpl.a(FragmentManager.java:1108) at android.support.v4.app.FragmentManagerImpl.t(FragmentManager.java:1954) at android.support.v4.app.FragmentActivity.onDestroy(FragmentActivity.java:313) at android.support.v7.app.ActionBarActivity.onDestroy(ActionBarActivity.java:169) at com.mypackage.myapp.BaseActivity.onDestroy(BaseActivity.java:105) at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:2670) at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:2701) at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:2817) at android.app.ActivityThread.access$1600(ActivityThread.java:117) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:946) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:130) at android.app.ActivityThread.main(ActivityThread.java:3733) at java.lang.reflect.Method.invokeNative(Method.java) at java.lang.reflect.Method.invoke(Method.java:507) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:931) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:689) at dalvik.system.NativeStart.main(NativeStart.java) 

所以我的问题是什么导致这个错误,什么是减轻这个问题的可接受的方法?

编辑:这是上面的崩溃中引用的MotionEvent.java:1998的链接。

编辑:这是我的onDestroy看起来像:

 @Override public void onDestroy() { AppMsg.cancelAll(); SuperCardToast.cancelAllSuperCardToasts(); super.onDestroy(); } 

具体来说, BaseActivity.java:105就是我所说的super.onDestroy();

Solutions Collecting From Web of "IllegalArgumentException:pointerIndex超出了SwipeRefreshLayout的范围"

我认为当触摸事件还在发生的时候,会抛出exception(在Activity正要onDestroy()时候stream向本地触摸onDestroy() 。为了避免崩溃,可以通过捕获exception来缓解这种情况。如果进入这个状态就被销毁。

我不确定(没有testing过),但是如果捕捉exception不适合你,你可以尝试阻止任何事件被传递给实现。

 public class ComeUpWithBetterNameSwipeRefreshLayout extends SwipeRefreshLayout { private boolean mAcceptEvents; public ComeUpWithBetterNameSwipeRefreshLayout(Context context) { super(context); } public void setAcceptEvents(boolean mAcceptEvents) { this.mAcceptEvents = mAcceptEvents; } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return mAcceptEvents? super.onInterceptTouchEvent(ev) : true; } public ComeUpWithBetterNameSwipeRefreshLayout(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); mAcceptEvents = true; } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); mAcceptEvents = false; } } 

要么

 @Override public void onDestroy() { mSwipeRefreshLayout.setAcceptEvents(false); AppMsg.cancelAll(); SuperCardToast.cancelAllSuperCardToasts(); super.onDestroy(); } 

以2

SwipeRefreshLayout尝试从无效指针索引中findPointerIndex(ev, activePointer) ,调用findPointerIndex(ev, activePointer)如果找不到findPointerIndex(ev, activePointer)返回-1 。 防止使用小于0的无效指针调度触摸事件,并且指针索引大于或等于该事件的指针计数可能会阻止validation本机MotionEvent实现内的指针,从而抛出IAE。

  @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if(ev.getAction() == MotionEvent.ACTION_CANCEL) { int pointerCount = MotionEventCompat.getPointerCount(ev); int index = MotionEventCompat.getActionIndex(ev); mActivePointerId = MotionEventCompat.getPointerId(ev, index); index = MotionEventCompat.findPointerIndex(ev,mActivePointerId); if (index > -1 && index < pointerCount) { super.onInterceptTouchEvent(ev); } else { return true; } }else if(ev.getAction() == MotionEventCompat.ACTION_POINTER_DOWN && super.onInterceptTouchEvent(ev)) { final int index = MotionEventCompat.getActionIndex(ev); mActivePointerId = MotionEventCompat.getPointerId(ev, index); return false; }else if(ev.getAction() == MotionEventCompat.ACTION_POINTER_UP && super.onInterceptTouchEvent(ev)){ onSecondaryPointerUp(ev); return false; }else if(ev.getAction() == MotionEvent.ACTION_DOWN && super.onInterceptTouchEvent(ev)){ mActivePointerId = MotionEventCompat.getPointerId(ev, 0); return false; } return super.onInterceptTouchEvent(ev); } private void onSecondaryPointerUp(MotionEvent ev) { final int pointerIndex = MotionEventCompat.getActionIndex(ev); final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex); if (pointerId == mActivePointerId) { final int newPointerIndex = pointerIndex == 0 ? 1 : 0; mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex); } } 

它已经logging在AOSP问题跟踪器 。

您可以通过将SwipeRefreshLayout并捕获exception来修复它,例如:

 public class SwipeRefreshLayout extends android.support.v4.widget.SwipeRefreshLayout { public SwipeRefreshLayout(Context context) { super(context); } public SwipeRefreshLayout(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean onTouchEvent(MotionEvent ev) { try { return super.onTouchEvent(ev); } catch (IllegalArgumentException e) { //Fix for support lib bug, happening when onDestroy() is return true; } } } 

对于任何人来说,当触摸事件与导航抽屉发生冲突时,我都会看到这一点。 我在我的活动的onTouch中添加了一个支票

 if (mNavigationDrawerFragment != null && mNavigationDrawerFragment.isDrawerOpen()) { return super.onTouchEvent(event); } 

而且似乎更好。 在NavigationDrawerFragment中:

 @Override public void onDrawerSlide(View drawerView, float slideOffset) { super.onDrawerSlide(drawerView, slideOffset); mIsDrawerOpen = slideOffset != 0; getActivity().invalidateOptionsMenu(); } 

并返回isDrawerOpen()中的isDrawerOpen()

如果您在应用中看不到任何明显的问题,则可以尝试使用SwipeRefreshLayout的自定义“静默”版本:

 public class CustomSwipeRefreshLayout extends SwipeRefreshLayout{ public CustomSwipeRefreshLayout(Context context) { super(context); } public CustomSwipeRefreshLayout(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean onTouchEvent(MotionEvent event) { try{ return super.onTouchEvent(event); } catch(Exception e){ return true; } } } 

我看到一个类似的错误,当一个ViewPager(扩展ViewGroup)项目的计数设置不正确。 因此,当列表中有10个(而不是11个)项目时,该应用程序将调用android.view.MotionEvent.getY(10) 。 您可以通过获取ViewGroup中的内容来尝试debugging。

我也有兴趣知道什么是你的com.mypackage.myapp.BaseActivity.onDestroy方法。

另外,我发现这个,Eclair显然有一个bug: https: //stackoverflow.com/a/16519902/2832027是不是有一个错误在Eclair已经修复,但有些人还没有得到更新,如果它在你的Eclair版本上工作正常?

更新:看看你的onDestroy方法和错误 – 它看起来像调用onDestroy可能会触发ViewGroup中的最后一个事件调用正在查看的组中的最后一个视图。 可以AppMsg.cancelAll()SuperCardToast.cancelAllSuperCardToasts()销毁这些视图? 你可以把super.onDestroy()放在你的onDestroy方法中吗?

但是,如果是这个问题,你自己就不能复制。 这也可能是ViewGroup与你自己用户的长度有关的问题。 如果你有非常短的ViewGroups你能复制这个问题吗?

您显示的错误日志似乎只是为了Eclair,但你说这个问题发生在所有设备上。 您可以发布非Eclair设备的错误日志吗?

看起来像在SwipeRefreshLayout的onTouchEvent里面,他们只是在ACTION_CANCEL上添加了pointerIdvalidation,但没有添加可用于ACTION_MOVE的pointerIndexvalidation。 所以我为它创build了一个自定义的类,并为它处理ACTION_CANCEL&ACTION_UP。

到目前为止,一切工作正常,很快就会发布我的应用程序的更新。 如果crashlytics仍然得到任何exception,那么我会更新我的解决scheme。 直到那个时候享受:)

公共类SwipeRefreshLayoutX扩展SwipeRefreshLayout {

 private int mActivePointerId; public SwipeRefreshLayoutX(Context context) { super(context); // TODO Auto-generated constructor stub } public SwipeRefreshLayoutX(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub } @Override public boolean onTouchEvent(MotionEvent event) { final int action = MotionEventCompat.getActionMasked(event); switch (action) { case MotionEvent.ACTION_DOWN: mActivePointerId = MotionEventCompat.getPointerId(event, 0); break; case MotionEvent.ACTION_POINTER_DOWN:{ final int index = MotionEventCompat.getActionIndex(event); mActivePointerId = MotionEventCompat.getPointerId(event, index); break; } case MotionEvent.ACTION_POINTER_UP:{ onSecondaryPointerUp(event); break; } case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL:{ final int pointerIndex = MotionEventCompat.findPointerIndex(event, mActivePointerId); if (pointerIndex < 0) { Log.e("ash", "Got ACTION_UP event but have an invalid active pointer id."); return false; } break; } } return super.onTouchEvent(event); } private void onSecondaryPointerUp(MotionEvent ev) { final int pointerIndex = MotionEventCompat.getActionIndex(ev); final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex); if (pointerId == mActivePointerId) { // This was our active pointer going up. Choose a new // active pointer and adjust accordingly. final int newPointerIndex = pointerIndex == 0 ? 1 : 0; mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex); } } 

}