React Native 0.66.4中ReactEditText.java空指针崩溃如何解决
问题说明
崩溃栈核心报错:java.lang.NullPointerException:尝试在空对象引用上调用虚方法 'android.graphics.drawable.Drawable android.graphics.drawable.Drawable$ConstantState.newDrawable(android.content.res.Resources)'
环境:React Native 0.66.4,崩溃关联ReactEditText.java,常规方案修复无效。
根因
这个是React Native 0.64~0.67版本的已知原生缺陷,触发逻辑是:
- ReactEditText内部缓存了输入框默认背景Drawable的ConstantState引用
- 应用切后台、系统内存不足回收资源、部分定制ROM主动清理Drawable缓存时,ConstantState会被置空
- 后续输入框重绘、焦点切换、软键盘弹出时,代码直接调用
newDrawable()方法没有做空判断,直接触发空指针 - 崩溃集中在安卓12及以上版本,小米、vivo等定制ROM出现概率更高
修复方案
方案1:修改RN源码打补丁(100%生效,推荐)
直接修改本地RN依赖里的ReactEditText源码,补上空判断即可,步骤如下:
- 打开文件路径:
node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java - 搜索所有调用
getConstantState().newDrawable(的代码位置,原代码逻辑大致为:
if (mDefaultPaddingBackground != null) { background = mDefaultPaddingBackground.getConstantState().newDrawable(getResources()); }
- 替换为带空安全的逻辑:
if (mDefaultPaddingBackground != null) { Drawable.ConstantState constantState = mDefaultPaddingBackground.getConstantState(); if (constantState != null) { background = constantState.newDrawable(getResources()); } else { background = mDefaultPaddingBackground.mutate(); } }
- 安装
patch-package依赖,执行npx patch-package react-native把修改持久化,后续重新安装依赖不会丢失改动。
方案2:无侵入原生拦截(适合不想改node_modules的场景)
在安卓工程的MainApplication中,给App的LayoutInflater注册全局控件拦截,对所有ReactEditText实例的背景Drawable做代理,补上空判断逻辑,核心代码放在onCreate方法中RN初始化之前即可:
// 核心拦截逻辑 registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() { @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) { LayoutInflater.from(activity).setFactory2(new LayoutInflater.Factory2() { @Override public View onCreateView(View parent, String name, Context context, AttributeSet attrs) { if (name.equals("com.facebook.react.views.textinput.ReactEditText")) { ReactEditText editText = new ReactEditText(context); Drawable bg = editText.getBackground(); editText.setBackground(new InsetDrawable(bg, 0) { @Override public Drawable.ConstantState getConstantState() { Drawable.ConstantState state = super.getConstantState(); return state == null ? new Drawable.ConstantState() { @Override public Drawable newDrawable() { return mutate(); } @Override public Drawable newDrawable(Resources res) { return mutate(); } @Override public int getChangingConfigurations() { return 0; } } : state; } }); return editText; } return null; } // 其余接口空实现即可 @Override public View onCreateView(String name, Context context, AttributeSet attrs) {return null;} @Override public void onActivityStarted(Activity activity) {} @Override public void onActivityResumed(Activity activity) {} @Override public void onActivityPaused(Activity activity) {} @Override public void onActivityStopped(Activity activity) {} @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) {} @Override public void onActivityDestroyed(Activity activity) {} }); } // 其余生命周期方法空实现 });
临时规避方案
如果暂时没法发版修复,可以先通过前端样式规避:给所有TextInput组件设置非默认背景,比如加borderWidth: 1、borderColor: 'transparent',强制RN使用自定义Drawable,不走默认的缓存逻辑,能覆盖80%左右的崩溃场景。
验证方式
- 本地测试打开开发者选项的「不保留活动」开关,反复切后台、点击输入框唤起/收起软键盘、切换输入框焦点,连续操作30次无崩溃即可确认修复生效
- 灰度发布后重点观测Crashlytics中安卓12+、小米/vivo机型的该崩溃占比,正常修复后崩溃率会降到0.01%以下
内容的提问来源于stack exchange,提问作者Gaurav Singh
相关产品推荐
相关产品推荐

