VelocityTracker在Android 4.4上导致崩溃

16

我的应用中有一个自定义的抽屉布局实现。在Android 2.3和4.0.4上运行良好,但在Android 4.4上几乎每次打开或关闭抽屉时都会崩溃。以下是错误日志:

E/AndroidRuntime( 9839): FATAL EXCEPTION: main
E/AndroidRuntime( 9839): Process: com.andryr.launcher, PID: 9839
E/AndroidRuntime( 9839): java.lang.IllegalStateException: Already in the pool!
E/AndroidRuntime( 9839):    at android.util.Pools$SimplePool.release(Pools.java:112)
E/AndroidRuntime( 9839):    at android.util.Pools$SynchronizedPool.release(Pools.java:161)
E/AndroidRuntime( 9839):    at android.view.VelocityTracker.recycle(VelocityTracker.java:85)
E/AndroidRuntime( 9839):    at com.andryr.widget.DrawerLayout.onTouchEvent(DrawerLayout.java:131)
E/AndroidRuntime( 9839):    at android.view.View.dispatchTouchEvent(View.java:7706)
E/AndroidRuntime( 9839):    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2210)
E/AndroidRuntime( 9839):    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1945)
E/AndroidRuntime( 9839):    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2216)
E/AndroidRuntime( 9839):    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1959)
E/AndroidRuntime( 9839):    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2216)
E/AndroidRuntime( 9839):    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1959)
E/AndroidRuntime( 9839):    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2216)
E/AndroidRuntime( 9839):    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1959)
E/AndroidRuntime( 9839):    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2216)
E/AndroidRuntime( 9839):    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1959)
E/AndroidRuntime( 9839):    at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:2068)
E/AndroidRuntime( 9839):    at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1515)
E/AndroidRuntime( 9839):    at android.app.Activity.dispatchTouchEvent(Activity.java:2458)
E/AndroidRuntime( 9839):    at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:2016)
E/AndroidRuntime( 9839):    at android.view.View.dispatchPointerEvent(View.java:7886)
E/AndroidRuntime( 9839):    at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:3947)
E/AndroidRuntime( 9839):    at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:3826)
E/AndroidRuntime( 9839):    at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3392)
E/AndroidRuntime( 9839):    at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3442)
E/AndroidRuntime( 9839):    at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3411)
E/AndroidRuntime( 9839):    at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3518)
E/AndroidRuntime( 9839):    at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3419)
E/AndroidRuntime( 9839):    at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:3575)
E/AndroidRuntime( 9839):    at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3392)
E/AndroidRuntime( 9839):    at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3442)
E/AndroidRuntime( 9839):    at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3411)
E/AndroidRuntime( 9839):    at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3419)
E/AndroidRuntime( 9839):    at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3392)
E/AndroidRuntime( 9839):    at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:5532)
E/AndroidRuntime( 9839):    at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:5512)
E/AndroidRuntime( 9839):    at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:5483)
E/AndroidRuntime( 9839):    at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:5612)
E/AndroidRuntime( 9839):    at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:185)
E/AndroidRuntime( 9839):    at android.os.MessageQueue.nativePollOnce(Native Method)
E/AndroidRuntime( 9839):    at android.os.MessageQueue.next(MessageQueue.java:138)
E/AndroidRuntime( 9839):    at android.os.Looper.loop(Looper.java:123)
E/AndroidRuntime( 9839):    at android.app.ActivityThread.main(ActivityThread.java:5001)
E/AndroidRuntime( 9839):    at java.lang.reflect.Method.invoke(Native Method)
E/AndroidRuntime( 9839):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
E/AndroidRuntime( 9839):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)

这是我的代码:

@Override
public boolean onInterceptTouchEvent(MotionEvent ev)
{
    if(!mInterceptTouchEvent)
    {
        return false;
    }
    if (ev.getAction() == MotionEvent.ACTION_DOWN
            && ((ev.getX() < ViewConfiguration.get(getContext())
                    .getScaledEdgeSlop() && !mExpanded) || (ev.getX() > getWidth()
                    - ViewConfiguration.get(getContext())
                            .getScaledEdgeSlop())
                    && mExpanded))
    {
        if (mVelocityTracker == null)
            mVelocityTracker = VelocityTracker.obtain();
        else
            mVelocityTracker.clear();
        return true;
    }
    return false;
}

