1.準備工作 : 應用接入指引
注意 sdkappid 和 accounttype ,之后將會在代碼中用到。
2.了解用戶集成體系:獨立模式和托管模式
如果您的APP自主維護用戶的注冊、用戶身份的驗證,則應當使用獨立模式,
如果您只是想快速開發(fā)一個APP原型,云通信可以為您提供一套符合業(yè)界通用安全標準的用戶體系,用戶的注冊、用戶身份的驗證將全部由云通信提供,此時應當選用托管模式。
【獨立模式】:用戶帳號信息由開發(fā)者保存,用戶身份驗證(比如注冊與驗密)也由開發(fā)者負責;
【托管模式】:由騰訊為開發(fā)者提供帳號的密碼注冊、存儲和密碼驗證,以及第三方 openid 和 token 的托管驗證服務。
公私鑰(密碼學中非對稱加密算法中的概念,用于鑒別開發(fā)者用戶的身份合法性)
【獨立模式】:私鑰由開發(fā)者保存,公鑰由騰訊保存。開發(fā)者使用私鑰生成用戶簽名 UserSig,騰訊使用公鑰對簽名 UserSig 進行校驗。
【托管模式】:私鑰由騰訊保存,公鑰由開發(fā)者保存。騰訊使用私鑰生成用戶簽名 UserSig,開發(fā)者可以使用公鑰對簽名 UserSig 進行校驗,注意,對于第三方開放帳號,此時不需要公私鑰。
本測試案例選擇的是托管模式,這里由 TLS 為開發(fā)者提供 App 帳號的密碼注冊、存儲和密碼驗證。帳號驗證成功后,派發(fā)私鑰加密生成的簽名到客戶端,App 業(yè)務服務器可以通過 下載的公鑰 解密簽名進行驗證。

3.開始項目
首先下載demo,并按照demo格式將以下四個moudle集成

更改sdk庫中的參數(shù)

demo中是三個tab,我們測試的例子中只有一個聊天列表的頁面。主要為ConversationFragment,這里是為了測試是我們自己的邏輯,不同于demo中的ConversationFragment。
想要實現(xiàn)點對點聊天,如果為了方便 我們可以直接在騰訊云通信后臺中配置中手動添加兩個管理員

并下載用戶憑證,分別得到admin1和admin2的 usersig 用戶登錄所需的兩個參數(shù)一個是id,另一個就是UserSig

