最近兩天一直在查找集成環信SDK的博客與文章,找來找去,最新的集成過程詳解也是環信官方SDK更新前的,大部分都是SDK3.4.1之前的,剛纔測試環信SDK3.5.1測試成功後就來寫篇文章記錄下,在這裏先感謝下這位大神的博客,附上連接,我是按照他寫的博客一步一步測試成功的:https://blog.csdn.net/JerryWu145/article/details/52507451?locationNum=14 在基礎上自己親手測試了一遍,發現自己容易遺漏小問題,不多說了,按照管理,先上圖,我測試的username一個是test,我直接用demo重新創建新的帳號teste2來測試,另一個客戶端用的環信官方的demo演示在物理機,上圖像,以下的測試demo和環信官方demo使用的是同一個APPLEY,
接下來開始根據環信的官方文檔集成環信最新版SDK,這裏只集成簡單的即時聊天,沒有添加語音及視頻通話,
1,註冊及創建應用,主要是獲取到APPKEY,這裏選的是開發註冊哦,這裏直接看官方就可以了,這裏附上連接
http://docs.easemob.com/im/000quickstart/10register
2,下載官方最新的SDK:http://www.easemob.com/download/im
3,手動導入jar包和引用jar包,下面是SDK3.5.1下載完畢後的文件目錄,其中lib.av是包含語音和視頻通話的,libs.lite是不包括語音和視頻通話,在這裏的測試demo裏直接使用lite裏面的文件即可,lite文件夾裏的jar包直接複製到android studio項目中的lib文件夾裏,複製成功後對jar包右鍵---add as library,這裏不懂的直接百度搜android studio導入jar包,大把的圖文解說
4,jar包導入完畢後在main文件目錄下創建文件夾jniLibs,然後把上圖lite文件夾裏的arm64到x86這些文件全部複製到jniLibs文件夾裏,jar包和so文件準備完畢了,還有其他方式也可以,在這裏不贅述,
5,配置清單文件,直接copy官網的,在這裏本文的測試demo的copy過來,裏面的一個權限
MOUNT_UNMOUNT_FILESYSTEMS引入後報錯,我在這裏直接Alt+Enter給解決了,有看到的大神知道什麼原因給解釋下唄
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.pietyhr.servicetest"
android:versionCode="100"
android:versionName="1.0.0">
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<!-- 懸浮窗權限 -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<application
android:name=".MyApp"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".LoginActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
//主界面
<activity android:name=".MainActivity"/>
//聊天界面
<activity android:name=".ChatActivity"/>
<!-- 設置環信應用的AppKey -->
<meta-data android:name="EASEMOB_APPKEY" android:value="1179181024146289#sevicetest" />
<!-- 聲明sdk所需的service SDK核心功能-->
<service android:name="com.hyphenate.chat.EMChatService"
android:exported="true"/>
<service
android:name="com.hyphenate.chat.EMJobService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="true"
/>
<!-- 聲明sdk所需的receiver -->
<receiver android:name="com.hyphenate.chat.EMMonitorReceiver">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REMOVED"/>
<data android:scheme="package"/>
</intent-filter>
<!-- 可選filter -->
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.USER_PRESENT" />
</intent-filter>
</receiver>
</application>
</manifest>
6,在 ProGuard 文件中加入以下 keep。
-keep class com.hyphenate.** {*;}
-dontwarn com.hyphenate.**
7,在application裏初始化環信SDK,在這裏的setRequireServerAck方法已過時,在這裏直接註釋掉了,可忽略,
這裏加紅注意點!!!!
application類寫好後記得在清單文件裏application節點裏添加 android:name=".MyApp",這裏忽略的話,項目會出現“Unknown Source”這樣的錯誤,就是因爲沒有給環信SDK初始化引起的,
【at com.hyphenate.chat.EMClient.login(Unknown Source)】
// 設置是否需要服務器收到消息確認 // options.setRequireServerAck(true);
package com.pietyhr.servicetest;
import android.app.ActivityManager;
import android.app.Application;
import android.content.Context;
import android.content.pm.PackageManager;
import com.hyphenate.chat.EMClient;
import com.hyphenate.chat.EMOptions;
import java.util.Iterator;
import java.util.List;
/**
* @author Lee
* @time 2018年10月26號
*/
public class MyApp extends Application {
// 上下文菜單
private Context mContext;
// 記錄是否已經初始化
private boolean isInit = false;
@Override
public void onCreate() {
super.onCreate();
mContext = this;
// 初始化環信SDK
initEasemob();
}
/**
*
*/
private void initEasemob() {
// 獲取當前進程 id 並取得進程名
int pid = android.os.Process.myPid();
String processAppName = getAppName(pid);
/**
* 如果app啓用了遠程的service,此application:onCreate會被調用2次
* 爲了防止環信SDK被初始化2次,加此判斷會保證SDK被初始化1次
* 默認的app會在以包名爲默認的process name下運行,如果查到的process name不是app的process name就立即返回
*/
if (processAppName == null || !processAppName.equalsIgnoreCase(mContext.getPackageName())) {
// 則此application的onCreate 是被service 調用的,直接返回
return;
}
if (isInit) {
return;
}
/**
* SDK初始化的一些配置
* 關於 EMOptions 可以參考官方的 API 文檔
* http://www.easemob.com/apidoc/android/chat3.0/classcom_1_1hyphenate_1_1chat_1_1_e_m_options.html
*/
EMOptions options = new EMOptions();
// 設置Appkey,如果配置文件已經配置,這裏可以不用設置
// options.setAppKey("guaju");
// 設置自動登錄
options.setAutoLogin(true);
// 設置是否需要發送已讀回執
options.setRequireAck(true);
// 設置是否需要發送回執,TODO 這個暫時有bug,上層收不到發送回執
options.setRequireDeliveryAck(true);
// 設置是否需要服務器收到消息確認
// options.setRequireServerAck(true);
options.getRequireDeliveryAck();
// 收到好友申請是否自動同意,如果是自動同意就不會收到好友請求的回調,因爲sdk會自動處理,默認爲true
options.setAcceptInvitationAlways(false);
// 設置是否自動接收加羣邀請,如果設置了當收到羣邀請會自動同意加入
options.setAutoAcceptGroupInvitation(false);
// 設置(主動或被動)退出羣組時,是否刪除羣聊聊天記錄
options.setDeleteMessagesAsExitGroup(false);
// 設置是否允許聊天室的Owner 離開並刪除聊天室的會話
options.allowChatroomOwnerLeave(true);
// 調用初始化方法初始化sdk
EMClient.getInstance().init(mContext, options);
// 設置開啓debug模式
EMClient.getInstance().setDebugMode(true);
// 設置初始化已經完成
isInit = true;
}
/**
* 根據Pid獲取當前進程的名字,一般就是當前app的包名
*
* @param pID 進程的id
* @return 返回進程的名字
*/
private String getAppName(int pID) {
String processName = null;
ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
List l = am.getRunningAppProcesses();
Iterator i = l.iterator();
PackageManager pm = this.getPackageManager();
while (i.hasNext()) {
ActivityManager.RunningAppProcessInfo info = (ActivityManager.RunningAppProcessInfo) (i.next());
try {
if (info.pid == pID) {
processName = info.processName;
return processName;
}
} catch (Exception e) {
// Log.d("Process", "Error>> :"+ e.toString());
}
}
return processName;
}
}
8,然後就是登錄註冊界面LoginActivity,主界面MainActivity,以及聊天界面ChatActivity,以及三個xml佈局源碼,
LoginActivity及XML佈局文件
package com.pietyhr.servicetest;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.hyphenate.EMCallBack;
import com.hyphenate.EMError;
import com.hyphenate.chat.EMClient;
import com.hyphenate.exceptions.HyphenateException;
/**
* @author Lee
*/
public class LoginActivity extends Activity {
// 彈出框
private ProgressDialog mDialog;
// username 輸入框
private EditText mUsernameEdit;
// 密碼輸入框
private EditText mPasswordEdit;
// 註冊按鈕
private Button mSignUpBtn;
// 登錄按鈕
private Button mSignInBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
initView();
}
/**
* 初始化界面控件
*/
private void initView() {
mUsernameEdit = (EditText) findViewById(R.id.ec_edit_username);
mPasswordEdit = (EditText) findViewById(R.id.ec_edit_password);
mSignUpBtn = (Button) findViewById(R.id.ec_btn_sign_up);
mSignUpBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
signUp();
}
});
mSignInBtn = (Button) findViewById(R.id.ec_btn_sign_in);
mSignInBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
signIn();
}
});
}
/**
* 註冊方法
*/
private void signUp() {
// 註冊是耗時過程,所以要顯示一個dialog來提示下用戶
mDialog = new ProgressDialog(this);
mDialog.setMessage("註冊中,請稍後...");
mDialog.show();
new Thread(new Runnable() {
@Override
public void run() {
try {
String username = mUsernameEdit.getText().toString().trim();
String password = mPasswordEdit.getText().toString().trim();
EMClient.getInstance().createAccount(username, password);
runOnUiThread(new Runnable() {
@Override
public void run() {
if (!LoginActivity.this.isFinishing()) {
mDialog.dismiss();
}
Toast.makeText(LoginActivity.this, "註冊成功", Toast.LENGTH_LONG).show();
}
});
} catch (final HyphenateException e) {
e.printStackTrace();
runOnUiThread(new Runnable() {
@Override
public void run() {
if (!LoginActivity.this.isFinishing()) {
mDialog.dismiss();
}
/**
* 關於錯誤碼可以參考官方api詳細說明
* http://www.easemob.com/apidoc/android/chat3.0/classcom_1_1hyphenate_1_1_e_m_error.html
*/
int errorCode = e.getErrorCode();
String message = e.getMessage();
Log.d("lzan13", String.format("sign up - errorCode:%d, errorMsg:%s", errorCode, e.getMessage()));
switch (errorCode) {
// 網絡錯誤
case EMError.NETWORK_ERROR:
Toast.makeText(LoginActivity.this, "網絡錯誤 code: " + errorCode + ", message:" + message, Toast.LENGTH_LONG).show();
break;
// 用戶已存在
case EMError.USER_ALREADY_EXIST:
Toast.makeText(LoginActivity.this, "用戶已存在 code: " + errorCode + ", message:" + message, Toast.LENGTH_LONG).show();
break;
// 參數不合法,一般情況是username 使用了uuid導致,不能使用uuid註冊
case EMError.USER_ILLEGAL_ARGUMENT:
Toast.makeText(LoginActivity.this, "參數不合法,一般情況是username 使用了uuid導致,不能使用uuid註冊 code: " + errorCode + ", message:" + message, Toast.LENGTH_LONG).show();
break;
// 服務器未知錯誤
case EMError.SERVER_UNKNOWN_ERROR:
Toast.makeText(LoginActivity.this, "服務器未知錯誤 code: " + errorCode + ", message:" + message, Toast.LENGTH_LONG).show();
break;
case EMError.USER_REG_FAILED:
Toast.makeText(LoginActivity.this, "賬戶註冊失敗 code: " + errorCode + ", message:" + message, Toast.LENGTH_LONG).show();
break;
default:
Toast.makeText(LoginActivity.this, "ml_sign_up_failed code: " + errorCode + ", message:" + message, Toast.LENGTH_LONG).show();
break;
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
/**
* 登錄方法
*/
private void signIn() {
mDialog = new ProgressDialog(this);
mDialog.setMessage("正在登陸,請稍後...");
mDialog.show();
String username = mUsernameEdit.getText().toString().trim();
String password = mPasswordEdit.getText().toString().trim();
EMClient.getInstance().login(username, password, new EMCallBack() {
/**
* 登陸成功的回調
*/
@Override
public void onSuccess() {
runOnUiThread(new Runnable() {
@Override
public void run() {
mDialog.dismiss();
// 加載所有會話到內存
EMClient.getInstance().chatManager().loadAllConversations();
// 加載所有羣組到內存,如果使用了羣組的話
// EMClient.getInstance().groupManager().loadAllGroups();
// 登錄成功跳轉界面
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
startActivity(intent);
finish();
}
});
}
/**
* 登陸錯誤的回調
* @param i
* @param s
*/
@Override
public void onError(final int i, final String s) {
runOnUiThread(new Runnable() {
@Override
public void run() {
mDialog.dismiss();
Log.d("lzan13", "登錄失敗 Error code:" + i + ", message:" + s);
/**
* 關於錯誤碼可以參考官方api詳細說明
* http://www.easemob.com/apidoc/android/chat3.0/classcom_1_1hyphenate_1_1_e_m_error.html
*/
switch (i) {
// 網絡異常 2
case EMError.NETWORK_ERROR:
Toast.makeText(LoginActivity.this, "網絡錯誤 code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
break;
// 無效的用戶名 101
case EMError.INVALID_USER_NAME:
Toast.makeText(LoginActivity.this, "無效的用戶名 code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
break;
// 無效的密碼 102
case EMError.INVALID_PASSWORD:
Toast.makeText(LoginActivity.this, "無效的密碼 code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
break;
// 用戶認證失敗,用戶名或密碼錯誤 202
case EMError.USER_AUTHENTICATION_FAILED:
Toast.makeText(LoginActivity.this, "用戶認證失敗,用戶名或密碼錯誤 code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
break;
// 用戶不存在 204
case EMError.USER_NOT_FOUND:
Toast.makeText(LoginActivity.this, "用戶不存在 code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
break;
// 無法訪問到服務器 300
case EMError.SERVER_NOT_REACHABLE:
Toast.makeText(LoginActivity.this, "無法訪問到服務器 code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
break;
// 等待服務器響應超時 301
case EMError.SERVER_TIMEOUT:
Toast.makeText(LoginActivity.this, "等待服務器響應超時 code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
break;
// 服務器繁忙 302
case EMError.SERVER_BUSY:
Toast.makeText(LoginActivity.this, "服務器繁忙 code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
break;
// 未知 Server 異常 303 一般斷網會出現這個錯誤
case EMError.SERVER_UNKNOWN_ERROR:
Toast.makeText(LoginActivity.this, "未知的服務器異常 code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
break;
default:
Toast.makeText(LoginActivity.this, "ml_sign_in_failed code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
break;
}
}
});
}
@Override
public void onProgress(int i, String s) {
}
});
}
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="@+id/ec_edit_username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="username"/>
<EditText
android:id="@+id/ec_edit_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="password"/>
<Button
android:id="@+id/ec_btn_sign_up"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="註冊"/>
<Button
android:id="@+id/ec_btn_sign_in"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="登錄"/>
</LinearLayout>
</RelativeLayout>
MainActivity和XML佈局文件
package com.pietyhr.servicetest;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.hyphenate.EMCallBack;
import com.hyphenate.chat.EMClient;
public class MainActivity extends AppCompatActivity {
// 發起聊天 username 輸入框
private EditText mChatIdEdit;
// 發起聊天
private Button mStartChatBtn;
// 退出登錄
private Button mSignOutBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 判斷sdk是否登錄成功過,並沒有退出和被踢,否則跳轉到登陸界面
if (!EMClient.getInstance().isLoggedInBefore()) {
startActivity(new Intent(MainActivity.this, LoginActivity.class));
finish();
return;
}
setContentView(R.layout.activity_main);
initView();
}
/**
* 初始化界面
*/
private void initView() {
mChatIdEdit = (EditText) findViewById(R.id.ec_edit_chat_id);
mStartChatBtn = (Button) findViewById(R.id.ec_btn_start_chat);
mStartChatBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 獲取我們發起聊天的者的username
String chatId = mChatIdEdit.getText().toString().trim();
if (!TextUtils.isEmpty(chatId)) {
// 獲取當前登錄用戶的 username
String currUsername = EMClient.getInstance().getCurrentUser();
if (chatId.equals(currUsername)) {
Toast.makeText(MainActivity.this, "不能和自己聊天", Toast.LENGTH_SHORT).show();
return;
}
// 跳轉到聊天界面,開始聊天
Intent intent = new Intent(MainActivity.this, ChatActivity.class);
intent.putExtra("ec_chat_id", chatId);
startActivity(intent);
} else {
Toast.makeText(MainActivity.this, "Username 不能爲空", Toast.LENGTH_LONG).show();
}
}
});
mSignOutBtn = (Button) findViewById(R.id.ec_btn_sign_out);
mSignOutBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
signOut();
}
});
}
/**
* 退出登錄
*/
private void signOut() {
// 調用sdk的退出登錄方法,第一個參數表示是否解綁推送的token,沒有使用推送或者被踢都要傳false
EMClient.getInstance().logout(false, new EMCallBack() {
@Override
public void onSuccess() {
Log.i("lzan13", "logout success");
// 調用退出成功,結束app
finish();
}
@Override
public void onError(int i, String s) {
Log.i("lzan13", "logout error " + i + " - " + s);
}
@Override
public void onProgress(int i, String s) {
}
});
}
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="@+id/ec_edit_chat_id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="對方的username"/>
<Button
android:id="@+id/ec_btn_start_chat"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="發起聊天"/>
<Button
android:id="@+id/ec_btn_sign_out"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="退出登錄"/>
</LinearLayout>
</RelativeLayout>
ChatActivity和XML佈局文件:
package com.pietyhr.servicetest;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.hyphenate.EMCallBack;
import com.hyphenate.EMMessageListener;
import com.hyphenate.chat.EMClient;
import com.hyphenate.chat.EMCmdMessageBody;
import com.hyphenate.chat.EMConversation;
import com.hyphenate.chat.EMMessage;
import com.hyphenate.chat.EMTextMessageBody;
import java.util.List;
/**
* @author Lee
* @time $date$ $time$
* @describe $param$
*/
public class ChatActivity extends AppCompatActivity implements EMMessageListener {
// 聊天信息輸入框
private EditText mInputEdit;
// 發送按鈕
private Button mSendBtn;
// 顯示內容的 TextView
private TextView mContentText;
// 消息監聽器
private EMMessageListener mMessageListener;
// 當前聊天的 ID
private String mChatId;
// 當前會話對象
private EMConversation mConversation;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
// 獲取當前會話的username(如果是羣聊就是羣id)
mChatId = getIntent().getStringExtra("ec_chat_id");
mMessageListener = this;
initView();
initConversation();
}
/**
* 初始化界面
*/
private void initView() {
mInputEdit = (EditText) findViewById(R.id.ec_edit_message_input);
mSendBtn = (Button) findViewById(R.id.ec_btn_send);
mContentText = (TextView) findViewById(R.id.ec_text_content);
// 設置textview可滾動,需配合xml佈局設置
mContentText.setMovementMethod(new ScrollingMovementMethod());
// 設置發送按鈕的點擊事件
mSendBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String content = mInputEdit.getText().toString().trim();
if (!TextUtils.isEmpty(content)) {
mInputEdit.setText("");
// 創建一條新消息,第一個參數爲消息內容,第二個爲接受者username
EMMessage message = EMMessage.createTxtSendMessage(content, mChatId);
// 將新的消息內容和時間加入到下邊
mContentText.setText(mContentText.getText() + "\n" + content + " -> " + message.getMsgTime());
// 調用發送消息的方法
EMClient.getInstance().chatManager().sendMessage(message);
// 爲消息設置回調
message.setMessageStatusCallback(new EMCallBack() {
@Override
public void onSuccess() {
// 消息發送成功,打印下日誌,正常操作應該去刷新ui
Log.i("lzan13", "send message on success");
}
@Override
public void onError(int i, String s) {
// 消息發送失敗,打印下失敗的信息,正常操作應該去刷新ui
Log.i("lzan13", "send message on error " + i + " - " + s);
}
@Override
public void onProgress(int i, String s) {
// 消息發送進度,一般只有在發送圖片和文件等消息纔會有回調,txt不回調
}
});
}
}
});
}
/**
* 初始化會話對象,並且根據需要加載更多消息
*/
private void initConversation() {
/**
* 初始化會話對象,這裏有三個參數麼,
* 第一個表示會話的當前聊天的 useranme 或者 groupid
* 第二個是繪畫類型可以爲空
* 第三個表示如果會話不存在是否創建
*/
mConversation = EMClient.getInstance().chatManager().getConversation(mChatId, null, true);
// 設置當前會話未讀數爲 0
mConversation.markAllMessagesAsRead();
int count = mConversation.getAllMessages().size();
if (count < mConversation.getAllMsgCount() && count < 20) {
// 獲取已經在列表中的最上邊的一條消息id
String msgId = mConversation.getAllMessages().get(0).getMsgId();
// 分頁加載更多消息,需要傳遞已經加載的消息的最上邊一條消息的id,以及需要加載的消息的條數
mConversation.loadMoreMsgFromDB(msgId, 20 - count);
}
// 打開聊天界面獲取最後一條消息內容並顯示
if (mConversation.getAllMessages().size() > 0) {
EMMessage messge = mConversation.getLastMessage();
EMTextMessageBody body = (EMTextMessageBody) messge.getBody();
// 將消息內容和時間顯示出來
mContentText.setText(body.getMessage() + " - " + mConversation.getLastMessage().getMsgTime());
}
}
/**
* 自定義實現Handler,主要用於刷新UI操作
*/
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
EMMessage message = (EMMessage) msg.obj;
// 這裏只是簡單的demo,也只是測試文字消息的收發,所以直接將body轉爲EMTextMessageBody去獲取內容
EMTextMessageBody body = (EMTextMessageBody) message.getBody();
// 將新的消息內容和時間加入到下邊
mContentText.setText(mContentText.getText() + "\n" + body.getMessage() + " <- " + message.getMsgTime());
break;
}
}
};
@Override
protected void onResume() {
super.onResume();
// 添加消息監聽
EMClient.getInstance().chatManager().addMessageListener(mMessageListener);
}
@Override
protected void onStop() {
super.onStop();
// 移除消息監聽
EMClient.getInstance().chatManager().removeMessageListener(mMessageListener);
}
/**
* --------------------------------- Message Listener -------------------------------------
* 環信消息監聽主要方法
*/
/**
* 收到新消息
*
* @param list 收到的新消息集合
*/
@Override
public void onMessageReceived(List<EMMessage> list) {
// 循環遍歷當前收到的消息
for (EMMessage message : list) {
if (message.getFrom().equals(mChatId)) {
// 設置消息爲已讀
mConversation.markMessageAsRead(message.getMsgId());
// 因爲消息監聽回調這裏是非ui線程,所以要用handler去更新ui
Message msg = mHandler.obtainMessage();
msg.what = 0;
msg.obj = message;
mHandler.sendMessage(msg);
} else {
// 如果消息不是當前會話的消息發送通知欄通知
}
}
}
/**
* 收到新的 CMD 消息
*
* @param list
*/
@Override
public void onCmdMessageReceived(List<EMMessage> list) {
for (int i = 0; i < list.size(); i++) {
// 透傳消息
EMMessage cmdMessage = list.get(i);
EMCmdMessageBody body = (EMCmdMessageBody) cmdMessage.getBody();
Log.i("lzan13", body.action());
}
}
@Override
public void onMessageRead(List<EMMessage> list) {
}
@Override
public void onMessageDelivered(List<EMMessage> list) {
}
@Override
public void onMessageRecalled(List<EMMessage> list) {
}
/**
* 消息的狀態改變
*
* @param message 發生改變的消息
* @param object 包含改變的消息
*/
@Override
public void onMessageChanged(EMMessage message, Object object) {
}
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<!--輸入框-->
<RelativeLayout
android:id="@+id/ec_layout_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true">
<Button
android:id="@+id/ec_btn_send"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:text="Send"/>
<EditText
android:id="@+id/ec_edit_message_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_toLeftOf="@id/ec_btn_send"/>
</RelativeLayout>
<!--展示消息內容-->
<TextView
android:id="@+id/ec_text_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@id/ec_layout_input"
android:maxLines="15"
android:scrollbars="vertical"/>
</RelativeLayout>
清單文件記得註冊好這些Activity,全部源碼都在這裏了,
這裏再把源碼附上吧,基本上前面這幾部完成,後面就可以測試了,
https://download.csdn.net/download/lgd19901215/10745573
注意點:測試demo書寫時在清單文件裏的APPKEY和環信官方demo快速體驗裏要一致哦,或者多註冊幾個帳號也可以,