You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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方法时,同样出现焦点丢失、软键盘隐藏问题。


问题分析与解决

核心问题

  1. TextWatcher循环回调干扰:调用setText()会再次触发TextWatcher的onTextChanged方法,导致JNI调用和更新操作反复触发,系统焦点和键盘状态在频繁的UI更新中被打乱。
  2. 焦点与键盘处理不完整:单纯调用requestFocus()不足以维持输入法状态,setText()后系统可能自动隐藏软键盘,且没有主动触发输入法显示的逻辑。
  3. 无更新状态标记:没有区分用户输入触发的回调和代码更新触发的回调,导致不必要的重复处理。

具体修复方案

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 21:45:28