VelocityTracker 导致 Android 4.4 崩溃
Posted
技术标签:
【中文标题】VelocityTracker 导致 Android 4.4 崩溃【英文标题】:VelocityTracker causes crash on Android 4.4 【发布时间】:2014-11-22 08:53:54 【问题描述】:在我的应用程序中,我有一个抽屉布局的自定义实现。它在 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对象时遇到同样的错误。 mVelocityTracker.computeCurrentVelocity(1000);是一项昂贵的手术。如果velocityTracker 为空,您应该考虑将其移至ACTION_UP 并设置默认的MIN_VELOCITY。 【参考方案1】:我必须在回收它后将我的velocitytracker设置为null以使其正常工作
【讨论】:
回收后未将 VelocityTracker 设置为 null 会导致崩溃,因为在您第二次使用 VelocityTracker 时,您将再次回收它(回收相同的 VT 两次)。我认为(但我不确定)另一种解决方案是调用 VelocityTracker.clear() 而不是 VelocityTracker.recycle() 对我来说,将其设置为 null 不起作用。就像@andryr 指出的那样,调用 clear() 可能是一个更好的选择。 调用 VelocityTracker.clear() 不会重用velocityTracker 对象。它只是将速度跟踪器重置为其初始状态,从而为每个滚动创建新对象。这会导致 GC 在我的应用程序中经常收集,这会使应用程序暂停几毫秒。有没有其他解决方法? 为我工作。我在我的视图中有自定义的触摸实现,并忘记在recycle()
之后取消velocityTracker 对象。非常感谢【参考方案2】:
如文档here 中所述,如果您想再次使用它,您必须致电velocityTracker.clear()
。
如果您不再使用该对象,请使用velocityTracker.recycle()
以释放资源。在您的活动被销毁时执行此操作。
(这就是@andryr 接受的答案有效的原因。您正在创建一个新答案)
【讨论】:
您回答中的几乎所有陈述都是错误的:velocityTracker 在原始帖子的第二次下降事件中被清除。 VelocityTracker 应该在不再需要时立即回收,在大多数情况下,它应该发生在 ACTION_UP 或 ACTION_CANCEL 上,而不是在活动销毁时。正是在您提到的示例中完成了此操作。文档中的另外一句话:“您通常应该只在跟踪运动时维护一个活动对象”(参见 VelocityTracker.obtain())VelocityTracker.obtain()
通常不会创建新对象,它会从池中返回一个。跨度>
以上是关于VelocityTracker 导致 Android 4.4 崩溃的主要内容,如果未能解决你的问题,请参考以下文章
Android View深入解析基础知识VelocityTracker,GestureDetector,Scroller