Android中runOnUIThread调用致软键盘隐藏的问题排查
问题描述
我的Android应用核心逻辑用C++编写,存在多处JNI调用场景。其中一个场景是用户在EditText输入文本时,调用JNI方法将每个单词首字母大写,预期不影响用户交互:
- 文本变更监听器调用JNI函数,在工作线程处理文本大写,随后通过
runOnUIThread在主线程更新EditText的值。
相关代码示例:
调用JNI函数的TextWatcher实现
edittext.addTextChangedListener(new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) { OnTextChanged(edittext.getId(), String.valueOf(s)); } // 省略其他重写方法 });
主线程更新EditText的方法
// 工作线程完成文本大写后,在主线程调用以下函数 // 先通过入口函数从布局获取文本框对象,再调用此方法 public static void UpdateTextField(EditText pEditTextObj, int pID, int pPosX, int pPosY, int pHeight, int pWidth, int pColor, String pText) { Log.d("SampleJNIApp01", "TextField valued to be updated = " + pText); pEditTextObj.setText(pText); pEditTextObj.requestFocus(); pEditTextObj.setCursorVisible(true); pEditTextObj.setSelection(pEditTextObj.getText().length()); // TODO - Show the keyboard }
现象:输入第一个字符后,文本确实会被JNI函数大写并更新到EditText,但焦点丢失、光标不可见且软键盘被隐藏;输入过程中,工作线程通过runOnUIThread调用Java方法时,同样出现焦点丢失、软键盘隐藏问题。
问题分析与解决
核心问题
- TextWatcher循环回调干扰:调用
setText()会再次触发TextWatcher的onTextChanged方法,导致JNI调用和更新操作反复触发,系统焦点和键盘状态在频繁的UI更新中被打乱。 - 焦点与键盘处理不完整:单纯调用
requestFocus()不足以维持输入法状态,setText()后系统可能自动隐藏软键盘,且没有主动触发输入法显示的逻辑。 - 无更新状态标记:没有区分用户输入触发的回调和代码更新触发的回调,导致不必要的重复处理。
具体修复方案
1. 添加更新状态标记,避免循环回调
在TextWatcher中增加布尔变量标记更新状态,避免重复触发JNI调用:
edittext.addTextChangedListener(new TextWatcher() { private boolean isUpdating = false; @Override public void onTextChanged(CharSequence s, int start, int before, int count) { if (!isUpdating) { OnTextChanged(edittext.getId(), String.valueOf(s)); } } @Override public void afterTextChanged(Editable s) {} @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} });
同时在UpdateTextField方法中控制状态标记:
// 在Activity中添加成员变量与方法 private boolean isUpdating = false; public void setIsUpdating(boolean status) { this.isUpdating = status; } // 修改UpdateTextField方法 public static void UpdateTextField(EditText pEditTextObj, int pID, int pPosX, int pPosY, int pHeight, int pWidth, int pColor, String pText) { YourActivity activity = (YourActivity) pEditTextObj.getContext(); activity.setIsUpdating(true); Log.d("SampleJNIApp01", "TextField valued to be updated = " + pText); pEditTextObj.setText(pText); pEditTextObj.setCursorVisible(true); pEditTextObj.setSelection(pEditTextObj.getText().length()); // 修复焦点与键盘问题 if (pEditTextObj.requestFocus()) { InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); imm.showSoftInput(pEditTextObj, InputMethodManager.SHOW_IMPLICIT); } activity.setIsUpdating(false); }
2. 完善焦点与软键盘处理
在requestFocus()成功后,主动调用输入法管理器显示软键盘,确保系统不会自动隐藏键盘。此操作必须在UI线程执行,这里已经通过runOnUIThread调用,符合要求。
3. 优化JNI调用时机
避免用户每输入一个字符就立即触发JNI处理,可增加延迟减少频繁线程切换和UI更新:
edittext.addTextChangedListener(new TextWatcher() { private boolean isUpdating = false; private Handler handler = new Handler(); private Runnable processRunnable; @Override public void onTextChanged(CharSequence s, int start, int before, int count) { if (!isUpdating) { // 取消之前的待处理任务,避免重复处理 if (processRunnable != null) { handler.removeCallbacks(processRunnable); } final String inputText = String.valueOf(s); processRunnable = () -> OnTextChanged(edittext.getId(), inputText); // 延迟300ms处理,降低JNI调用频率 handler.postDelayed(processRunnable, 300); } } // 省略其他重写方法 });
内容的提问来源于stack exchange,提问作者Abhi_M
相关产品推荐
相关产品推荐

