升级compileSdkVersion至33后,onScroll参数null引发崩溃问题排查
问题:GestureDetector监听ScrollView滚动时的NullPointerException问题
我用GestureDetector捕获ScrollView的滚动事件,代码如下:
val gestureDetector = GestureDetector(this.fragment.activity, ScrollGestureListener(scrollView)) scrollView.setOnTouchListener(OnTouchListener { view, event -> gestureDetector.onTouchEvent(event) return@OnTouchListener false }) internal inner class ScrollGestureListener(view: View) : GestureDetector.SimpleOnGestureListener() { override fun onScroll(e1: MotionEvent, e2: MotionEvent, distanceX: Float, distanceY: Float): Boolean { this@DocumentViewer.onDrag() return true } }
在compileSdkVersion为30时,应用会因onScroll的第一个参数始终为null而崩溃,我通过将第一个参数设为可空类型解决了该问题:
override fun onScroll(e1: MotionEvent?, e2: MotionEvent, distanceX: Float, distanceY: Float): Boolean {
现在尝试将compileSdkVersion升级至33,上述代码无法编译;但如果恢复标准函数签名,滚动ScrollView时应用会再次崩溃。
排查过程
- 看到一条评论指出:若有GestureDetector之外的组件消费ACTION_DOWN事件,就会出现此类崩溃,但我无法定位到相关组件。我曾在
ScrollGestureListener中临时实现onDown方法,但它从未被调用,这似乎与ACTION_DOWN的评论相关。 - 查看GestureDetector.java源码,发现传递给
onScroll的第一个参数是mCurrentDownEvent,这也与ACTION_DOWN的评论相关。但在Android Studio中给GestureDetector.java加断点时,mCurrentDownEvent从未显示为null,且始终与第二个参数ev相同——GestureDetector只是将mCurrentDownEvent设为ev的副本。 - 崩溃似乎发生在GestureDetector调用
onScroll时,因为我的onScroll方法中的断点从未触发,即使移除onScroll的重写,调用父类实现也会崩溃。控制台报错如下:
E/AndroidRuntime: FATAL EXCEPTION: main Process: com.arlomedia.bandhelper, PID: 18797 java.lang.NullPointerException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkNotNullParameter, parameter e1 at com.arlomedia.bandhelper.helpers.DocumentViewer$ScrollGestureListener.onScroll(Unknown Source:2) at android.view.GestureDetector.onTouchEvent(GestureDetector.java:788) at com.arlomedia.bandhelper.helpers.DocumentViewer.viewDocument$lambda-9(DocumentViewer.kt:1398) at com.arlomedia.bandhelper.helpers.DocumentViewer.$r8$lambda$kyj-4h2jNAIkIYOWSE7_HVetJAg(Unknown Source:0) at com.arlomedia.bandhelper.helpers.DocumentViewer$$ExternalSyntheticLambda7.onTouch(Unknown Source:6) at android.view.View.dispatchTouchEvent(View.java:15147)
- 尝试在OnTouchListener中调用
onTouchEvent前添加检查:
if (event != null) { gestureDetector.onTouchEvent(event) }
但event从未为null,且查看其属性也无法判断是否会引发崩溃。
- 另一个线索:只有当调用
onTouchEvent时手指仍在屏幕上,才会触发onScroll。我尝试延迟调用:
val runnable = Runnable { gestureDetector.onTouchEvent(event) } App.instance.timerHandler.postDelayed(runnable, 1000)
若滚动后1秒内抬起手指,onScroll不会被调用,也不会崩溃;若手指保持在屏幕上超过1秒,onScroll会被调用并引发崩溃。
求助
我想到两种修复思路:在OnTouchListener中验证事件后再调用onTouchEvent,或找出导致GestureDetector向onScroll发送无效事件的原因,但目前已无头绪,希望得到解决方案。
内容的提问来源于stack exchange,提问作者arlomedia
相关产品推荐
相关产品推荐

