本人菜鸟一枚,自学不才,在Android开发圈子也是摸打滚爬,3,4年,之前没有写博客的习惯,只是自己总结一些,没有分享。当然好多还是参考的大神们分享的东西,去github fork 别人的代码。。
话不多说,开干!
【1】SDK的集成套路,注册登录SDK官网点击打开链接,下载SDK,看集成文档。
【2】特别值得一提的是,融云的SDK,模块化很好,下载SDK时,可以先择需要集成的模块,见下图:
IMKit SDK是融云做好的UI库,java代码封装成了jar,布局文件暴露,可以自己修改颜色样式。IMKit Module依赖IMLibModule。
【3】导入AndroidStudio时,将IMKit及IMLIb都以Module的方式导入。
然后app项目添加依赖module,只添加IMKit,即可。app的dependencies如下:
【4】在IMLib的AndroidManifest.xml中将RONG_CLOUD_APP_KEY修改为自己在官网创建项目生成的appkey。
(集成过SDK的都明白这一步!!!不赘述)
【5】在自己项目的AndroidManifest.xml文件中添加FileProvider
- <provider
- android:name=“android.support.v4.content.FileProvider”
- android:authorities=“com.bj.rongyundemo.FileProvider”
- android:exported=“false”
- android:grantUriPermissions=“true”>
- <meta-data
- android:name=“android.support.FILE_PROVIDER_PATHS”
- android:resource=“@xml/rc_file_path” />
- </provider>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.bj.rongyundemo.FileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/rc_file_path" />
</provider>
需要注意的是authorities的值修改为"自己的包名.FileProvider"。至此融云的配置基本完成,可以进行开发了。
【6】融云初始化,在自己的Application中定义。代码如下:
- public class App extends Application {
- @Override
- public void onCreate() {
- super.onCreate();
- if (getApplicationInfo().packageName.equals(getCurProcessName(getApplicationContext())) ||
- “io.rong.push”.equals(getCurProcessName(getApplicationContext()))) {
- /**
- * IMKit SDK调用第一步 初始化
- */
- RongIM.init(this);
- }
- }
- /**
- * 获得当前进程的名字
- *
- * @param context
- * @return 进程号
- */
- public static String getCurProcessName(Context context) {
- int pid = android.os.Process.myPid();
- ActivityManager activityManager = (ActivityManager) context
- .getSystemService(Context.ACTIVITY_SERVICE);
- for (ActivityManager.RunningAppProcessInfo appProcess : activityManager
- .getRunningAppProcesses()) {
- if (appProcess.pid == pid) {
- return appProcess.processName;
- }
- }
- return null;
- }
- }
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
if (getApplicationInfo().packageName.equals(getCurProcessName(getApplicationContext())) ||
"io.rong.push".equals(getCurProcessName(getApplicationContext()))) {
/**
* IMKit SDK调用第一步 初始化
*/
RongIM.init(this);
}
}
/**
* 获得当前进程的名字
*
* @param context
* @return 进程号
*/
public static String getCurProcessName(Context context) {
int pid = android.os.Process.myPid();
ActivityManager activityManager = (ActivityManager) context
.getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningAppProcessInfo appProcess : activityManager
.getRunningAppProcesses()) {
if (appProcess.pid == pid) {
return appProcess.processName;
}
}
return null;
}
}
【7】我写了一个有底部导航栏的主页面,一个好友列表,一个消息列表。效果如下:
1----->>问友界面只有发消息的点击事件跟融云有关系,核心代码如下:
- holder.tv_send.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- // start chat acitivity
- if(RongIM.getInstance()!=null){
- RongIM.getInstance().startPrivateChat(mContext,”10010”,”联通”);
- }
- }
- });
holder.tv_send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// start chat acitivity
if(RongIM.getInstance()!=null){
RongIM.getInstance().startPrivateChat(mContext,"10010","联通");
}
}
});
startPrivateChart(a,b)是开启单聊的方式,参数a是聊天对方的id,参数b是聊天对方的昵称。
2---->开始聊天之前必须先连接融云服务器,连接融云服务器的方式是必须拿到该APP的登录帐号在融云的token。
这个token的获取需要通过ServerAPI来获取(此步需要APP服务端来做,省略)。这里我们通过融云的API调试,获取Token。
先讲一下如何官网进行API调试,如下,点击已创建的应用,点API调试,点获取Toekn,拉到最下面输入用户id即可。
获取到token之后,就可以进行连接融云服务器了。代码如下:
- /**
- * 建立与融云服务器的连接
- *
- * @param token
- */
- private void connect(String token) {
- if (getApplicationInfo().packageName.equals(App.getCurProcessName(getApplicationContext()))) {
- /**
- * IMKit SDK调用第二步,建立与服务器的连接
- */
- RongIM.connect(token, new RongIMClient.ConnectCallback() {
- /**
- * Token 错误,在线上环境下主要是因为 Token 已经过期,您需要向 App Server 重新请求一个新的 Token
- */
- @Override
- public void onTokenIncorrect() {
- Log.e(“tag”, ”–onTokenIncorrect”);
- }
- /**
- * 连接融云成功
- * @param userid 当前 token
- */
- @Override
- public void onSuccess(String userid) {
- Log.e(“tag”, ”–onSuccess” + userid);
- }
- /**
- * 连接融云失败
- * @param errorCode 错误码,可到官网 查看错误码对应的注释
- */
- @Override
- public void onError(RongIMClient.ErrorCode errorCode) {
- Log.e(“tag”, ”–onError” + errorCode);
- }
- });
- }
- }
/**
* 建立与融云服务器的连接
*
* @param token
*/
private void connect(String token) {
if (getApplicationInfo().packageName.equals(App.getCurProcessName(getApplicationContext()))) {
/**
* IMKit SDK调用第二步,建立与服务器的连接
*/
RongIM.connect(token, new RongIMClient.ConnectCallback() {
/**
* Token 错误,在线上环境下主要是因为 Token 已经过期,您需要向 App Server 重新请求一个新的 Token
*/
@Override
public void onTokenIncorrect() {
Log.e("tag", "--onTokenIncorrect");
}
/**
* 连接融云成功
* @param userid 当前 token
*/
@Override
public void onSuccess(String userid) {
Log.e("tag", "--onSuccess" + userid);
}
/**
* 连接融云失败
* @param errorCode 错误码,可到官网 查看错误码对应的注释
*/
@Override
public void onError(RongIMClient.ErrorCode errorCode) {
Log.e("tag", "--onError" + errorCode);
}
});
}
}
连接成功后,会返回用户的id。
3——–>>>>>>>>>>消息列表界面,使用IMKit提供的UI。初始化一个Fragment即可。如下:
- /**
- * 初始化会话列表
- * @return
- */
- private Fragment initConversationList() {
- if (frag_msg == null) {
- ConversationListFragment listFragment = new ConversationListFragment();
- Uri uri=Uri.parse(“rong://”+getApplicationInfo().packageName).buildUpon()
- .appendPath(“conversationlist”)
- .appendQueryParameter(Conversation.ConversationType.PRIVATE.getName(), “false”) //设置私聊会话是否聚合显示
- .appendQueryParameter(Conversation.ConversationType.GROUP.getName(), “false”)//群组
- .appendQueryParameter(Conversation.ConversationType.SYSTEM.getName(), “false”)//系统
- .appendQueryParameter(Conversation.ConversationType.DISCUSSION.getName(), “false”)
- .build();
- listFragment.setUri(uri);
- return listFragment;
- }else{
- return frag_msg;
- }
- }
/**
* 初始化会话列表
* @return
*/
private Fragment initConversationList() {
if (frag_msg == null) {
ConversationListFragment listFragment = new ConversationListFragment();
Uri uri=Uri.parse("rong://"+getApplicationInfo().packageName).buildUpon()
.appendPath("conversationlist")
.appendQueryParameter(Conversation.ConversationType.PRIVATE.getName(), "false") //设置私聊会话是否聚合显示
.appendQueryParameter(Conversation.ConversationType.GROUP.getName(), "false")//群组
.appendQueryParameter(Conversation.ConversationType.SYSTEM.getName(), "false")//系统
.appendQueryParameter(Conversation.ConversationType.DISCUSSION.getName(), "false")
.build();
listFragment.setUri(uri);
return listFragment;
}else{
return frag_msg;
}
}
【8】聊天界面编写,布局文件activity_chat.xml
- <?xml version=“1.0” encoding=“utf-8”?>
- <LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”
- xmlns:app=“http://schemas.android.com/apk/res-auto”
- android:layout_width=“match_parent”
- android:layout_height=“match_parent”
- android:orientation=“vertical”>
- <android.support.v7.widget.Toolbar
- android:id=“@+id/toolbar”
- android:layout_width=“match_parent”
- app:layout_collapseMode=“pin”
- android:layout_height=“?attr/actionBarSize”
- android:background=“#4ececf”
- app:contentInsetStart=“0dp”>
- <TextView
- android:id=“@+id/tv_title”
- android:layout_width=“wrap_content”
- android:layout_height=“wrap_content”
- android:layout_gravity=“center”
- android:text=“聊天”
- android:textColor=“@android:color/white”
- android:textSize=“20sp” />
- </android.support.v7.widget.Toolbar>
- <fragment
- android:id=“@+id/conversation”
- android:name=“io.rong.imkit.fragment.ConversationFragment”
- android:layout_width=“match_parent”
- android:layout_height=“match_parent” />
- </LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
app:layout_collapseMode="pin"
android:layout_height="?attr/actionBarSize"
android:background="#4ececf"
app:contentInsetStart="0dp">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="聊天"
android:textColor="@android:color/white"
android:textSize="20sp" />
</android.support.v7.widget.Toolbar>
<fragment
android:id="@+id/conversation"
android:name="io.rong.imkit.fragment.ConversationFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
ChatActivity.class如下:
- public class ChatActivity extends FragmentActivity {
- @Bind(R.id.tv_title)
- TextView tvTitle;
- @Bind(R.id.toolbar)
- Toolbar toolbar;
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_chat);
- ButterKnife.bind(this);
- //IM
- RongIM.getInstance().enableNewComingMessageIcon(true);//显示新消息提醒
- RongIM.getInstance().enableUnreadMessageIcon(true);//显示未读消息数目
- String targetId = getIntent().getData().getQueryParameter(“targetId”);
- String title = getIntent().getData().getQueryParameter(“title”);//必须实现内容提供者才能获取到title
- if(!TextUtils.isEmpty(title)){
- tvTitle.setText(title);
- }else{
- //根据id,去服务端请求用户信息,再设置
- }
- }
- }
public class ChatActivity extends FragmentActivity {
@Bind(R.id.tv_title)
TextView tvTitle;
@Bind(R.id.toolbar)
Toolbar toolbar;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
ButterKnife.bind(this);
//IM
RongIM.getInstance().enableNewComingMessageIcon(true);//显示新消息提醒
RongIM.getInstance().enableUnreadMessageIcon(true);//显示未读消息数目
String targetId = getIntent().getData().getQueryParameter("targetId");
String title = getIntent().getData().getQueryParameter("title");//必须实现内容提供者才能获取到title
if(!TextUtils.isEmpty(title)){
tvTitle.setText(title);
}else{
//根据id,去服务端请求用户信息,再设置
}
}
}
因为我们使用的是IMKit UI库的界面,只需要的在activity中包含一个conversationFragment即可。
android:name="io.rong.imkit.fragment.ConversationFragment"
RongIM.getInstance().enableNewComingMessageIcon(true);//显示新消息提醒 RongIM.getInstance().enableUnreadMessageIcon(true);//显示未读消息
这两行代码是是聊天界面的消息提示。
String targetId = getIntent().getData().getQueryParameter("targetId"); String title = getIntent().getData().getQueryParameter("title");//必须实现内容提供者才能获取到titletargetid和title是问友界面,发送消息的事件中,startPrivate()方法中传过来的值。
最后还有最关键的一步,在自己应用的AndroidManifest.xml中配置ChatActivity。
配置如下:
- <activity android:name=“.ChatActivity”
- 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=“com.bj.rongyundemo”
- android:pathPrefix=“/conversation/”
- android:scheme=“rong” />
- </intent-filter>
- </activity>
<activity android:name=".ChatActivity"
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="com.bj.rongyundemo"
android:pathPrefix="/conversation/"
android:scheme="rong" />
</intent-filter>
</activity>
host修改为自己的包名。至此已经能够正常收发消息了。
【9】消息推送,融云有自己的消息推送,当然也可以集成第三方推送,融云官方均有介绍。这里我们只做融云推送。
1---->>写WenwenNotificationReceiver
- public class WenwenNotificationReceiver extends PushMessageReceiver {
- @Override
- public boolean onNotificationMessageArrived(Context context, PushNotificationMessage message) {
- Log.e(“tag”,”——-message——”);
- return false; // 返回 false, 会弹出融云 SDK 默认通知; 返回 true, 融云 SDK 不会弹通知, 通知需要由您自定义。
- }
- @Override
- public boolean onNotificationMessageClicked(Context context, PushNotificationMessage message) {
- Log.e(“tag”,”——-message——”);
- return false; // 返回 false, 会走融云 SDK 默认处理逻辑, 即点击该通知会打开会话列表或会话界面; 返回 true, 则由您自定义处理逻辑。
- }
- }
public class WenwenNotificationReceiver extends PushMessageReceiver {
@Override
public boolean onNotificationMessageArrived(Context context, PushNotificationMessage message) {
Log.e("tag","-------message------");
return false; // 返回 false, 会弹出融云 SDK 默认通知; 返回 true, 融云 SDK 不会弹通知, 通知需要由您自定义。
}
@Override
public boolean onNotificationMessageClicked(Context context, PushNotificationMessage message) {
Log.e("tag","-------message------");
return false; // 返回 false, 会走融云 SDK 默认处理逻辑, 即点击该通知会打开会话列表或会话界面; 返回 true, 则由您自定义处理逻辑。
}
}
2------->>在自己的清单文件中配置WenwenNotificationReceiver :
- <receiver
- android:exported=“true”
- android:name=“.receiver.WenwenNotificationReceiver”>
- <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>
<receiver
android:exported="true"
android:name=".receiver.WenwenNotificationReceiver">
<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>
【10】消息数量的监听。在消息列表Fragment的底部tab,我们要显示收到未读消息的数量。这个需要的MainActivity中添加监听即可。
/
- **
- * 融云未读消息监听
- */
- private void initUnreadCountListener() {
- final Conversation.ConversationType[] conversationTypes = {Conversation.ConversationType.PRIVATE, Conversation.ConversationType.DISCUSSION,
- Conversation.ConversationType.GROUP, Conversation.ConversationType.SYSTEM,
- Conversation.ConversationType.PUBLIC_SERVICE};
- Handler handler = new Handler();
- handler.postDelayed(new Runnable() {
- @Override
- public void run() {
- RongIM.getInstance().setOnReceiveUnreadCountChangedListener(mCountListener, conversationTypes);
- }
- }, 500);
- }
- public RongIM.OnReceiveUnreadCountChangedListener mCountListener = new RongIM.OnReceiveUnreadCountChangedListener() {
- @Override
- public void onMessageIncreased(int count) {
- Log.e(“tag”,”count:” + count);
- if (count == 0) {
- tvTabBadge.setVisibility(View.GONE);
- } else if (count > 0 && count < 100) {
- tvTabBadge.setVisibility(View.VISIBLE);
- tvTabBadge.setText(count + ”“);
- } else {
- tvTabBadge.setVisibility(View.VISIBLE);
- tvTabBadge.setText(“99+”);
- }
- }
- };
**
* 融云未读消息监听
*/
private void initUnreadCountListener() {
final Conversation.ConversationType[] conversationTypes = {Conversation.ConversationType.PRIVATE, Conversation.ConversationType.DISCUSSION,
Conversation.ConversationType.GROUP, Conversation.ConversationType.SYSTEM,
Conversation.ConversationType.PUBLIC_SERVICE};
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
RongIM.getInstance().setOnReceiveUnreadCountChangedListener(mCountListener, conversationTypes);
}
}, 500);
}
public RongIM.OnReceiveUnreadCountChangedListener mCountListener = new RongIM.OnReceiveUnreadCountChangedListener() {
@Override
public void onMessageIncreased(int count) {
Log.e("tag","count:" + count);
if (count == 0) {
tvTabBadge.setVisibility(View.GONE);
} else if (count > 0 && count < 100) {
tvTabBadge.setVisibility(View.VISIBLE);
tvTabBadge.setText(count + "");
} else {
tvTabBadge.setVisibility(View.VISIBLE);
tvTabBadge.setText("99+");
}
}
};
onCreate()方法中调用即可。
【11】用户头像和昵称修改成用户自己的头像和昵称。两种方式。
第一种方式是内容提供者:
1-->在MainActivity的onCreate()方法中添加:
RongIM.setUserInfoProvider(this,true);2—->实现该方法
/** * 融云实现内容提供者,将用户信息提交给融云,融云在使用自己的SDK时,会加载自己的头像。昵称 * @param userId * @return */
- @Override
- public UserInfo getUserInfo(String userId) {
- for (Friend friend:friends){
- if(friend.getUserId().equals(userId)){
- return new UserInfo(friend.getUserId(),friend.getUserName(),Uri.parse(friend.getAvator()));
- }
- }
- return null;
- }
@Override
public UserInfo getUserInfo(String userId) {
for (Friend friend:friends){
if(friend.getUserId().equals(userId)){
return new UserInfo(friend.getUserId(),friend.getUserName(),Uri.parse(friend.getAvator()));
}
}
return null;
}
就是用户根据自己的id去获取用户昵称及头像,封装为融云的UserInfo即可。
第二种方式是在用户连接融云服务器成功后,执行如下代码:
- if(RongIM.getInstance()!=null){
- //1、设置当前用户信息
- RongIM.getInstance().setCurrentUserInfo(new UserInfo(userid,friends.get(0).getUserName(),Uri.parse(friends.get(0).getAvator())));
- //2
- RongIM.getInstance().setMessageAttachedUserInfo(true);
- }
if(RongIM.getInstance()!=null){
//1、设置当前用户信息
RongIM.getInstance().setCurrentUserInfo(new UserInfo(userid,friends.get(0).getUserName(),Uri.parse(friends.get(0).getAvator())));
//2
RongIM.getInstance().setMessageAttachedUserInfo(true);
}
//当A.b互通消息时,各端接收到消息后,SDK会自动从消息中取出用户信息放入用户信息缓存中,并刷新UI显示
设置当前的用户信息,UserInfo是融云定义好的实体。
-------------------至此,融云的单聊模式功能全部结束------------------------
接下来一段时间,会搞环信SDK,环信坑比较多。。。。