仅集成聊天
更新时间: 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登录 --> 使用聊天界面
适用场景
仅集成聊天功能而不包括完整社交功能的场景通常有以下几种:
- 客户服务与在线支持:在这类场景中,聊天功能作为客户与企业沟通的桥梁,例如在线客服、智能客服数字人、售后支持。
- 专业服务咨询:垂直领域的辅助沟通工具,例如医疗咨询、教育平台、金融顾问、法律咨询。
- 电商与交易平台:买卖双方沟通,例如订单状态通知、砍价/议价功能。
- 专业协作工具:项目管理软件,例如设计协作平台、代码协作工具。
- 特定社交场景:约会应用的初期互动,例如陌生人社交、兴趣小组。
- 嵌入式通讯功能:游戏内聊天,例如直播平台、共享经济应用。
准备工作
在开始集成前,请确保您已完成以下操作:
- 在 网易云信控制台 创建应用,开通即时通讯 IM 服务,获取 App Key。
- 注册 IM 账号,获取账号 ID (
account_id
) 和凭证 (token
)。 - 准备开发环境:
- 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 组件库,您可任意选择一种使用。




方式一:使用聊天 Activity(简单)
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);
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(推荐)
-
创建聊天界面布局:
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>
-
在自定义 Activity 中集成聊天 Fragment:
基础版 UI 界面Java
public 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 界面Java
public 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); } }
-
启动聊天界面:
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();
}
}
}