Android

仅集成聊天

更新时间: 2025/10/20 17:44:47

网易云信 IM UIKit 是基于 网易云信 IM SDK(简称 NIM SDK) 开发的即时通讯 UI 组件库,本文介绍了如何按照以下流程集成仅包含聊天功能的核心组件,从而在您的应用中实现单聊和群聊功能。如需集成更丰富的组件,请参考 集成 IM UIKit

graph LR
classDef default fill:#337EFF,stroke:#337EFF,stroke-width:0px,color:#FFFFFF;
导入聊天组件 --> 添加权限 --> 配置代码混淆 --> 初始化SDK --> 实现IM登录 --> 使用聊天界面

适用场景

仅集成聊天功能而不包括完整社交功能的场景通常有以下几种:

  • 客户服务与在线支持:在这类场景中,聊天功能作为客户与企业沟通的桥梁,例如在线客服、智能客服数字人、售后支持。
  • 专业服务咨询:垂直领域的辅助沟通工具,例如医疗咨询、教育平台、金融顾问、法律咨询。
  • 电商与交易平台:买卖双方沟通,例如订单状态通知、砍价/议价功能。
  • 专业协作工具:项目管理软件,例如设计协作平台、代码协作工具。
  • 特定社交场景:约会应用的初期互动,例如陌生人社交、兴趣小组。
  • 嵌入式通讯功能:游戏内聊天,例如直播平台、共享经济应用。

准备工作

在开始集成前,请确保您已完成以下操作:

  1. 网易云信控制台 创建应用,开通即时通讯 IM 服务,获取 App Key。
  2. 注册 IM 账号,获取账号 ID (account_id) 和凭证 (token)。
  3. 准备开发环境:
    • Android Studio 4.0+
    • Java 11+
    • Gradle 7.0+
    • Android Gradle Plugin 7.0+

第一步:导入聊天组件

仅聊天功能需要以下核心组件,示例中的 {LATEST_VERSION},建议使用 10.x.x 系列的最新版本,版本号请参考 更新日志

gradledependencies {
    // IM UIKit 核心聊天组件
    implementation("com.netease.yunxin.kit.chat:chatkit-ui:${LATEST_VERSION}")
    // 可选:位置消息组件(如不需要可移除)
    implementation("com.netease.yunxin.kit.locationkit:locationkit:${LATEST_VERSION}")

    // 第三方依赖库
    implementation("com.github.bumptech.glide:glide:4.13.1")
    implementation("com.squareup.retrofit2:retrofit:2.9.0")
    implementation("com.squareup.retrofit2:converter-gson:2.9.0")
    implementation("com.squareup.retrofit2:converter-scalars:2.9.0")
    implementation("com.squareup.okhttp3:okhttp:4.12.0")
}

第二步:添加必要权限

AndroidManifest.xml 文件中添加聊天功能所需的权限:

XML<!-- 声明云信后台服务 -->
<service
    android:name="com.netease.nimlib.service.NimService"
    android:process=":core"
    tools:remove="true"/>

<service
    android:name="com.netease.nimlib.push.net.HeartbeatService"
    android:process=":core"
    tools:remove="true"/>

<!-- 声明云信后台辅助服务 -->
<service
    android:name="com.netease.nimlib.job.NIMJobService"
    android:exported="false"
    android:permission="android.permission.BIND_JOB_SERVICE"
    android:process=":core"
    tools:remove="true"/>

<!-- 云信SDK的监视系统启动和网络变化的广播接收器,用户开机自启动以及网络变化时候重新登录 -->
<receiver
    android:name="com.netease.nimlib.service.NimReceiver"
    android:exported="false"
    android:process=":core"
    tools:remove="true">
    <intent-filter>
        <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
    </intent-filter>
</receiver>

<!-- 云信进程间通信receiver -->
<receiver android:name="com.netease.nimlib.service.ResponseReceiver"
    tools:remove="true"/>

<!-- 云信进程间通信service -->
<service android:name="com.netease.nimlib.service.ResponseService"
    tools:remove="true"/>

<!-- 云信内部使用的进程间通信provider -->
<provider
    android:name="com.netease.nimlib.ipc.cp.provider.PreferenceContentProvider"
    android:authorities="${applicationId}.ipc.provider.preference"
    android:exported="false"
    tools:remove="true"/>
<provider
    android:name="com.netease.nimlib.ipc.NIMContentProvider"
    android:authorities="${applicationId}.ipc.provider"
    android:exported="false"
    android:process=":core"
    tools:remove="true"/>

<!-- 声明云信后台服务 -->
<service android:name="com.netease.nimlib.service.NimServiceV2" />

