在即时通讯 IM 场景中,“正在输入”状态提示是一个提升交互体验的核心基础功能之一。它通过在聊天界面实时显示“对方正在输入…”,为用户创造了一种动态的、临场式的对话感知,进一步增强了沟通的实时性和沉浸感。

本文将详细解析“正在输入”状态的技术原理,并以环信IM SDK实战为例,手把手带你构建一套可靠的“正在输入”状态解决方案。

技术原理

输入状态提示功能基于环信 SDK 的 透传消息消息类型机制实现,工作流程如下:
在这里插入图片描述

  1. 监听输入状态:监听用户 A 的输入状态,检测到用户开始输入时,触发输入状态发送逻辑。

  2. 发送透传消息:每隔固定时间间隔(默认 5 秒),通过透传消息将输入状态发送给用户 B,通知其开始输入文本。

  3. 接收与处理消息:用户 B 收到透传消息后,判断当前是否在与用户 A 聊天的页面,若是则显示用户 A 的输入状态。

  4. 自动隐藏输入状态提示:若用户 B 在指定时间内未收到新的输入状态消息,则自动隐藏输入状态提示。

  1. 透传消息是一种特殊类型消息,收发双方不会存数据库。在本方案中,透传消息只发送给在线用户。

  2. 消息发送方可根据需要设置透传消息发送间隔。

实现过程

本文以Android端代码为例,其他端请访问环信文档。

前提条件

接下来实现“对方正在输入…”

发送输入状态的透传消息

当用户在输入框中输入文字时,定期发送包含输入状态的透传消息:

// 设置透传消息发送间隔。默认为 5000 毫秒,即 5 秒。
private static final int INPUT_STATUS_INTERVAL = 5000; 
private long lastSendTime = 0;

// 假设 editText 是你的输入框
EditText editText = findViewById(R.id.edit_text);
editText.addTextChangedListener(new TextWatcher() {
    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        long currentTime = System.currentTimeMillis();
        // 限制发送频率
        if (currentTime - lastSendTime > INPUT_STATUS_INTERVAL) {
            sendInputStatusMessage();
            lastSendTime = currentTime;
        }
    }

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {}

    @Override
    public void afterTextChanged(Editable s) {}
});

private void sendInputStatusMessage() {
    String action = "input_status";
    // 创建透传消息
    EMMessage cmdMsg = EMMessage.createSendMessage(EMMessage.Type.CMD);
    EMCmdMessageBody cmdBody = new EMCmdMessageBody(action);
    // 将该透传消息只发送给在线用户
    cmdBody.deliverOnlineOnly(true);
    
    cmdMsg.addBody(cmdBody);
    // 设置接收方(toChatUsername 为当前聊天对象的环信用户 ID)
    cmdMsg.setTo(toChatUsername); 
    cmdMsg.setChatType(EMMessage.ChatType.Chat);
    // 发送消息
    EMClient.getInstance().chatManager().sendMessage(cmdMsg);
}

接收和解析透传消息

收到透传消息后,用户判断是否为输入状态消息,并在 UI 上进行相应提示。收到输入状态后,若在一定时间间隔内未再次收到,则自动隐藏提示。

// 用于处理自动隐藏输入状态的 Handler
private Handler inputStatusHandler = new Handler(Looper.getMainLooper());
private Runnable hideInputStatusRunnable = new Runnable() {
    @Override
    public void run() {
        // 隐藏"对方正在输入..."提示
        // 例如:inputStatusTextView.setVisibility(View.GONE);
    }
};

EMMessageListener msgListener = new EMMessageListener() {
    @Override
    public void onCmdMessageReceived(List<EMMessage> messages) {
        for (EMMessage message : messages) {
            EMCmdMessageBody cmdBody = (EMCmdMessageBody) message.getBody();
            String action = cmdBody.action();
            if ("input_status".equals(action)) {
                // 使用 Handler 在主线程更新 UI
                inputStatusHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        // 显示"对方正在输入..."提示
                        // 例如:inputStatusTextView.setVisibility(View.VISIBLE);
                        // 例如:inputStatusTextView.setText("对方正在输入...");
                    }
                });
                
                // 移除之前的自动隐藏任务,重新计时
                inputStatusHandler.removeCallbacks(hideInputStatusRunnable);
                // 5 秒后自动隐藏输入状态提示
                inputStatusHandler.postDelayed(hideInputStatusRunnable, 5000);
            }
        }
    }

    // 其他回调方法...
    @Override
    public void onMessageReceived(List<EMMessage> messages) {}
    @Override
    public void onMessageRead(List<EMMessage> messages) {}
    @Override
    public void onMessageDelivered(List<EMMessage> messages) {}
    @Override
    public void onMessageRecalledWithExt(List<EMRecallMessageInfo> recallMessageInfo) {}
    @Override
    public void onMessageChanged(EMMessage message, Object change) {}
};

EMClient.getInstance().chatManager().addMessageListener(msgListener);


注意事项

为避免内存泄漏,务必在 Activity 销毁时移除已注册的监听器,并且清理 Handler 中的回调任务。

// 在 Activity 中
@Override
protected void onDestroy() {
    // 移除消息监听器
    if (msgListener != null) {
        EMClient.getInstance().chatManager().removeMessageListener(msgListener);
    }
    // 清理 Handler 回调(针对输入状态提示功能)
    if (inputStatusHandler != null) {
        inputStatusHandler.removeCallbacksAndMessages(null);
    }
    super.onDestroy();
}

本次基于环信SDK的实战开发,我们实现了IM“正在输入”状态提示功能。如遇到其他问题,请至 环信官网 联系技术支持咨询。

参考文档: