Android專案中接入網易雲信聊天
首先上圖
由於專案中原有的聊天出現收發訊息不及時以及其他的問題,導致客服那邊損失了不少的訂單,遂接入新的第三方即時聊天sdk。有人可能會說,為什麼不自己寫呢?技術人員不夠,時間長,開發成本高,最主要的是,有幾個小公司自己搞聊天sdk啊!
首先看下網易雲信的開發者文件,建立賬號、應用,獲取api key。詳細請參考網易雲信連結
上面有詳細的接入步驟。我們下面特跟著步驟來。
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、將網易雲信服務與本地伺服器繫結,看下圖示
請求資料介面,登入,可以看下整合與登入的關係:
主要登入業務程式碼
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