<provider
    android:name="com.netease.nimlib.ipc.NIMContentProviderV2"
    android:authorities="${applicationId}.ipc.provider.v2"
    android:exported="false" />

第三步:配置代码混淆

将以下规则添加到 proguard-rules.pro 文件中,确保混淆不会影响 IM UIKit 功能:

ProGuard# IM SDK 防混淆
-dontwarn com.netease.nim.**
-keep class com.netease.nim.** {*;}
-dontwarn com.netease.nimlib.**
-keep class com.netease.nimlib.** {*;}

# IM UIKit 防混淆
-dontwarn com.netease.yunxin.kit.**
-keep class com.netease.yunxin.kit.** {*;}
-keep public class * extends com.netease.yunxin.kit.corekit.XKitInitOptions
-keep class * implements com.netease.yunxin.kit.corekit.XKitService {*;}

# Glide 防混淆
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public class * extends com.bumptech.glide.module.AppGlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
  **[] $VALUES;
  public *;
}

# OkHttp 防混淆
-dontwarn okhttp3.**
-keep class okhttp3.** {*;}

第四步:初始化 SDK

在应用的 Application 类的 onCreate() 方法中初始化 IM UIKit:

Javapublic class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        // 创建 SDK 配置
        SDKOptions options = new SDKOptions();
        // 设置 App Key
        options.appKey = "YOUR_APP_KEY";

        // 初始化 IM UIKit
        IMKitClient.init(this, options, null);

        // 如需使用位置消息功能,还需初始化位置组件
        // LocationConfig locationConfig = new LocationConfig();
        // locationConfig.aMapWebServerKey = "YOUR_AMAP_WEB_KEY";
        // LocationKitClient.init(this, locationConfig);
    }
}

第五步:登录 IM 账号

在需要登录的界面(例如应用的登录页面),添加以下登录代码:

Java/**
 * 登录网易云信 IM
 * @param accountId 账号 ID
 * @param token 账号 Token
 */
private void loginIM(String accountId, String token) {
    // 创建登录选项
    V2NIMLoginOption option = new V2NIMLoginOption();

    // 执行登录操作
    IMKitClient.login(accountId, token, option, new FetchCallback<Void>() {
        @Override
        public void onSuccess(@Nullable Void data) {
            Log.i("IM_LOGIN", "登录成功");
            // 登录成功后的操作,例如跳转到主界面
            startMainActivity();
        }

        @Override
        public void onError(int errorCode, @NonNull String errorMsg) {
            Log.e("IM_LOGIN", "登录失败: " + errorCode + ", " + errorMsg);
            // 登录失败处理
            Toast.makeText(LoginActivity.this, "IM 登录失败: " + errorMsg, Toast.LENGTH_SHORT).show();
        }
    });
}

账号 ID 和 Token 应从您的服务器获取,或通过网易云信服务端 API 生成,不要在客户端硬编码。

第六步:使用聊天界面

网易云信 IM UIKit 提供两套界面风格的 UI 组件库,您可任意选择一种使用。

基础版单聊
im-android-flutter.png
基础版群聊
party-android.png
通用版单聊
im-android-flutter.png
通用版群聊
party-android.png

方式一:使用聊天 Activity(简单)

基础版 UI 界面
Java// 打开单聊
  //路由的方式
   XKitRouter.withKey(RouterConstant.PATH_CHAT_P2P_PAGE)
              .withParam(RouterConstant.CHAT_ID_KRY,"account_id")
              .withContext(context)
              .navigate();
  // 显示跳转
       Intent intent = new Intent(getContext(), ChatP2PActivity.class);
          intent.putExtra(RouterConstant.CHAT_ID_KRY, "account_id");
          startActivity(intent);

// 打开群聊 
//路由的方式
        XKitRouter.withKey(RouterConstant.PATH_CHAT_TEAM_PAGE)
              .withParam(RouterConstant.CHAT_ID_KRY, "team_id")
              .withContext(context)
              .navigate();
// 显示跳转
          Intent intent = new Intent(getContext(), ChatTeamActivity.class);
          intent.putExtra(RouterConstant.CHAT_ID_KRY, "team_id");
          startActivity(intent);
通用版 UI 界面
Java// 打开单聊
  //路由的方式
   XKitRouter.withKey(RouterConstant.PATH_FUN_CHAT_P2P_PAGE)
              .withParam(RouterConstant.CHAT_ID_KRY,"account_id")
              .withContext(context)
              .navigate();
  // 显示跳转
       Intent intent = new Intent(getContext(), ChatP2PActivity.class);
          intent.putExtra(RouterConstant.CHAT_ID_KRY, "account_id");
          startActivity(intent);