初始化配置
@Override
public void initTencentImConfig() {
//初始化IMSDK
InitBusiness.start(mView.getApplicationContext(), TIMLogLevel.DEBUG.ordinal());
//初始化TLS
TlsBusiness.init(mView.getApplicationContext());
Log.d("tencentim", "初始化騰訊云Im");
String id = TLSService.getInstance().getLastUserIdentifier();
UserInfo.getInstance().setId(id);
UserInfo.getInstance().setUserSig(TLSService.getInstance().getUserSig(id));
// 初始化TLSSDK
TLSHelper tlsHelper = TLSHelper.getInstance().init(mView.getApplicationContext(), Constant.SDK_APPID);
//登錄
login();
//simulateRegistered();//模擬注冊
}
3登錄
private void login(){
LoginBusiness.loginIm("admin1",
"eJx1z09PgzAYx-E7r6LpVWNoiwxMPLCNsC7imMM-eGmAduYJ0nVQF6fxvRtxiVx8rr9P8s3z6SCEcH6zuSjrevemrbBHozC6QpgxFlJ8-geMASlKK1gnB0A81yWUTggbKfVuoFOi3FrV-arLiR*4PzdSIJW2sIWTKWULmoz2XjZi6P0f6uFlGNP4fsYT6S9YlUyrffC4mpuP43Ma8Wy*uivyPuN6Rj2Z5E8*OVQmgjhqikIHTbrTSQNn1XT-cKhjEoYZvG5aWsiA6wVdrvntMl1fj5IWWnX6iTE-DJhHsfPlfAPflld1",
new TIMCallBack() {
@Override
public void onError(int i, String s) {
Log.d("tencentim", s);
Toast.makeText(mView.getContext(), s, Toast.LENGTH_SHORT).show();
}
@Override
public void onSuccess() {
Toast.makeText(mView.getContext(), "admin1登錄成功", Toast.LENGTH_SHORT).show();
sendConversation();
}
});
}
發(fā)送消息
//通用消息發(fā)送
//為了生成對話,每次跳轉到這里都發(fā)送一條消息給對方
@Override
public void sendConversation() {
TIMConversation timConversation;
//獲取單聊會話
String peer = "admin2"; //獲取與用戶 "xxx" 的會話
timConversation = TIMManager.getInstance().getConversation(
TIMConversationType.C2C, //會話類型:單聊
peer); //會話對方用戶帳號//對方id
//構造一條消息
TIMMessage msg = new TIMMessage();
//添加文本內容
TIMTextElem elem = new TIMTextElem();
elem.setText("你好admin2,我是admin1,哈哈哈");
//將elem添加到消息
if (msg.addElement(elem) != 0) {
Log.d("tencentim", "addElement failed");
return;
}
//發(fā)送消息
timConversation.sendMessage(msg, new TIMValueCallBack<TIMMessage>() {//發(fā)送消息回調
@Override
public void onError(int code, String desc) {//發(fā)送消息失敗
//錯誤碼code和錯誤描述desc,可用于定位請求失敗原因
//錯誤碼code含義請參見錯誤碼表
Log.d("tencentim", "send message failed. code: " + code + " errmsg: " + desc);
}
@Override
public void onSuccess(TIMMessage msg) {//發(fā)送消息成功
Log.e("tencentim", "SendMsg ok");
}
});
}
取得會話
/**
* 取得會話
*/
@Override
public void getConversation() {
List<TIMConversation> list = TIMManagerExt.getInstance().getConversationList();
List<TIMConversation> result = new ArrayList<>();
for (TIMConversation conversation : list) {
//continue用來結束本次循環(huán)
if (conversation.getType() == TIMConversationType.System) continue;
result.add(conversation);
TIMConversationExt conversationExt = new TIMConversationExt(conversation);
conversationExt.getMessage(1, null, new TIMValueCallBack<List<TIMMessage>>() {
@Override
public void onError(int i, String s) {
Log.e("tencentim", "get message error" + s);
}
@Override
public void onSuccess(List<TIMMessage> timMessages) {
if (timMessages.size() > 0) {
mView.updateMessage(timMessages.get(0));
}
}
});
}
mView.initView(result);
}
通知view更新列表,將conversations添加到adapter中,更新列表。當然,我們前提會注冊監(jiān)聽器
public class ConversationPresenter implements ConversationContract.Presenter, Observer {
private ConversationContract.View mView;
private TLSStrAccRegListener strAccRegListener;
private TLSHelper tlsHelper;
public ConversationPresenter(ConversationContract.View view) {
//注冊消息監(jiān)聽
MessageEvent.getInstance().addObserver(this);
//注冊刷新監(jiān)聽
RefreshEvent.getInstance().addObserver(this);
//注冊好友關系鏈監(jiān)聽
FriendshipEvent.getInstance().addObserver(this);
//注冊群關系監(jiān)聽
GroupEvent.getInstance().addObserver(this);
this.mView = view;
mView.setPresenter(this);
}
......
......
@Override
public void update(Observable observable, Object data) {
if (observable instanceof MessageEvent) {
Log.d("tencentim", "更新消息");
if (data instanceof TIMMessage) {
TIMMessage msg = (TIMMessage) data;
mView.updateMessage(msg);
}
} else if (observable instanceof FriendshipEvent) {
Log.d("tencentim", "更新好友鏈");
FriendshipEvent.NotifyCmd cmd = (FriendshipEvent.NotifyCmd) data;
switch (cmd.type) {
case ADD_REQ:
case READ_MSG:
case ADD:
mView.updateFriendshipMessage();
break;
}
} else if (observable instanceof GroupEvent) {
Log.d("tencentim", "更新群組");
GroupEvent.NotifyCmd cmd = (GroupEvent.NotifyCmd) data;
switch (cmd.type) {
case UPDATE:
case ADD:
mView.updateGroupInfo((TIMGroupCacheInfo) cmd.data);
break;
case DEL:
mView.removeConversation((String) cmd.data);
break;
}
} else if (observable instanceof RefreshEvent) {
Log.d("tencentim", "更新會話列表");
getConversation();
mView.refresh();
}
}
}
更改登錄 admin1 admin2 的信息(id,和usersig) 分別運行,效果如下圖
登錄admin1 模擬發(fā)消息給admin2

