仅三星S6 Edge安卓7版本触摸TextView触发空指针崩溃
问题复现与现象
- 复现设备范围:仅搭载Android 7系统的三星Galaxy S6 Edge,其余设备无同类异常
- 触发路径:用户触摸文本类控件(TextView、EditText、Button等)时直接触发致命崩溃
- 崩溃异常:
java.lang.NullPointerException - 核心报错信息:尝试在空对象引用上调用虚方法
boolean android.widget.Editor$SelectionModifierCursorController.isDragAcceleratorActive()
完整崩溃堆栈
Fatal Exception: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.widget.Editor$SelectionModifierCursorController.isDragAcceleratorActive()' on a null object reference at android.widget.Editor.updateFloatingToolbarVisibility(Editor.java:1541) at android.widget.Editor.onTouchEvent(Editor.java:1491) at android.widget.TextView.onTouchEvent(TextView.java:10012) at android.view.View.dispatchTouchEvent(View.java:10717) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2865) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2550) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2865) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2550) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2865) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2550) at android.widget.ScrollView.dispatchTouchEvent(ScrollView.java:738) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2865) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2550) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2865) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2550) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2865) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2550) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2865) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2550) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2865) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2550) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2865) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2550) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2865) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2550) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2865) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2550) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2865) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2550) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2865) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2550) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2865) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2550) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2865) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2550) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2865) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2550) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2865) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2550) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2865) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2550) at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:559) at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1870) at android.app.Activity.dispatchTouchEvent(Activity.java:3236) at androidx.appcompat.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java) at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:521) at android.view.View.dispatchPointerEvent(View.java:10946) at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:5110) at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4962) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4493) at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4546) at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4512) at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4645) at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4520) at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4702) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4493) at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4546) at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4512) at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4520) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4493) at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:7000) at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:6929) at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:6890) at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:7110) at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:185) at android.view.InputEventReceiver.nativeConsumeBatchedInputEvents(InputEventReceiver.java) at android.view.InputEventReceiver.consumeBatchedInputEvents(InputEventReceiver.java:176) at android.view.ViewRootImpl.doConsumeBatchedInput(ViewRootImpl.java:7074) at android.view.ViewRootImpl$ConsumeBatchedInputRunnable.run(ViewRootImpl.java:7137) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:927) at android.view.Choreographer.doCallbacks(Choreographer.java:702) at android.view.Choreographer.doFrame(Choreographer.java:632) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:913) at android.os.Handler.handleCallback(Handler.java:751) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:154) at android.app.ActivityThread.main(ActivityThread.java:6682) at java.lang.reflect.Method.invoke(Method.java) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1520) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1410)
问题根因
该问题属于三星定制Android 7系统的ROM原生缺陷:三星修改了系统android.widget.Editor类的updateFloatingToolbarVisibility方法逻辑,未对SelectionModifierCursorController实例做空值判断就直接调用其isDragAcceleratorActive()方法。当文本控件未完成编辑器初始化、或未启用文本选择功能时被触摸,就会触发该空指针崩溃,不属于应用自身代码逻辑错误。
修复方案
- 通用兼容方案:自定义所有文本类控件(包括TextView、EditText、Button、CheckedTextView等),重写
onTouchEvent方法捕获该特定空指针异常,避免崩溃,示例代码如下:
public class FixBugTextView extends AppCompatTextView { // 实现类的三个构造方法 @Override public boolean onTouchEvent(MotionEvent event) { try { return super.onTouchEvent(event); } catch (NullPointerException e) { // 拦截三星Android7系统Editor模块空指针崩溃 return true; } } }
- 轻量方案:针对三星Android 7设备,对不需要文本选择功能的控件主动调用
setTextIsSelectable(false),跳过选择工具栏更新逻辑,从触发路径上规避问题。 - 依赖升级方案:如果项目使用AndroidX的AppCompat控件库,将
androidx.appcompat:appcompat升级到1.2.0及以上版本,官方已对该类厂商ROM空指针异常做了内置兼容拦截。
内容的提问来源于stack exchange,提问作者Anwar Zahid
相关产品推荐
相关产品推荐