// 打开群聊 
//路由的方式
        XKitRouter.withKey(RouterConstant.PATH_FUN_CHAT_TEAM_PAGE)
              .withParam(RouterConstant.CHAT_ID_KRY, "team_id")
              .withContext(context)
              .navigate();
// 显示跳转
          Intent intent = new Intent(getContext(), ChatTeamActivity.class);
          intent.putExtra(RouterConstant.CHAT_ID_KRY, "team_id");
          startActivity(intent);

方式二:集成到自定义 Activity(推荐)

  1. 创建聊天界面布局:

    XML<!-- activity_custom_chat.xml -->
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <FrameLayout
            android:id="@+id/chat_container"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    
    </LinearLayout>
    
  2. 在自定义 Activity 中集成聊天 Fragment:

    基础版 UI 界面
    Javapublic class CustomChatActivity extends AppCompatActivity {
    
        private String chatId;
        private boolean isTeamChat;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_custom_chat);
    
            // 获取聊天 ID 和类型
            chatId = getIntent().getStringExtra(RouterConstant.CHAT_ID_KRY);
            isTeamChat = getIntent().getBooleanExtra("is_team", false);
    
            // 添加聊天 Fragment
            if (isTeamChat) {
                // 群聊
                ChatTeamFragment teamFragment = new ChatTeamFragment();
    
                // 设置参数
                Bundle args = new Bundle();
                args.putString(RouterConstant.CHAT_ID_KRY, chatId);
                teamFragment.setArguments(args);
    
                // 添加 Fragment
                getSupportFragmentManager()
                    .beginTransaction()
                    .add(R.id.chat_container, teamFragment)
                    .commit();
            } else {
                // 单聊
                ChatP2PFragment p2pFragment = new ChatP2PFragment();
    
                // 设置参数
                Bundle args = new Bundle();
                args.putString(RouterConstant.CHAT_ID_KRY, chatId);
                p2pFragment.setArguments(args);
    
                // 添加 Fragment
                getSupportFragmentManager()
                    .beginTransaction()
                    .add(R.id.chat_container, p2pFragment)
                    .commit();
            }
        }
    
        // Activity 启动方法
        public static void start(Context context, String chatId, boolean isTeam) {
            Intent intent = new Intent(context, CustomChatActivity.class);
            intent.putExtra(RouterConstant.CHAT_ID_KRY, chatId);
            intent.putExtra("is_team", isTeam);
            context.startActivity(intent);
        }
    }
    
    通用版 UI 界面
    Javapublic class CustomChatActivity extends AppCompatActivity {
    
        private String chatId;
        private boolean isTeamChat;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_custom_chat);
    
            // 获取聊天 ID 和类型
            chatId = getIntent().getStringExtra(RouterConstant.CHAT_ID_KRY);
            isTeamChat = getIntent().getBooleanExtra("is_team", false);
    
            // 添加聊天 Fragment
            if (isTeamChat) {
                // 群聊
                ChatTeamFragment teamFragment = new FunChatTeamFragment();
    
                // 设置参数
                Bundle args = new Bundle();
                args.putString(RouterConstant.CHAT_ID_KRY, chatId);
                teamFragment.setArguments(args);
    
                // 添加 Fragment
                getSupportFragmentManager()
                    .beginTransaction()
                    .add(R.id.chat_container, teamFragment)
                    .commit();
            } else {
                // 单聊
                ChatP2PFragment p2pFragment = new FunChatP2PFragment();
    
                // 设置参数
                Bundle args = new Bundle();
                args.putString(RouterConstant.CHAT_ID_KRY, chatId);
                p2pFragment.setArguments(args);
    
                // 添加 Fragment
                getSupportFragmentManager()
                    .beginTransaction()
                    .add(R.id.chat_container, p2pFragment)
                    .commit();
            }
        }
    
        // Activity 启动方法
        public static void start(Context context, String chatId, boolean isTeam) {
            Intent intent = new Intent(context, CustomChatActivity.class);
            intent.putExtra(RouterConstant.CHAT_ID_KRY, chatId);
            intent.putExtra("is_team", isTeam);
            context.startActivity(intent);
        }
    }
    
    
  3. 启动聊天界面:

    Java// 启动单聊
    CustomChatActivity.start(context, targetAccountId, false);
    
    // 启动群聊
    CustomChatActivity.start(context, teamId, true);
    

(可选)第七步:接入图片选择

具体请参考 图片选择器

常见功能

自定义界面 UI

如果您需要自定义界面 UI,可以通过设置聊天界面选项实现。例如,修改聊天界面标题栏右侧图标:

