QQ群 : 668524118
这里写的比较简单 ,
如有不懂的地方 ,欢迎加群询问 .
1)首先下载融云即时通讯的SDK
导入融云的通讯界面库,以及通讯功能库
IMKit,IMLib
2)在build.gradle中的dependencies中添加
compile project(':IMKit') compile project(':IMLib')
然后syon now,就Ok
在mainfest中添加
authorities中换成 自己的包名. FileProvider
下边activity的name换成自己的,这是在连接融云的服务器(建议写在登录的时候)
<!-- 融云 --> <provider android:name="android.support.v4.content.FileProvider" android:authorities="cm.hetao.yingyue.FileProvider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/rc_file_path" /> </provider> <!-- 登录 --> <activity android:name=".activity.LoginActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".activity.MainActivity" /> <!-- 会话列表 --> <activity android:name=".activity.MessageActivity" android:screenOrientation="portrait" android:windowSoftInputMode="stateHidden|adjustResize"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <data android:host="cm.hetao.yingyue" android:pathPrefix="/conversationlist" android:scheme="rong" /> </intent-filter> </activity> <!-- 会话界面 --> <activity android:name=".activity.ChatInterfaceActivity" android:screenOrientation="portrait" android:windowSoftInputMode="stateHidden|adjustResize"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <data android:host="cm.hetao.yingyue" android:pathPrefix="/conversation/" android:scheme="rong" /> </intent-filter> </activity>3) 创建融云账号,创建自己的应用,在 api 调试中 获取 Token
将获取到的 token 放进去,去连接融云的服务器
这些可以写在登录界面
/** * 连接融云服务器 * * @param token */ public void connectRongServer(String token) { RongIM.connect(token, new RongIMClient.ConnectCallback() { @Override public void onSuccess(final String userId) { Log.e("error", "成功"); // RongIM.getInstance().enableNewComingMessageIcon(true);//显示新消息提醒 RongIM.getInstance().enableUnreadMessageIcon(true);//显示未读消息数目 RongIM.setUserInfoProvider(new RongIM.UserInfoProvider() { @Override public io.rong.imlib.model.UserInfo getUserInfo(String s) { return findUserInfo(s); } }, true); } @Override public void onError(RongIMClient.ErrorCode errorCode) { Log.e("erroy", "connect failure errorCode is :" + errorCode.getValue()); } @Override public void onTokenIncorrect() { Log.e("erroy", "token is error , please check token and appkey "); } }); } /** * 根据user_id去查找用户信息 * * @param user_id 发送消息的用户id */ private io.rong.imlib.model.UserInfo findUserInfo(String user_id) { if (user_id.equals("sys")) { return new io.rong.imlib.model.UserInfo(user_id, "系统消息", Uri.parse(MyApplication.SERVICE_IMAGE)); } String resVlt = String.format(Constants.CHAT_USER_DETAIL, Integer.valueOf(user_id)); HttpHelper.getInstance().get(MyApplication.getTokenURL(resVlt), null, new HttpHelper.XCallBack() { @Override public void onResponse(String result) { try { JSONObject object = new JSONObject(result); JSONObject data = object.getJSONObject("data"); friend = new Friend(data.getString("id"), data.getString("name"), data.getString("head_img")); } catch (Exception e) { Log.e("HttpHelper", e.toString()); } } }); if (friend != null) { return new io.rong.imlib.model.UserInfo(friend.getId(), friend.getName(), Uri.parse(MyApplication.IMAGE_HOST + friend.getHead_img())); } else { return null; } } private void getRyService() { RongIM.setConnectionStatusListener(new RongIMClient.ConnectionStatusListener() { @Override public void onChanged(RongIMClient.ConnectionStatusListener.ConnectionStatus status) { if (ConnectionStatus.KICKED_OFFLINE_BY_OTHER_CLIENT == status) { Log.e("error", "被顶号"); } Log.e("error", "网络状态已经变化"); } }); // 声明消息监听器,建议在 Application 中声明为成员变量。 RongIM.setOnReceiveMessageListener(new RongIMClient.OnReceiveMessageListener() { @Override public boolean onReceived(final Message message, int arg1) { return false; } }); }
因为融云是不会保存用户的信息数据的,所以你需要自己去保存用户的信息数据.
我是创建了一个实体类来保存的
/** * 融云用户信息 */ public class Friend implements Serializable { private String id; private String name; private String head_img; public Friend(String id, String name, String head_img) { this.id = id; this.name = name; this.head_img = head_img; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getHead_img() { return head_img; } public void setHead_img(String head_img) { this.head_img = head_img; } }
刷新用户数据
private void refreshUserInfoCache(UserInfo user) { //刷新用户数据 io.rong.imlib.model.UserInfo userInfo = new io.rong.imlib.model.UserInfo(user.getId() + "", user.getName(), Uri.parse(user.getMember().getHead_img())); RongIM.getInstance().refreshUserInfoCache(userInfo); }
融云的推送服务信息配置
<!-- 融云推送服务 --> <receiver- android:name=".receive.SealNotificationReceive" android:exported="true"> <intent-filter> <action android:name="io.rong.push.intent.MESSAGE_ARRIVED" /> <action android:name="io.rong.push.intent.MI_MESSAGE_ARRIVED" /> <action android:name="io.rong.push.intent.MESSAGE_CLICKED" /> <action android:name="io.rong.push.intent.MI_MESSAGE_CLICKED" /> </intent-filter> </receiver-> <!-- 必选: SDK 核心功能 --> <!-- 第三方相关,向第三方推送服务请求 token 的服务 --> <service android:name="io.rong.push.core.PushRegistrationService" android:exported="false" /> <!-- 处理 push 消息相关的服务 --> <service android:name="io.rong.push.core.MessageHandleService" android:exported="true" /> <!-- push服务 --> <service android:name="io.rong.push.PushService" android:exported="true" android:process="io.rong.push"> <!-- push进程,可以改名 --> </service> <!-- push 相关事件接收器 --> <receiver android:name="io.rong.push.PushReceiver" android:process="io.rong.push"> <!-- 此处进程可以改名,名称需要和PushService所在进程统一 --> <!-- 心跳事件 --> <intent-filter> <action android:name="io.rong.push.intent.action.HEART_BEAT" /> </intent-filter> <!-- 网络变动事件 --> <intent-filter> <action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> </intent-filter> <!-- 部分用户事件 --> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.USER_PRESENT" /> <action android:name="android.intent.action.ACTION_POWER_CONNECTED" /> <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" /> </intent-filter> </receiver>
会话列表页面布局( conversationlist是固定的 )
<FrameLayout android:id="@+id/conversationlist" android:layout_width="match_parent" android:layout_height="match_parent" />会话列表页面具体实现( 本人用的是xutils3框架 )
/** * 消息 */ @ContentView(R.layout.activity_message) public class MessageActivity extends BaseActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setStyle(STYLE_HOME); setCaption("消息"); //设置消息体内是否携带用户信息。 RongIM.getInstance().setMessageAttachedUserInfo(true); } @Override protected void onResume() { IsNeedLoginActivity(); initMesageNumber(); initPlugin(); ConversationListFragment fragment = new ConversationListFragment(); Uri uri = Uri.parse("rong://" + getApplicationInfo().packageName).buildUpon() .appendPath("conversationlist") .appendQueryParameter(Conversation.ConversationType.PRIVATE.getName(), "false") //设置私聊会话非聚合显示 .appendQueryParameter(Conversation.ConversationType.GROUP.getName(), "true")//设置群组会话聚合显示 .appendQueryParameter(Conversation.ConversationType.DISCUSSION.getName(), "false")//设置讨论组会话非聚合显示 .appendQueryParameter(Conversation.ConversationType.SYSTEM.getName(), "false")//设置系统会话非聚合显示 .build(); fragment.setUri(uri); FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); //rong_content 为你要加载的 id transaction.add(R.id.conversationlist, fragment); transaction.commit(); super.onResume(); } /** * 初始化Plugin图标 */ private void initPlugin() { List<IExtensionModule> moduleList = RongExtensionManager.getInstance().getExtensionModules(); IExtensionModule defaultModule = null; if (moduleList != null) { for (IExtensionModule module : moduleList) { if (module instanceof DefaultExtensionModule) { defaultModule = module; break; } } if (defaultModule != null) { RongExtensionManager.getInstance().unregisterExtensionModule(defaultModule); RongExtensionManager.getInstance().registerExtensionModule(new SampleExtensionModule()); } } } private class SampleExtensionModule extends DefaultExtensionModule { private EditText mEditText; @Override public List<IPluginModule> getPluginModules(Conversation.ConversationType conversationType) { // super.getPluginModules(conversationType); 如果需要对红包进行排序可从父类中的 getPluginModules 集合中过滤取出 JrmfExtensionModule List<IPluginModule> pluginModuleList = new ArrayList<>(); pluginModuleList.add(new BaiDuLocationPlugin()); pluginModuleList.add(new ImagePlugin()); pluginModuleList.add(new FilePlugin()); // 此时扩展区域的展示顺序应该为 : 百度地图、图片、文件 return pluginModuleList; } @Override public void onAttachedToExtension(RongExtension extension) { mEditText = extension.getInputEditText(); } @Override public void onDetachedFromExtension() { mEditText = null; } @Override public List<IEmoticonTab> getEmoticonTabs() { return super.getEmoticonTabs(); } } }
通过在AndroidManifest文件中的配置来进行隐式调用( name一样是固定的 )
<fragment android:id="@+id/conversation" android:name="io.rong.imkit.fragment.ConversationFragment" android:layout_width="match_parent" android:layout_height="match_parent" />
会话界面具体实现逻辑( 我这里添加了因为项目需求添加了一些自定义消息 )
/** * 聊天界面 */ @ContentView(R.layout.activity_chat_interface) public class ChatInterfaceActivity extends BaseActivity { @ViewInject(R.id.conversation) private Fragment conversation; private String targetid = ""; //用户id private OrderITextMsgMessage orderITextMsgMessage; //邀单消息 @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Override protected void onResume() { initPlugin(); initData(); super.onResume(); } private void initData() { switch (SharedPreferencesUtils.getIntPreferences(SharedPreferencesUtils.CONFIG_USER_TYPE)) { case 2: //达人 String resvlt = String.format(Constants.TALENT_TALENTID_PROFILE, MyApplication.getUserId()); HttpHelper.getInstance().get(MyApplication.getTokenURL(resvlt), null, this, new onTalentProflieXCallBack()); break; case 3: //会员 HttpHelper.getInstance().get(MyApplication.getTokenURL(Constants.MEMBER_PROFILE), null, this, new onMemberProflieXCallBack()); break; } } /** * 初始化Plugin图标 */ private void initPlugin() { List<IExtensionModule> moduleList = RongExtensionManager.getInstance().getExtensionModules(); IExtensionModule defaultModule = null; if (moduleList != null) { for (IExtensionModule module : moduleList) { if (module instanceof DefaultExtensionModule) { defaultModule = module; break; } } if (defaultModule != null) { RongExtensionManager.getInstance().unregisterExtensionModule(defaultModule); RongExtensionManager.getInstance().registerExtensionModule(new SampleExtensionModule()); } } } /** * 初始化会话界面的监听事件 */ private void initChatListener() { //设置消息体内是否携带用户信息。 RongIM.getInstance().setMessageAttachedUserInfo(true); setStyle(STYLE_BACK_QUERY); targetid = getIntent().getData().getQueryParameter("targetId");//获取id if (String.valueOf(targetid).equals(String.valueOf("sys"))) { setCaption(getIntent().getData().getQueryParameter("title")); //获取消息title } else { setCaption(getIntent().getData().getQueryParameter("title")); //获取消息title } setTitleBarImageResource(R.drawable.yy_head_yaoqing).setOnTitleBarRightImgListener(new onTitleBarRightImgListener() { @Override public void onTitleBarRightImgListener() { Intent intent = new Intent(); try { intent.putExtra("id", Integer.valueOf(targetid)); if (MyApplication.getUserId() == Integer.valueOf(targetid)) { return; } } catch (Exception e) { return; } intent.putExtra("is_chat", "chatinterface"); openActivity(intent, TalentDetailActivity.class); } }); /** * 消息页面的 消息 头像等监听事件 */ RongIM.setConversationBehaviorListener(new RongIM.ConversationBehaviorListener() { @Override public boolean onUserPortraitClick(Context context, Conversation.ConversationType conversationType, io.rong.imlib.model.UserInfo userInfo) { //消息头像点击事件 return false; } @Override public boolean onUserPortraitLongClick(Context context, Conversation.ConversationType conversationType, io.rong.imlib.model.UserInfo userInfo) { //消息头像长按事件 return false; } @Override public boolean onMessageClick(Context context, View view, Message message) { Intent intent = new Intent(); //点击消息处理事件, message.getContent() 获得消息内容 if (message.getContent() instanceof LocationMessage) { //位置消息 LocationMessage locationMessage = (LocationMessage) message.getContent(); intent.putExtra("lng", locationMessage.getLng()); intent.putExtra("lat", locationMessage.getLat()); openActivity(intent, BaiDuLocationDetailActivity.class); } if (message.getContent() instanceof ImageMessage) { //图片消息 ImageMessage imageMessage = (ImageMessage) message.getContent(); intent = new Intent(); intent.putExtra("photo", imageMessage.getLocalUri() == null ? imageMessage.getRemoteUri().toString() : imageMessage.getLocalUri().toString()); if (imageMessage.getThumUri() != null) { intent.putExtra("thumbnail", imageMessage.getThumUri()); } openActivity(intent, PhotoDetailActivity.class); } if (message.getContent() instanceof UserTextMsgMessage) { //用户的审核消息 UserTextMsgMessage userTextMsgMessage = (UserTextMsgMessage) message.getContent(); intent = new Intent(); } if (message.getContent() instanceof OrderTextMsgMessage) { //订单消息 OrderTextMsgMessage orderTextMsgMessage = (OrderTextMsgMessage) message.getContent(); intent = new Intent(); intent.putExtra("status", orderTextMsgMessage.getStatus()); intent.putExtra("id", orderTextMsgMessage.getId()); openActivity(intent, OrderTextMsgDetailActivity.class); } if (message.getContent() instanceof OrderITextMsgMessage) { //邀单消息 orderITextMsgMessage = (OrderITextMsgMessage) message.getContent(); String resvlt = String.format(Constants.ORDER_INVITE_DETAIL, orderITextMsgMessage.getId()); HttpHelper.getInstance().get(MyApplication.getTokenURL(resvlt), null, ChatInterfaceActivity.this, new onOrderInviteDetailXCallBack()); } return true; } @Override public boolean onMessageLinkClick(Context context, String s) { //链接消息点击事件 return false; } @Override public boolean onMessageLongClick(Context context, View view, Message message) { //消息长按事件 return false; } }); } private class SampleExtensionModule extends DefaultExtensionModule { private EditText mEditText; @Override public List<IPluginModule> getPluginModules(Conversation.ConversationType conversationType) { // super.getPluginModules(conversationType); 如果需要对红包进行排序可从父类中的 getPluginModules 集合中过滤取出 JrmfExtensionModule List<IPluginModule> pluginModuleList = new ArrayList<>(); pluginModuleList.add(new BaiDuLocationPlugin()); pluginModuleList.add(new ImagePlugin()); pluginModuleList.add(new FilePlugin()); // 此时扩展区域的展示顺序应该为 : 百度地图、图片、文件 return pluginModuleList; } @Override public void onAttachedToExtension(RongExtension extension) { mEditText = extension.getInputEditText(); } @Override public void onDetachedFromExtension() { mEditText = null; } @Override public List<IEmoticonTab> getEmoticonTabs() { return super.getEmoticonTabs(); } } /** * 达人详情 */ private class onTalentProflieXCallBack implements HttpHelper.XCallBack { @Override public void onResponse(String result) { TalentDetailInfo talentDetailInfo = null; try { talentDetailInfo = getHttpResult(result, TalentDetailInfo.class); } catch (Exception e) { showToastText(e.toString()); } if (talentDetailInfo != null) { RongIM.getInstance().refreshUserInfoCache(new io.rong.imlib.model.UserInfo(String.valueOf(talentDetailInfo.getTalent().getId()), talentDetailInfo.getTalent().getName(), Uri.parse(MyApplication.IMAGE_HOST + talentDetailInfo.getTalent().getHead_img()))); initChatListener(); } } } /** * 会员详情 */ private class onMemberProflieXCallBack implements HttpHelper.XCallBack { @Override public void onResponse(String result) { UserInfo user = null; try { user = getHttpResult(result, UserInfo.class); user.setId(user.getUser()); } catch (Exception e) { Placard.showInfo(e.toString()); } if (user != null) { RongIM.getInstance().refreshUserInfoCache(new io.rong.imlib.model.UserInfo(String.valueOf(user.getUser()), user.getName(), Uri.parse(MyApplication.IMAGE_HOST + user.getMember().getHead_img()))); initChatListener(); } } } /** * 邀单详情 */ private class onOrderInviteDetailXCallBack implements HttpHelper.XCallBack { @Override public void onResponse(String result) { OrderDetailInfo orderDetail = null; try { orderDetail = getHttpResult(result, OrderDetailInfo.class); } catch (Exception e) { showToastText(e.toString()); } if (orderDetail != null) { Intent intent = new Intent(); intent.putExtra("id", orderITextMsgMessage.getId()); intent.putExtra("status", orderITextMsgMessage.getStatus()); switch (orderITextMsgMessage.getStatus()) { case 0: if (orderDetail.getUser().getId() == MyApplication.getUserId()) { intent.putExtra("viewtype", "aboutmaster"); if (orderITextMsgMessage.getId() != 0) { intent.putExtra("invite_order_id", orderITextMsgMessage.getId()); } openActivity(intent, SearchListActivity.class); } else { openActivity(intent, OrderITextMsgDetailActivity.class); } break; case 1: openActivity(intent, OrderITextMsgDetailActivity.class); break; case 2: openActivity(intent, OrderITextMsgDetailActivity.class); break; case 3: openActivity(intent, OrderITextMsgDetailActivity.class); break; } } } } }
融云的推送服务( 默认即可 )
/** * 广播接收器 */ public class SealNotificationReceive extends PushMessageReceiver { @Override public boolean onNotificationMessageArrived(Context context, PushNotificationMessage message) { return false; // 返回 false, 会弹出融云 SDK 默认通知; 返回 true, 融云 SDK 不会弹通知, 通知需要由您自定义。 } @Override public boolean onNotificationMessageClicked(Context context, PushNotificationMessage message) { return false; // 返回 false, 会走融云 SDK 默认处理逻辑, 即点击该通知会打开会话列表或会话界面; 返回 true, 则由您自定义处理逻辑。 } }
自定义消息类( 构造函数是通过ipc通道传输的 ,虽然没被调用 ,但是不可删除 )
/** * 自定义消息类 * 用户的审核状态(达人审核,实名认证审核) */ @MessageTag(value = "User:TxtMsg", flag = MessageTag.ISCOUNTED | MessageTag.ISPERSISTED) public class UserTextMsgMessage extends MessageContent { private String content;//消息属性,可随意定义 /** * 构造函数。 * * @param data 初始化传入的二进制数据。 */ public UserTextMsgMessage(byte[] data) { String jsonStr = null; try { jsonStr = new String(data, "UTF-8"); Log.e("rongyun_user_message", jsonStr); } catch (UnsupportedEncodingException e1) { Placard.showInfo(e1.toString()); } try { JSONObject jsonObj = new JSONObject(jsonStr); if (jsonObj.has("content")) Log.e("content :", jsonObj.optString("content")); setContent(jsonObj.optString("content")); } catch (JSONException e) { Log.e("rongyun_JSONException", e.getMessage()); } } /** * 构造函数。 * * @param in 初始化传入的 Parcel。 */ public UserTextMsgMessage(Parcel in) { setContent(ParcelUtils.readFromParcel(in));//该类为工具类,消息属性 } /** * 读取接口,目的是要从Parcel中构造一个实现了Parcelable的类的实例处理。 */ public static final Creator<UserTextMsgMessage> CREATOR = new Creator<UserTextMsgMessage>() { @Override public UserTextMsgMessage createFromParcel(Parcel source) { return new UserTextMsgMessage(source); } @Override public UserTextMsgMessage[] newArray(int size) { return new UserTextMsgMessage[size]; } }; /** * 构建一个文字消息实例。 * * @return 文字消息实例。 */ public static TextMessage obtain(String text) { return new TextMessage(text); } /** * 描述了包含在 Parcelable 对象排列信息中的特殊对象的类型。 * * @return 一个标志位,表明Parcelable对象特殊对象类型集合的排列。 */ public int describeContents() { return 0; } /** * 将类的数据写入外部提供的 Parcel 中。 * * @param dest 对象被写入的 Parcel。 * @param flags 对象如何被写入的附加标志。 */ @Override public void writeToParcel(Parcel dest, int flags) { //这里可继续增加你消息的属性 ParcelUtils.writeToParcel(dest, getContent()); } /* * * 实现 encode() 方法,该方法的功能是将消息属性封装成 json 串, * 再将 json 串转成 byte 数组,该方法会在发消息时调用,如下面示例代码: * */ @Override public byte[] encode() { JSONObject jsonObj = new JSONObject(); try { jsonObj.put("content", this.getContent()); } catch (JSONException e) { Log.e("JSONException", e.getMessage()); } try { return jsonObj.toString().getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return null; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } }
自定义消息模板
/** * 自定义消息模板 * <p> * desc新建一个消息类继承 IContainerItemProvider.MessageProvider 类,实现对应接口方法, * 注意开头的注解! * 注意泛型! * </p> */ @ProviderTag(messageContent = UserTextMsgMessage.class, showPortrait = false, centerInHorizontal = true, showSummaryWithName = false) public class UserTextMsgMessageItemProvider extends IContainerItemProvider.MessageProvider<UserTextMsgMessage> { /** * //根据需求,适配数据 * * @param view * @param i * @param userTextMsgMessage * @param uiMessage */ @Override public void bindView(View view, int i, UserTextMsgMessage userTextMsgMessage, UIMessage uiMessage) { //根据需求,适配数据 ViewHolder holder = (ViewHolder) view.getTag(); // if (uiMessage.getMessageDirection() == Message.MessageDirection.SEND) {//消息方向,自己发送的 // holder.user_content.setBackgroundResource(io.rong.imkit.R.drawable.rc_ic_bubble_right); // } else { // holder.user_content.setBackgroundResource(io.rong.imkit.R.drawable.rc_ic_bubble_left); // } holder.user_content.setText(userTextMsgMessage.getContent()); } @Override public Spannable getContentSummary(UserTextMsgMessage userTextMsgMessage) { return new SpannableString(userTextMsgMessage.getContent()); } @Override public void onItemClick(View view, int i, UserTextMsgMessage userTextMsgMessage, UIMessage uiMessage) { } /** * 展示在会话界面的自定义的消息的布局 * * @param context * @param viewGroup * @return */ @Override public View newView(Context context, ViewGroup viewGroup) { // 这就是展示在会话界面的自定义的消息的布局 View view = LayoutInflater.from(context).inflate(R.layout.user_message, null); ViewHolder holder = new ViewHolder(); holder.user_content = (TextView) view.findViewById(R.id.user_content); view.setTag(holder); return view; } private static class ViewHolder { TextView user_content; } }
闲聊群 : 668524118
这里写的比较简单 ,
如有不懂的地方 ,欢迎加群询问 .