@Override
public boolean onTouchEvent(MotionEvent ev)
{
    if(!mInterceptTouchEvent)
    {
        return false;
    }
    mLastMotionX = ev.getX();
    if (mVelocityTracker == null)
        return false;
    switch (ev.getAction())
    {
    case MotionEvent.ACTION_MOVE:
        mVelocityTracker.addMovement(ev);
        mVelocityTracker.computeCurrentVelocity(1000);
        mVelocity = mVelocityTracker.getXVelocity();
        // ViewHelper.setTranslationX(mDrawerLayout,
        // -mDrawerLayout.getWidth()
        // + mLastMotionX);
        // ViewHelper.setTranslationX(mRootView, mLastMotionX);
        mPosition = (int) (-mDrawerLayout.getWidth() + mLastMotionX);
        requestLayout();
        break;
    case MotionEvent.ACTION_CANCEL:
    case MotionEvent.ACTION_UP:

        if (Math.abs(mVelocity) > ViewConfiguration.get(getContext())
                .getScaledMinimumFlingVelocity()
                && Math.abs(mVelocity) < ViewConfiguration
                        .get(getContext()).getScaledMaximumFlingVelocity())
        {
            if (mVelocity > 0)
            {
                openDrawer();

            } else
            {
                closeDrawer();

            }
        } else
        {
            if (mLastMotionX > getWidth() / 2)
            {
                openDrawer();

            } else
            {
                closeDrawer();

            }
        }

        mVelocityTracker.recycle();
        return false;

    }
    return true;
}

第131行是:

    mVelocityTracker.recycle();

我不明白为什么会发生这种情况。


你解决了这个问题吗?我在回收VelocityTracker对象时也遇到了同样的错误。 - Mahantesh M Ambi
1
mVelocityTracker.computeCurrentVelocity(1000); 是一个昂贵的操作。如果 velocityTracker 为空,你应该考虑将其移动到 ACTION_UP,并设置默认的 MIN_VELOCITY。 - worked
2个回答

30

我必须在回收VelocityTracker后将其设置为null,以使其正常工作。


1
在回收VelocityTracker之后不将其设置为null会导致崩溃,因为第二次使用VelocityTracker时,您将再次回收它(两次回收相同的VT)。我认为(但我不确定)另一种解决方案是调用VelocityTracker.clear()而不是VelocityTracker.recycle()。 - andryr
对我来说,将其设置为 null 没有起作用。可能更好的选择是像 @andryr 指出的那样调用 clear() 方法。 - xip
1
调用VelocityTracker.clear()不会重用velocityTracker对象。它只是将速度跟踪器重置回其初始状态,因此每次滚动都会导致新对象的创建。这会导致应用程序频繁进行垃圾回收,从而使应用程序暂停几毫秒。是否有其他解决方法? - Harikrishnan
对我有用。我的视图中有自定义的touches实现,忘记在“recycle()”后将velocityTracker对象置空。非常感谢。 - Veaceslav Gaidarji

3
根据文档(这里)的说明,如果你想再次使用它,则必须调用velocityTracker.clear()
如果您不打算再次使用该对象,请使用velocityTracker.recycle()来释放资源。在您的活动被销毁时执行此操作。
(这就是为什么@andryr的答案可行。您正在创建一个新的对象)

1
你的回答中几乎每个语句都是错误的: 在原始帖子中,velocityTracker 在第二次 down 事件时被清除。 VelocityTracker 应该尽快被回收,大多数情况下应该在 ACTION_UP 或 ACTION_CANCEL 上发生,而不是在 activity 销毁时。正如你提到的示例所做的那样。还有一句引用自文档:"通常只有在跟踪移动时才维护活动对象"(参见 VelocityTracker.obtain()) VelocityTracker.obtain() 通常不会创建新对象,而是从池中返回一个对象。 - Sergey Glotov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接