IllegalArgumentException: pointerIndex вне диапазона от SwipeRefreshLayout

Я получаю некоторые из этих IllegalArgumentException: pointerIndex out of range падает на crashlytics, и я не понимаю, что происходит. Это не ограничивается одной сборки 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.access0(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.access0(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.access00(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.access00(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)

поэтому мой вопрос заключается в том, что вызывает эту ошибку и каким будет приемлемый метод смягчения этот вопрос?

EDIT: вот ссылка на MotionEvent.java: 1998 который упоминается в аварии выше.

EDIT: вот мой onDestroy выглядит так:

@Override
public void onDestroy() {

    AppMsg.cancelAll();
    SuperCardToast.cancelAllSuperCardToasts();

    super.onDestroy();
}

конкретно BaseActivity.java:105 вот куда я зову super.onDestroy();.

6 ответов


Я предполагаю, что исключение выбрасывается, пока происходит событие касания (потоковое до родного касания, пока активность собирается onDestroy(). Это нормально, чтобы смягчить это, поймав исключение во избежание сбоя. Активность вот-вот будет уничтожена, если она войдет в это состояние.

Я не уверен (havent tested), но вы можете попытаться предотвратить любые события, которые будут переданы в реализацию вообще, если ловить исключение не подходит вы.

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 пытается getY () из недопустимого индекса указателя, вызывая findPointerIndex(ev, activePointer) возвращает -1 если не удается найти. Предотвращение отправки события касания с недопустимым указателем меньше 0 и индексом указателя больше или равно количеству указателей для этого события, вероятно, предотвратит проверку указателя внутри родной MotionEvent реализация от метаний ИАЭ.

 @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);
    }
}

он уже зарегистрирован в AOSP проблема tracker.

вы можете исправить это, подклассировав SwipeRefreshLayout и поймать исключение, например:

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();
}

и возврат mIsDrawerOpen на 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) было установлено неправильно. Таким образом, приложение будет вызывать android.view.MotionEvent.getY(10) когда в списке было 10 (а не 11) пунктов. Вы можете попробовать отладку, получив количество того, что находится в ViewGroup.

мне также было бы интересно узнать, что находится внутри вашего com.пакета mypackage.приложение myapp.BaseActivity.метод onDestroy.

кроме того, я заметил это, по-видимому, ошибка в Eclair: https://stackoverflow.com/a/16519902/2832027 Может быть, была ошибка в Eclair, которая была исправлена, но у некоторых людей нет обновления, если он работает нормально на вашей версии Eclair?

обновление: глядя на ваш метод onDestroy и ошибки - похоже, вызов onDestroy может вызвать одно последнее событие в ViewGroup, которое вызывает последнее представление в просматриваемой группе. Мог бы!--1--> или SuperCardToast.cancelAllSuperCardToasts() уничтожить эти взгляды? Не могли бы вы поставил супер.onDestroy () перед ними в вашем методе onDestroy?

хотя, если это проблема, странно, что вы не можете воспроизвести ее самостоятельно. Это также может быть проблема с длиной пользователей ViewGroup против ваших собственных. Если у вас очень короткие ViewGroups, вы можете реплицировать проблему?

журналы ошибок, которые вы показываете, только кажутся для Eclair, но вы говорите, что проблема возникает на всех устройствах. Можете ли вы опубликовать журнал ошибок для устройства, отличного от Eclair?


похоже, что внутри onTouchEvent SwipeRefreshLayout они просто добавили проверку pointerId на ACTION_CANCEL, но не добавили проверку pointerIndex, которая доступна для ACTION_MOVE . Поэтому я создал для него пользовательский класс и обработал его для ACTION_CANCEL & ACTION_UP.

до сих пор все работает правильно и скоро выпустит обновление моего приложения. Если все еще получите какое-либо исключение на crashlytics, я обновлю свое решение. До тех пор наслаждайтесь :)

открытый класс 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);
    }
}

}