Android项目中接入网易云信聊天

xiaoxiao2025-06-07  32

首先上图

由于项目中原有的聊天出现收发消息不及时以及其他的问题,导致客服那边损失了不少的订单,遂接入新的第三方即时聊天sdk。有人可能会说,为什么不自己写呢?技术人员不够,时间长,开发成本高,最主要的是,有几个小公司自己搞聊天sdk啊!

首先看下网易云信的开发者文档,创建账号、应用,获取api key。详细请参考网易云信链接

https://dev.yunxin.163.com/docs/product/IM%E5%8D%B3%E6%97%B6%E9%80%9A%E8%AE%AF/%E6%96%B0%E6%89%8B%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97

上面有详细的接入步骤。我们下面特跟着步骤来。

1、集成sdk进入项目中,文档上给出两种集成方式,通过Gradle和类库配置sdk,推荐是前一种方式。

2、sdk初始化工作

文档上有以下说明,可以在任意位置初始化

这里公司的项目是在MainActivity和Application做了初始化的处理,看下代码:

private void initUIKit() { // 初始化 NimUIKit.init(this, buildUIKitOptions()); // IM 会话窗口的定制初始化。 SessionHelper.init(); } private UIKitOptions buildUIKitOptions() { UIKitOptions options = new UIKitOptions(); // 设置app图片/音频/日志等缓存目录 options.appCacheDir = NimSDKOptionConfig.getAppCacheDir(this) + "/app"; return options; }

在Application中,主要是这句SessionHelper.init();// IM 会话窗口的定制初始化。然后是获取登录的accid和token,这两个参数具体是什么作用,在此就不多做说明,可以看下云信文档说明

private void initIMConfig() { NIMClient.init(this, loginInfo(), new SDKOptions()); } private SDKOptions options() { SDKOptions options = new SDKOptions(); return options; } private LoginInfo loginInfo() { String account = SPUtils.getInstance().getString("accid"); String token = SPUtils.getInstance().getString("token"); if (!TextUtils.isEmpty(account) && !TextUtils.isEmpty(token)) { return new LoginInfo(account, token); } return null; }

以上代码需要在onCreat()中进行。

3、网易云信的聊天功能主要集中在uikit中,需要作为library导入到项目中,图示1-2所示可以看到具体依赖哪个modlue

4、将网易云信服务与本地服务器绑定,看下图示

 请求数据接口,登录,可以看下集成与登录的关系:

https://dev.yunxin.163.com/docs/product/IM%E5%8D%B3%E6%97%B6%E9%80%9A%E8%AE%AF/%E4%BA%A7%E5%93%81%E4%BB%8B%E7%BB%8D/%E5%B8%90%E5%8F%B7%E9%9B%86%E6%88%90%E4%B8%8E%E7%99%BB%E5%BD%95

主要登录业务代码

private void getIMToken() { if (!LoginHelper.isLogin()) { return; } RestClient.builder() .url("这里填后端给的数据接口") .params("uid", LoginHelper.uid()) .params("secret", LoginHelper.secret()) .params("type", "2") .success(this::handleIMResult) .build() .post(); } private void handleIMResult(String response) { JLogger.e("TIM", "handleIMResult: " + response); final JSONObject jsonObject = JSON.parseObject(response); if (JConstants.OK.equals(jsonObject.getString("code"))) { final JSONObject data = jsonObject.getJSONObject("data"); final String token = data.getString("token"); final String accid = data.getString("accid"); //将accid和token值存放到本地 SPUtils.getInstance().put("accid", accid); SPUtils.getInstance().put("token", token); final LoginInfo info = new LoginInfo(accid, token); NIMClient.getService(AuthService.class).login(info) .setCallback(new RequestCallback() {//sdk提供的手动登录方法 @Override public void onSuccess(Object param) { JLogger.e("IM onSuccess: " + param.toString()); initUnReadMessage();//未读消息 initUserMessage();//更新用户本人资料 } @Override public void onFailed(int code) { JLogger.e("IM onFailed."); } @Override public void onException(Throwable exception) { JLogger.e("IM onException."); } }); } else if (JConstants.SECRET_ERROR.equals(jsonObject.getString("code"))) { LoginHelper.loginOut(); getSupportDelegate().start(new EcLoginDelegate()); } }

手动解析获取accid和token值。

登陆成功只能算完成了一小部分,接下来开始痛苦的调试移植删除工作了。

5、会话列表

要做到开头gif图的效果,RecentContactsFragment.java(这个类就是会话列表类)类需要继承项目中的JumeiDelegate(这个类是项目的应用Fragment基类,具体怎么回事就不做说明了,我们老大封装好了一系列的方法。)如此这般点击消息就能跳转到会话列表了。

由于这部分代码和网易云信的demo一样,我就不贴了。

6、启动单人会话列表

package com.jm.ec.im; import android.content.Context; import com.netease.nimlib.sdk.msg.constant.SessionTypeEnum; import cn.faxingw.uikit.api.NimUIKit; import cn.faxingw.uikit.business.session.viewholder.SessionHelper; public class ChatHelper {//项目中多处出现调用会话窗口,这里做了封装方便调用 public static void chat(Context context, String userId) {//启动单人会话的方法,传入accid即可 NimUIKit.startChatting(context, userId, SessionTypeEnum.P2P, SessionHelper.getMyP2pCustomization(), null); } }

7、单人会话列表的时下

单聊的方法主要在P2PMessageActivity.java类中,看下代码

package cn.faxingw.uikit.business.session.activity; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.widget.Toast; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.netease.nimlib.sdk.NIMClient; import com.netease.nimlib.sdk.Observer; import com.netease.nimlib.sdk.RequestCallback; import com.netease.nimlib.sdk.friend.FriendService; import com.netease.nimlib.sdk.friend.constant.VerifyType; import com.netease.nimlib.sdk.friend.model.AddFriendData; import com.netease.nimlib.sdk.msg.MsgServiceObserve; import com.netease.nimlib.sdk.msg.constant.SessionTypeEnum; import com.netease.nimlib.sdk.msg.model.CustomNotification; import com.netease.nimlib.sdk.msg.model.IMMessage; import java.util.List; import java.util.Set; import cn.faxingw.uikit.R; import cn.faxingw.uikit.api.NimUIKit; import cn.faxingw.uikit.api.model.contact.ContactChangedObserver; import cn.faxingw.uikit.api.model.main.OnlineStateChangeObserver; import cn.faxingw.uikit.api.model.session.SessionCustomization; import cn.faxingw.uikit.api.model.user.UserInfoObserver; import cn.faxingw.uikit.api.wrapper.NimToolBarOptions; import cn.faxingw.uikit.business.session.constant.Extras; import cn.faxingw.uikit.business.session.fragment.MessageFragment; import cn.faxingw.uikit.business.uinfo.UserInfoHelper; import cn.faxingw.uikit.common.activity.ToolBarOptions; import cn.faxingw.uikit.common.ui.imageview.HeadImageView; import cn.faxingw.uikit.impl.NimUIKitImpl; /** * 点对点聊天界面 * <p/> * Created by huangjun on 2015/2/1. */ public class P2PMessageActivity extends BaseMessageActivity { private boolean isResume = false; private boolean naviToStylistDetail = false; private String contactId; private HeadImageView avatarRight; public static void start(Context context, String contactId, SessionCustomization customization, IMMessage anchor, boolean naviToStylistDetail) { Intent intent = new Intent(); intent.putExtra(Extras.EXTRA_ACCOUNT, contactId); intent.putExtra(Extras.EXTRA_CUSTOMIZATION, customization); intent.putExtra("naviToStylistDetail", naviToStylistDetail); if (anchor != null) { intent.putExtra(Extras.EXTRA_ANCHOR, anchor); } intent.setClass(context, P2PMessageActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP); context.startActivity(intent); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 单聊特例话数据,包括个人信息, requestBuddyInfo(); // setHeadView(); displayOnlineState(); registerObservers(true); registerOnlineStateChangeListener(true); } private void setHeadView() { avatarRight = (HeadImageView)findViewById(R.id.message_item_portrait_right); avatarRight.loadBuddyAvatar(sessionId); } @Override protected void onDestroy() { super.onDestroy(); registerObservers(false); registerOnlineStateChangeListener(false); } @Override protected void onResume() { super.onResume(); isResume = true; } @Override protected void onStop() { super.onStop(); isResume = false; } private void requestBuddyInfo() { setTitle(UserInfoHelper.getUserTitleName(sessionId, SessionTypeEnum.P2P)); doAddFriend(null, true); // 直接加为好友 naviToStylistDetail = getIntent().getBooleanExtra("naviToStylistDetail", false); } private void doAddFriend(String msg, boolean addDirectly) { final VerifyType verifyType = addDirectly ? VerifyType.DIRECT_ADD : VerifyType.VERIFY_REQUEST; NIMClient.getService(FriendService.class).addFriend(new AddFriendData(sessionId, verifyType, msg)) .setCallback(new RequestCallback<Void>() { @Override public void onSuccess(Void param) { if (VerifyType.DIRECT_ADD == verifyType) { Log.d("TAG", "添加好友成功"); } else { Log.d("TAG", "添加好友请求发送成功"); } } @Override public void onFailed(int code) { if (code == 408) { Toast.makeText(P2PMessageActivity.this, R.string.network_is_not_available, Toast .LENGTH_SHORT).show(); } else { Log.i("TAG","on failed:"+code); } } @Override public void onException(Throwable exception) { } }); } private void registerObservers(boolean register) { if (register) { registerUserInfoObserver(); } else { unregisterUserInfoObserver(); } NIMClient.getService(MsgServiceObserve.class).observeCustomNotification(commandObserver, register); NimUIKit.getContactChangedObservable().registerObserver(friendDataChangedObserver, register); } ContactChangedObserver friendDataChangedObserver = new ContactChangedObserver() { @Override public void onAddedOrUpdatedFriends(List<String> accounts) { setTitle(UserInfoHelper.getUserTitleName(sessionId, SessionTypeEnum.P2P)); } @Override public void onDeletedFriends(List<String> accounts) { setTitle(UserInfoHelper.getUserTitleName(sessionId, SessionTypeEnum.P2P)); } @Override public void onAddUserToBlackList(List<String> account) { setTitle(UserInfoHelper.getUserTitleName(sessionId, SessionTypeEnum.P2P)); } @Override public void onRemoveUserFromBlackList(List<String> account) { setTitle(UserInfoHelper.getUserTitleName(sessionId, SessionTypeEnum.P2P)); } }; private UserInfoObserver uinfoObserver; OnlineStateChangeObserver onlineStateChangeObserver = new OnlineStateChangeObserver() { @Override public void onlineStateChange(Set<String> accounts) { // 更新 toolbar if (accounts.contains(sessionId)) { // 按照交互来展示 displayOnlineState(); } } }; private void registerOnlineStateChangeListener(boolean register) { if (!NimUIKitImpl.enableOnlineState()) { return; } NimUIKitImpl.getOnlineStateChangeObservable().registerOnlineStateChangeListeners(onlineStateChangeObserver, register); } private void displayOnlineState() { if (!NimUIKitImpl.enableOnlineState()) { return; } String detailContent = NimUIKitImpl.getOnlineStateContentProvider().getDetailDisplay(sessionId); setSubTitle(detailContent); } private void registerUserInfoObserver() { if (uinfoObserver == null) { uinfoObserver = new UserInfoObserver() { @Override public void onUserInfoChanged(List<String> accounts) { if (accounts.contains(sessionId)) { requestBuddyInfo(); } } }; } NimUIKit.getUserInfoObservable().registerObserver(uinfoObserver, true); } private void unregisterUserInfoObserver() { if (uinfoObserver != null) { NimUIKit.getUserInfoObservable().registerObserver(uinfoObserver, false); } } /** * 命令消息接收观察者 */ Observer<CustomNotification> commandObserver = new Observer<CustomNotification>() { @Override public void onEvent(CustomNotification message) { if (!sessionId.equals(message.getSessionId()) || message.getSessionType() != SessionTypeEnum.P2P) { return; } showCommandMessage(message); } }; protected void showCommandMessage(CustomNotification message) { if (!isResume) { return; } String content = message.getContent(); try { JSONObject json = JSON.parseObject(content); int id = json.getIntValue("id"); if (id == 1) { // 正在输入 Toast.makeText(P2PMessageActivity.this, "对方正在输入...", Toast.LENGTH_LONG).show(); } else { Toast.makeText(P2PMessageActivity.this, "command: " + content, Toast.LENGTH_SHORT).show(); } } catch (Exception e) { } } @Override protected MessageFragment fragment() { Bundle arguments = getIntent().getExtras(); arguments.putSerializable(Extras.EXTRA_TYPE, SessionTypeEnum.P2P); MessageFragment fragment = new MessageFragment(); fragment.setArguments(arguments); fragment.setContainerId(R.id.message_fragment_container); return fragment; } @Override protected int getContentViewId() { return R.layout.nim_message_activity; } @Override protected void initToolBar() { ToolBarOptions options = new NimToolBarOptions(); setToolBar(R.id.toolbar, options); } }

这里说明下doAddFriend方法,网易云信demo中开始和对方会话是先加好友,或是经过对方同意后加好友,这里直接加好友后开始聊天。

服务器端在加用户好友之前需要获取用户的信息,将用户的sid转换为accid。这个方法放在了MessageFragment.java中的getFriendInfo()方法

private void getFriendInfo() { RestClient.builder() .url("这里是用户信息接口") .params("accid", sessionId) .success(new ISuccess() { @Override public void onSuccess(String response) { JLogger.e(response); final JSONObject jsonObject = JSON.parseObject(response); if ("101".equals(jsonObject.getString("code"))) { StylistEntity stylistEntity = StylistEntityConverter.convert(sessionId, response); Jumei.getConfigurator().withStylistId(stylistEntity.getAccid()); } } }) .error(new IError() { @Override public void onError(int code, String msg) { Jumei.getConfigurator().withStylistId(""); } }) .failure(new IFailure() { @Override public void onFailure() { Jumei.getConfigurator().withStylistId(""); } }) .build() .post(); }

再来看setTitle(UserInfoHelper.getUserTitleName(sessionId, SessionTypeEnum.P2P));这个方法是获取对方的姓名,网上有人在这里遇到坑了,可以看下这篇文章https://blog.csdn.net/brucechen1994/article/details/79787896

我的方法是直接绕过去了

package cn.faxingw.uikit.business.uinfo; import android.text.TextUtils; import com.netease.nimlib.sdk.msg.constant.SessionTypeEnum; import com.netease.nimlib.sdk.uinfo.model.UserInfo; import cn.faxingw.uikit.api.NimUIKit; import cn.faxingw.uikit.business.team.helper.TeamHelper; public class UserInfoHelper { // 获取用户显示在标题栏和最近联系人中的名字 public static String getUserTitleName(String id, SessionTypeEnum sessionType) { if (sessionType == SessionTypeEnum.P2P) { String account = NimUIKit.getAccount(); if (account !=null) { return "我的电脑"; } else { return getUserDisplayName(id); } } else if (sessionType == SessionTypeEnum.Team) { return TeamHelper.getTeamName(id); } return id; } /** * @param account 用户帐号 * @return */ public static String getUserDisplayName(String account) { String alias = NimUIKit.getContactProvider().getAlias(account); if (!TextUtils.isEmpty(alias)) { return alias; } else { UserInfo userInfo = NimUIKit.getUserInfoProvider().getUserInfo(account); if (userInfo != null && !TextUtils.isEmpty(userInfo.getName())) { return userInfo.getName(); } else { return account; } } } // 获取用户原本的昵称 public static String getUserName(String account) { UserInfo userInfo = NimUIKit.getUserInfoProvider().getUserInfo(account); if (userInfo != null && !TextUtils.isEmpty(userInfo.getName())) { return userInfo.getName(); } else { return account; } } /** * @param account 账号 * @param selfNameDisplay 如果是自己,则显示内容 * @return */ public static String getUserDisplayNameEx(String account, String selfNameDisplay) { if (account.equals(NimUIKit.getAccount())) { return selfNameDisplay; } return getUserDisplayName(account); } }

项目中注释了原有的很多东西才跑通、、调试的过程不说也罢。其他的就是些网易云信原本的东西了。至此,项目引进网易云聊天,当然后续的点击客服头像跳转到发型师详情页面,这涉及到EventBus的知识,下次再说咯。

最后贴上我项目中uikit聊天的百度云链接,其实基本上和网易云信的demo是相同的,只是注释了定位,群聊,聊天室等一些项目中用不到的功能,只是简单的聊天而已

uikit

https://pan.baidu.com/s/1ymXV3FnD17P1giOF8Q1qlg 

网易云信demo百度云链接

https://pan.baidu.com/s/1ekML668Sp6ukuYyCW0E60w

 

转载请注明原文地址: https://www.6miu.com/read-5031415.html

最新回复(0)