JavaChatUIConfig chatUIConfig = new ChatUIConfig();
    chatUIConfig.messageProperties = new MessageProperties();
    //设置右侧按钮图标
    chatUIConfig.messageProperties.titleBarRightRes = R.drawable.ic_user_setting;
    //设置右侧按钮单击事件
    chatUIConfig.messageProperties.titleBarRightClick = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            //todo 右侧按钮的单击事件
        }
    };
    ChatKitClient.setChatUIConfig(chatUIConfig);

更多 UI 定制示例,请参考 自定义会话消息界面 UI

增加自定义消息类型

如果您需要在聊天中支持自定义消息类型,可以通过以下步骤实现:

Java// 1. 创建自定义消息内容类
public class CustomMessageAttachment extends MsgAttachment {
    // 自定义数据字段
    private String customData;

    public CustomMessageAttachment(String data) {
        this.customData = data;
    }

    public String getCustomData() {
        return customData;
    }

    @Override
    public String toJson(boolean send) {
        JSONObject jsonObject = new JSONObject();
        try {
            jsonObject.put("type", "custom");
            jsonObject.put("data", customData);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return jsonObject.toString();
    }
}

// 2. 注册自定义消息解析器
ChatKitClient.addCustomAttachParser((data, msgType) -> {
    try {
        JSONObject jsonObject = new JSONObject(data);
        if ("custom".equals(jsonObject.optString("type"))) {
            return new CustomMessageAttachment(jsonObject.optString("data"));
        }
    } catch (JSONException e) {
        e.printStackTrace();
    }
    return null;
});

// 3. 注册自定义消息 UI 渲染器
ChatViewHolderFactory.getInstance().addCustomViewHolder(CustomMessageAttachment.class,
        (parent, viewType) -> new YourCustomViewHolder(parent));

发送自定义消息

Java// 创建自定义消息
CustomMessageAttachment attachment = new CustomMessageAttachment("自定义数据");
ChatMessageInfo messageInfo = ChatMessageBuilder.createCustomMessage(
        targetAccount, // 接收方账号
        SessionTypeEnum.P2P, // 单聊
        attachment);    // 自定义消息附件

// 发送消息
ChatRepo.sendMessage(messageInfo, false, null);

进阶功能

查询聊天记录

Java// 按时间查询历史消息
V2NIMMessageListOption.V2NIMMessageListOptionBuilder optionBuilder =
        V2NIMMessageListOption.V2NIMMessageListOptionBuilder.builder("conversationId") // 会话ID
            .withLimit(100) // 查询消息条数
            .withDirection(V2NIMMessageQueryDirection.V2NIM_QUERY_DIRECTION_DESC);// 查询顺序,DESC 表示从新到旧
// 查询单聊历史
ChatRepo.getMessageList(
          optionBuilder.build(),
          new FetchCallback<List<IMMessageInfo>>() {
            @Override
            public void onError(int errorCode, @Nullable String errorMsg) {
              // 查询失败
            }

            @Override
            public void onSuccess(@Nullable List<IMMessageInfo> param) {
              // 查询成功
            }
          });

消息已读回执

Java// 发送已读回执(单聊)
ChatRepo.sendP2PMessageReceipt(
        message, // 已读的消息对象
        new FetchCallback<Void>() {
            @Override
            public void onSuccess(@Nullable Void data) {
                // 已读回执发送成功
            }

            @Override
            public void onError(int code, @NonNull String errorMsg) {
                // 已读回执发送失败
            }
        });

        // 发送已读回执(群聊)
ChatRepo.sendTeamMessageReceipts(
        msgList, // 已读的消息对象列表
        new FetchCallback<Void>() {
            @Override
            public void onSuccess(@Nullable Void data) {
                // 已读回执发送成功
            }

            @Override
            public void onError(int code, @NonNull String errorMsg) {
                // 已读回执发送失败
            }
        });

过滤聊天内容敏感词

通过在发送消息前进行内容检测和过滤,可以实现敏感词过滤功能:

Java// 发送消息前的敏感词过滤
private void sendMessageWithFilter(String text, String targetAccount) {
    // 这里可以集成您自己的敏感词过滤逻辑
    if (containsSensitiveWords(text)) {
        Toast.makeText(context, "消息包含敏感词,请修改后重新发送", Toast.LENGTH_SHORT).show();
        return;
    }

    // 通过过滤后创建并发送消息
    ChatMessageInfo message = ChatMessageBuilder.createTextMessage(
            targetAccount, SessionTypeEnum.P2P, text);
    ChatRepo.sendMessage(message, false, null);
}

// 简单的敏感词检测示例
private boolean containsSensitiveWords(String text) {
    // 您可以从服务器或本地配置文件加载完整的敏感词列表
    String[] sensitiveWords = {"敏感词 1", "敏感词 2", "敏感词 3"};

    for (String word : sensitiveWords) {
        if (text.contains(word)) {
            return true;
        }
    }
    return false;
}

常见问题

消息发送失败

可能原因:

  • 用户未登录或登录状态已过期
  • 网络不稳定
  • 对方已设置了黑名单

解决方案:

  • 检查登录状态 IMKitClient.getLoginInfo() != null
  • 检查网络连接
  • 使用消息发送状态监听器捕获详细错误信息
Java// 监听消息发送状态
V2NIMMessageService v2MessageService = NIMClient.getService(V2NIMMessageService.class);

V2NIMMessageListener messageListener = new V2NIMMessageListener() {

    @Override
    public void onReceiveMessages(List<V2NIMMessage> messages) {
// 接受到消息
    }

        @Override
    public void onSendMessage(V2NIMMessage message) {

    // 发送的消息,接受发送中消息状态变更

    }
};
v2MessageService.addMessageListener(messageListener);

图片、语音等多媒体消息无法发送

可能原因:

  • 未获取相关权限(相机、存储、麦克风)
  • 文件大小超过限制
  • 文件格式不支持

解决方案:

  • 确保已正确请求并获取权限
  • 检查文件大小,建议默认限制单个附件最大 100MB
  • 使用支持的文件格式

处理运行时权限

Android 6.0 及以上版本需要动态申请敏感权限,以下是聊天功能需要的核心权限处理示例:

Javaprivate static final int PERMISSION_REQUEST_CODE = 100;

private void checkAndRequestPermissions() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        List<String> permissionsNeeded = new ArrayList<>();

        // 相机权限
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) !=
                PackageManager.PERMISSION_GRANTED) {
            permissionsNeeded.add(Manifest.permission.CAMERA);
        }

        // 麦克风权限
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) !=
                PackageManager.PERMISSION_GRANTED) {
            permissionsNeeded.add(Manifest.permission.RECORD_AUDIO);
        }

        // 存储权限
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { // Android 13+
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_IMAGES) !=
                    PackageManager.PERMISSION_GRANTED) {
                permissionsNeeded.add(Manifest.permission.READ_MEDIA_IMAGES);
            }
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_VIDEO) !=
                    PackageManager.PERMISSION_GRANTED) {
                permissionsNeeded.add(Manifest.permission.READ_MEDIA_VIDEO);
            }
        } else { // Android 12 及以下
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) !=
                    PackageManager.PERMISSION_GRANTED) {
                permissionsNeeded.add(Manifest.permission.READ_EXTERNAL_STORAGE);
            }
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) !=
                    PackageManager.PERMISSION_GRANTED) {
                permissionsNeeded.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
            }
        }

        // 申请权限
        if (!permissionsNeeded.isEmpty()) {
            ActivityCompat.requestPermissions(this,
                permissionsNeeded.toArray(new String[0]),
                PERMISSION_REQUEST_CODE);
        }
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
        @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);

    if (requestCode == PERMISSION_REQUEST_CODE) {
        // 检查权限授予结果
        boolean allGranted = true;
        for (int result : grantResults) {
            if (result != PackageManager.PERMISSION_GRANTED) {
                allGranted = false;
                break;
            }
        }

        if (allGranted) {
            // 所有权限都已授予,继续操作
        } else {
            // 提示用户权限缺失可能导致的功能限制
            Toast.makeText(this, "部分聊天功能(如发送图片、语音)可能无法正常使用,请在设置中开启相关权限",
                    Toast.LENGTH_LONG).show();
        }
    }
}
此文档是否对你有帮助?
有帮助
去反馈
  • 适用场景
  • 准备工作
  • 第一步:导入聊天组件
  • 第二步:添加必要权限
  • 第三步:配置代码混淆
  • 第四步:初始化 SDK
  • 第五步:登录 IM 账号
  • 第六步:使用聊天界面
  • 方式一:使用聊天 Activity(简单)
  • 方式二:集成到自定义 Activity(推荐)
  • (可选)第七步:接入图片选择
  • 常见功能
  • 自定义界面 UI
  • 增加自定义消息类型
  • 发送自定义消息
  • 进阶功能
  • 查询聊天记录
  • 消息已读回执
  • 过滤聊天内容敏感词
  • 常见问题
  • 消息发送失败
  • 图片、语音等多媒体消息无法发送
  • 处理运行时权限