安卓集成腾讯即时通信IM完成聊天室功能
安卓集成腾讯即时通信IM完成聊天室功能
-
- 没有效果图的文章都是扯淡
- 请将下面的MainActivity的代码复制到源码里面,替换掉源码的MainActivity.class
- 话不多说,下来上代码:
- 以上就是所有的代码
- 附上demo源码。
- 源码:[源码请点这里](https://download.csdn.net/download/qq_35840038/12729429)
- 如果有什么问题,欢迎大家指导。并相互联系,希望能够通过文章互相学习。
没有效果图的文章都是扯淡
延续我们的优良传统,先上效果图
效果图如下:
注意:需继承AppCompatActivity
请将下面的MainActivity的代码复制到源码里面,替换掉源码的MainActivity.class
这里我用了两个手机做测试,同样对照activity里面有两个userid和usersig。注意两个手机不能安装同一个userid的APP,否则会被挤下线
近期,由于项目需要,研究了一下腾讯的即时通信IM,完成了一个类似直播间聊天的功能。具体需求为当进入一个直播间时,可以发送并接收当前直播间的所有聊天消息,从而完成直播间聊天功能。
首先了解一下腾讯的即时通信IM(地址为:https://cloud.tencent.com/document/product/269)
注:我用的是无UI库的,聊天室用RecyclerView显示即可。
//这里列出即时通信IM的概要;
1.开发者后台注册拿到APPID和appkey(参考官方文档)
2.初始化并完成登录;
3.登录完成后修改个人资料(这一步是我在测试中发现的,如果不修改对方收到你发的消息后,昵称为null)
4.登录完成后加入一个直播间(需要直播间id和直播间名称–这是服务端返回的)
5.加入后监听接收消息回调并调用发送消息方法;
6.退出时先退出直播间,然后注销SDK初始化和SDK登录。
配置请参考demo
话不多说,下来上代码:
先看主界面xml:
注:这里的自定义CustomLinearLayout是为了解决点击输入框浮在键盘上顶部出现空白的问题
这个是recyclerview的item布局:
注:布局就这么一丢丢
下来给出自定义的布局和消息存储的实体类
package com.edu.im_demo;import android.content.Context;import android.graphics.Rect;import android.os.Build;import android.util.AttributeSet;import android.view.WindowInsets;import android.widget.LinearLayout;import androidx.annotation.RequiresApi;public class CustomLinearLayout extends LinearLayout { public CustomLinearLayout(Context context) { super(context); } public CustomLinearLayout(Context context, AttributeSet attrs) { super(context, attrs); } public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override protected boolean fitSystemWindows(Rect insets) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { insets.left = 0; insets.top = 0; insets.right = 0; } return super.fitSystemWindows(insets); } @RequiresApi(api = Build.VERSION_CODES.KITKAT_WATCH) @Override public WindowInsets onApplyWindowInsets(WindowInsets insets) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { return super.onApplyWindowInsets(insets.replaceSystemWindowInsets(0, 0, 0, insets.getSystemWindowInsetBottom())); } else { return insets; } }}
注:主界面布局中使用
实体类
package com.edu.im_demo;public class ChatMsgBean { private String nickname; private String content; public ChatMsgBean(){} public ChatMsgBean(String nickname, String content) { this.nickname = nickname; this.content = content; } public String getNickname() { return nickname; } public void setNickname(String nickname) { this.nickname = nickname; } public String getContent() { return content; } public void setContent(String content) { this.content = content; }}
注:接收消息发出的类
下来给出适配器
package com.edu.im_demo;import android.app.Activity;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.TextView;import androidx.recyclerview.widget.RecyclerView;import com.orz.nb.plus.emojiview.EmojiUtils;import java.util.List;/ * 聊天室 */public class ChatMsgAdapter extends RecyclerView.Adapter { / 上下文 */ Activity context; / 数据源 */ List data; / 控件 */ LayoutInflater inflater; / * 这里的data作为数据源从activity传入 * @param activity * @param datas */ public ChatMsgAdapter(Activity activity, List datas){ this.context = activity; this.data = datas; //获取布局 inflater = LayoutInflater.from(activity); } / * 加载布局,相当于activity的onCreate方法 * @param parent * @param viewType * @return */ @Override public ChatMsgAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.chit_item, parent, false); return new ChatMsgAdapter.ViewHolder(view); } / * 绑定数据 * @param viewHolder * @param position */ @Override public void onBindViewHolder(final ChatMsgAdapter.ViewHolder viewHolder, int position) { viewHolder.chat_item_nickname.setText(data.get(position).getNickname() + ":");// viewHolder.chat_item_content.setText(data.get(position).getContent()); EmojiUtils.showEmoji(viewHolder.chat_item_content, data.get(position).getContent()); } / * 数据源的内容大小 * @return */ @Override public int getItemCount() { return data.size(); } public void refresh() { notifyDataSetChanged(); } / * //自定义的ViewHolder,持有每个Item的的所有界面元素 */ public class ViewHolder extends RecyclerView.ViewHolder { private TextView chat_item_nickname, chat_item_content; public ViewHolder(View rootView) { super(rootView); chat_item_nickname = rootView.findViewById(R.id.chat_item_nickname); chat_item_content = rootView.findViewById(R.id.chat_item_content); } }}
我自己写的IM管理类
package com.edu.im_demo;import android.content.Context;import android.text.TextUtils;import androidx.annotation.NonNull;import com.tencent.imsdk.v2.V2TIMCallback;import com.tencent.imsdk.v2.V2TIMManager;import com.tencent.imsdk.v2.V2TIMMessage;import com.tencent.imsdk.v2.V2TIMSDKConfig;import com.tencent.imsdk.v2.V2TIMSDKListener;import com.tencent.imsdk.v2.V2TIMSimpleMsgListener;import com.tencent.imsdk.v2.V2TIMValueCallback;/ * 这是我整合的IM的管理类 */public class IMManager { / * 是否已经登录到 im 服务器 * * 用户已经处于已登录和登录中状态,则认为用户已经登录,无需在进行登录 */ public static boolean isLoginIMService(){ int loginStatus = V2TIMManager.getInstance().getLoginStatus(); return loginStatus == V2TIMManager.V2TIM_STATUS_LOGINED || loginStatus == V2TIMManager.V2TIM_STATUS_LOGINING; } / * 用户登录到IM服务器 * * @param userID 用户编号 * @param userSig 用户签名 * @param callback 登录成功与否回调 */ public static void login(String userID, String userSig, V2TIMCallback callback) { if (isLoginIMService()) { callback.onSuccess(); return; } V2TIMManager.getInstance().login(userID, userSig, callback); } / * 初始化 IM 连接腾讯服务器 * * @param context 上下文对象 * @param imSDKConfig imSdk配置 * @param listener连接腾讯服务器回调 */ public static void initConnectTXService( Context context, int imSdkAppId, V2TIMSDKConfig imSDKConfig, V2TIMSDKListener listener ) { V2TIMManager.getInstance() .initSDK(context, imSdkAppId, imSDKConfig, listener); } / * 创建一个直播聊天室 * * @param groupType 群类型(V2TIMManager.GROUP_TYPE_AVCHATROOM) * * "Work" :工作群 * "Public" :公开群 * "Meeting" :会议群 * "AVChatRoom" :直播群 *
* * @param groupId 聊天室 id * @param groupName 聊天室名称 * @param callback 创建状态回调 */ public static void createLiveRoom( String groupType, String groupId, @NonNull String groupName, V2TIMValueCallback callback ) { V2TIMManager.getInstance().createGroup(groupType, groupId, groupName, callback); } / * 加入到某一个直播间 IM * * @param groupId 聊天室 id * @param listener 是否成功接入回调 */ public static void joinToLiveRoom(String groupId, String msg, V2TIMCallback listener) { V2TIMManager.getInstance().joinGroup(groupId, msg, listener); } / * 退出直播直播间 * * @param groupId 聊天室 id */ public static void exitLiveRoom(String groupId) { V2TIMManager.getInstance().quitGroup(groupId, null); } / * 发送纯文本消息到聊天群组 * @param msg 发送的聊天内容 * @param groupId 聊天室 ID * @param priority 发送消息的优先级 (V2TIMMessage.V2TIM_PRIORITY_NORMAL) * * V2TIMMessage.V2TIM_PRIORITY_HIGH:云端会优先传输,适用于在群里发送重要消息,比如主播发送的文本消息等。 * V2TIMMessage.V2TIM_PRIORITY_NORMAL:云端按默认优先级传输,适用于在群里发送非重要消息,比如观众发送的弹幕消息等。 *
* @param callback 发送状态回调 */ public static void sendTextMsg( String msg, String groupId, int priority, V2TIMValueCallback callback ) { V2TIMManager.getInstance().sendGroupTextMessage( msg, groupId, priority, callback ); } / * 发送单聊普通文本消息 */ public static void sendC2CTextMsg(String msg, String userID, V2TIMValueCallback callback) { V2TIMManager.getInstance().sendC2CTextMessage(msg, userID, callback); } / * 接受消息 */ public static void receiveMsg(V2TIMSimpleMsgListener callback) { V2TIMManager.getInstance().addSimpleMsgListener(callback); } / * 解散聊天室 */ public static void dismissGroup(String groupId) { if (TextUtils.isEmpty(groupId)) { V2TIMManager.getInstance().dismissGroup(groupId, null); } } / * 用户退出 im 登录 */ public static void userLogout() { V2TIMManager.getInstance().logout(null); } / * 登出账号并注销 im sdk */ public static void unInitIMSDk() { userLogout(); V2TIMManager.getInstance().unInitSDK(); }}
重中之重主界面MainActivity来啦
package com.edu.im_demo;import android.app.Activity;import android.content.Context;import android.os.Bundle;import android.util.Log;import android.view.KeyEvent;import android.view.View;import android.view.inputmethod.EditorInfo;import android.view.inputmethod.InputMethodManager;import android.widget.EditText;import android.widget.ImageView;import android.widget.TextView;import android.widget.Toast;import androidx.appcompat.app.AppCompatActivity;import androidx.recyclerview.widget.LinearLayoutManager;import androidx.recyclerview.widget.RecyclerView;import com.orz.nb.plus.emojiview.EmojiView;import com.tencent.imsdk.v2.V2TIMCallback;import com.tencent.imsdk.v2.V2TIMGroupMemberInfo;import com.tencent.imsdk.v2.V2TIMManager;import com.tencent.imsdk.v2.V2TIMMessage;import com.tencent.imsdk.v2.V2TIMSDKConfig;import com.tencent.imsdk.v2.V2TIMSDKListener;import com.tencent.imsdk.v2.V2TIMSimpleMsgListener;import com.tencent.imsdk.v2.V2TIMUserFullInfo;import com.tencent.imsdk.v2.V2TIMValueCallback;import java.util.ArrayList;import java.util.List;/ * 腾讯IM完成聊天室 * 直接运行即可 */public class MainActivity extends AppCompatActivity { private Activity mActivity = MainActivity.this; //IM测试的账号数据 private int IMSDKAPPID = 1400415921; private String ROOMID = "@TGS#aDGNB5UG3"; private String ROOMNAME = "测试"; //控件 RecyclerView chat_rv; EditText send_msg; ImageView chat_img_emojy; EmojiView emoji_view; List lists; ChatMsgAdapter chatMsgAdapter; / * 隐藏键盘 */ private void hintKbOne() { InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); View v = getWindow().peekDecorView(); if (null != v) { imm.hideSoftInputFromWindow(v.getWindowToken(), 0); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); chat_rv = findViewById(R.id.chat_rv); chat_img_emojy = findViewById(R.id.chat_img_emojy); send_msg = findViewById(R.id.send_msg); emoji_view = findViewById(R.id.emoji_view); //绑定表情 emoji_view.setTarget(send_msg); chat_img_emojy.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { emoji_view.setVisibility(View.VISIBLE); hintKbOne(); send_msg.clearFocus(); } }); send_msg.setOnFocusChangeListener(new android.view.View. OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { if (hasFocus) { emoji_view.setVisibility(View.GONE); }else{ } } }); //初始化rv initRecyclerView(); //发送消息 send_msg.setOnEditorActionListener(new TextView.OnEditorActionListener() { @Override public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if (actionId == EditorInfo.IME_ACTION_SEND) { if(send_msg.getText().toString().trim().length() == 0){ Toast.makeText(mActivity, "说点什么吧", Toast.LENGTH_SHORT).show(); return true; } hintKbOne(); sendMessage(send_msg.getText().toString()); //清空 send_msg.setText(""); return true; } return false; } }); //腾讯IM实现初始化 init(); //接收消息 getMessage(); } / * 接收消息 */ private void getMessage() { //接收消息 IMManager.receiveMsg(new V2TIMSimpleMsgListener() { @Override public void onRecvGroupTextMessage(String msgID, String groupID, V2TIMGroupMemberInfo sender, String text) { super.onRecvGroupTextMessage(msgID, groupID, sender, text); Log.i("腾讯云即时通信IM", "接收成功" + sender.toString()); addMessage(sender.getNickName(), text); } }); } / * IM初始化 */ private void init() { //腾讯云IM V2TIMSDKConfig config = new V2TIMSDKConfig(); // 3. 指定 log 输出级别,详情请参考 SDKConfig。 config.setLogLevel(V2TIMSDKConfig.V2TIM_LOG_INFO); // 4. 初始化 SDK 并设置 V2TIMSDKListener 的监听对象。 IMManager.initConnectTXService(mActivity, IMSDKAPPID, config, new V2TIMSDKListener() { @Override public void onConnecting() { super.onConnecting(); Log.i("腾讯云即时通信IM", "正在连接腾讯云服务器"); } @Override public void onConnectSuccess() { super.onConnectSuccess(); Log.i("腾讯云即时通信IM", "连接腾讯云服务器成功"); if(IMManager.isLoginIMService()){ joinRoom(); }else{ IMManager.userLogout(); login(); } } @Override public void onConnectFailed(int code, String error) { super.onConnectFailed(code, error); Log.i("腾讯云即时通信IM", "连接腾讯云服务器失败"); } @Override public void onKickedOffline() { super.onKickedOffline(); Log.i("腾讯云即时通信IM", "当前用户被踢下线"); login(); } @Override public void onUserSigExpired() { super.onUserSigExpired(); Log.i("腾讯云即时通信IM", "登录票据已经过期"); login(); } }); } / * 实例化RecyclerView */ public void initRecyclerView() { lists = new ArrayList(); chatMsgAdapter = new ChatMsgAdapter(mActivity, lists); LinearLayoutManager linearLayoutManager = new LinearLayoutManager(mActivity); linearLayoutManager.setStackFromEnd(true); linearLayoutManager.scrollToPositionWithOffset(chatMsgAdapter.getItemCount() - 1, Integer.MIN_VALUE); chat_rv.setLayoutManager(linearLayoutManager); chat_rv.setAdapter(chatMsgAdapter); }// final String UserID = "23";// final String UserSig = "eJyrVgrxCdYrSy1SslIy0jNQ0gHzM1NS80oy0zIhwsZQ0eKU7MSCgswUJStDEwMDE0NTSyNDiExqRUFmUSpQ3NTU1MjAwAAiWpKZCxazNLcwNjYyMIWakpkONNTPzyTcLCzdsDLC0zTbMrHMPy870NXJUdvI3NwrMt20vMKl0q3KzNy1IKPYVqkWABdFLy0_"; final String UserID = "6"; final String UserSig = "eJwtzMEKgkAUheF3mXXIvZNjjdAigowINw5ES80xL6bdJikhevdkdHm*A-9XmFMWvK0TsZABiIXfVNqup4o8RzO*yiZnplLEGAKEqLTE6bEDk7OjK6UkAEzaU*tNr9YaQhXNFbqNzaLmrm6eW5a7xKTn9oL35IBH-Wh56VLcZ6bI2djhip*N*P0BGg4wBA__"; / * 登录这里的id和sig分别为userid和用userID加密后生成的sign(服务端提供) * 这里的userid可以替换为自己的userid * usersign可以使用开发者后台提供的生成工具生成测试(生成工具地址:https://console.cloud.tencent.com/im-detail/tool-usersig) * 具体正式usersign需要服务端生成后返回 */ public void login() { Log.i("腾讯云即时通信IM", "登录的账号为:" + 23); Log.i("腾讯云即时通信IM", "登录的密码为:" + "eJyrVgrxCdYrSy1SslIy0jNQ0gHzM1NS80oy0zIhwsZQ0eKU7MSCgswUJStDEwMDE0NTSyNDiExqRUFmUSpQ3NTU1MjAwAAiWpKZCxazNLcwNjYyMIWakpkONNTPzyTcLCzdsDLC0zTbMrHMPy870NXJUdvI3NwrMt20vMKl0q3KzNy1IKPYVqkWABdFLy0_"); Log.i("腾讯云即时通信IM", "登录的账号为:" + 6); Log.i("腾讯云即时通信IM", "登录的密码为:" + "eJwtzMEKgkAUheF3mXXIvZNjjdAigowINw5ES80xL6bdJikhevdkdHm*A-9XmFMWvK0TsZABiIXfVNqup4o8RzO*yiZnplLEGAKEqLTE6bEDk7OjK6UkAEzaU*tNr9YaQhXNFbqNzaLmrm6eW5a7xKTn9oL35IBH-Wh56VLcZ6bI2djhip*N*P0BGg4wBA__"); IMManager.login(UserID, UserSig, new V2TIMCallback() { @Override public void onError(int i, String s) { Log.i("腾讯云即时通信IM", "登录失败,错误码为" + i + ",原因为:" + s); } @Override public void onSuccess() { Log.i("腾讯云即时通信IM", "登录成功,即將加入房间"); V2TIMUserFullInfo v2TIMUserFullInfo = new V2TIMUserFullInfo(); v2TIMUserFullInfo.setNickname(UserID.equals("23") ? "独孤求败" : "宇宙中的飞猪"); V2TIMManager.getInstance().setSelfInfo(v2TIMUserFullInfo, new V2TIMCallback(){@Overridepublic void onError(int i, String s) { //修改失败 Log.i("腾讯云即时通信IM", "修改失败");}@Overridepublic void onSuccess() { //修改成功 Log.i("腾讯云即时通信IM", "修改成功");} }); joinRoom(); } }); } / * 加入一个房间 */ private void joinRoom() { Log.i("腾讯云即时通信IM", "你即将加入的房间号为:" + ROOMID); IMManager.joinToLiveRoom(ROOMID, ROOMNAME, new V2TIMCallback() { @Override public void onError(int i, String s) { Log.i("腾讯云即时通信IM", "加入房间失败。错误码为:" + i + ",错误信息为:" + s); } @Override public void onSuccess() { Log.i("腾讯云即时通信IM", "加入房间成功"); } }); } / * 发送一个消息 * @param msg */ private void sendMessage(final String msg) { IMManager.sendTextMsg(msg, ROOMID, V2TIMMessage.V2TIM_PRIORITY_NORMAL, new V2TIMValueCallback() { @Override public void onError(int i, String s) { Log.i("腾讯云即时通信IM", "发送失败"); } @Override public void onSuccess(V2TIMMessage v2TIMMessage) { Log.i("腾讯云即时通信IM", "发送成功"); addMessage(UserID.equals("23") ? "独孤求败" : "宇宙中的飞猪", msg); } }); } / * 添加消息进rv并刷新数据显示 * @param nickName * @param msg */ public void addMessage(String nickName, String msg) { lists.add(new ChatMsgBean(nickName, msg)); //让最后一个消息顶上来 chat_rv.scrollToPosition(chatMsgAdapter.getItemCount() - 1); chatMsgAdapter.notifyDataSetChanged(); } @Override public void onPause() { super.onPause(); //退出房间号 IMManager.exitLiveRoom(ROOMID); //销毁SDK IMManager.unInitIMSDk(); }}
注:自此功能完成。
其实整个功能不难,个人觉得开发者后台的文档有点乱,所以导致有时候理不清楚,故记录一下,顺便帮助一下需要使用的大佬,如果问题还请指出。
以上就是所有的代码
附上demo源码。
源码:源码请点这里
q:486789970
email:mr.cai_cai@foxmail.com
如果有什么问题,欢迎大家指导。并相互联系,希望能够通过文章互相学习。
---财财亲笔