登錄admin1 模擬發(fā)消息給admin2
再登錄admin2 模擬發(fā)消息給admin1 同時他也收到admin1之前給admin2發(fā)的消息

如此以來 通過手動添加實現(xiàn)一個簡單的c2c聊天
但是我們不能都通過后臺添加管理員 我們需要注冊登錄實現(xiàn)聊天,那我們開始通過代碼實現(xiàn)。首先我們要模擬注冊和模擬登錄,托管模式我們主要依靠的是tls。主要寫模擬注冊和模擬登錄 省略頁面部分。
主要流程 simulateRegistered();//模擬注冊--->simulateLogin();//模擬登錄,主要是為了獲得usersig-->客戶端登錄驗證-->發(fā)送消息
private void simulateRegistered() {
// 引導用戶輸入合法的用戶名和密碼
int result = tlsHelper.TLSStrAccReg("用戶1", "12345678", new TLSStrAccRegListener() {
@Override
public void OnStrAccRegSuccess(TLSUserInfo tlsUserInfo) {
/* 成功注冊了一個字符串帳號, 可以引導用戶使用剛注冊的用戶名和密碼登錄 */
simulateLogin("用戶1","12345678");
}
@Override
public void OnStrAccRegFail(TLSErrInfo tlsErrInfo) {
/* 注冊失敗,請?zhí)崾居脩羰≡?*/
}
@Override
public void OnStrAccRegTimeout(TLSErrInfo tlsErrInfo) {
/* 網(wǎng)絡超時,可能是用戶網(wǎng)絡環(huán)境不穩(wěn)定,一般讓用戶重試即可。*/
}
});
if (result == TLSErrInfo.INPUT_INVALID){
Log.d("tencentim","注冊失敗");
}
}
private void simulateLogin(final String username, String password) {
tlsHelper.TLSPwdLogin(username, password.getBytes(), new TLSPwdLoginListener() {
@Override
public void OnPwdLoginSuccess(TLSUserInfo tlsUserInfo) {
String usersig = tlsHelper.getUserSig(tlsUserInfo.identifier);
Log.d("tencentim","usersig:"+usersig);
//獲得usersig后登錄
LoginBusiness.loginIm(username, usersig,
new TIMCallBack() {
@Override
public void onError(int i, String s) {
Log.d("tencentim", s);
Toast.makeText(mView.getContext(), s, Toast.LENGTH_SHORT).show();
}
@Override
public void onSuccess() {
Toast.makeText(mView.getContext(), username + "登錄成功", Toast.LENGTH_SHORT).show();
sendConversation();
getConversation();
}
});
}
@Override
public void OnPwdLoginReaskImgcodeSuccess(byte[] bytes) {
}
@Override
public void OnPwdLoginNeedImgcode(byte[] bytes, TLSErrInfo tlsErrInfo) {
Log.d("tencentim","用戶需要進行圖片驗證碼的驗證");
}
@Override
public void OnPwdLoginFail(TLSErrInfo tlsErrInfo) {
}
@Override
public void OnPwdLoginTimeout(TLSErrInfo tlsErrInfo) {
}
});
}
如果賬戶存在,則不能注冊,進行登錄驗證即可。注意注冊時要數(shù)字字母都有,位數(shù)不能過少,具體看官方規(guī)定,自行測試。

登錄usertest1,收到usertest2的消息