1,在Application中初始化,要记得manifest也要相应的配置声明一下Application
package demo.com.hximdemo;
import android.app.ActivityManager;
import android.app.Application;
import android.content.pm.PackageManager;
import android.util.Log;
import com.hyphenate.chat.EMClient;
import com.hyphenate.chat.EMOptions;
import java.util.Iterator;
import java.util.List;
/**
* Created by Alv_chi on 2016/11/21.
*/
public class MyApp extends Application {
private static final String TAG = "easeMob";
@Override
public void onCreate() {
super.onCreate();
initialEaseMob();
}
private void initialEaseMob() {
EMOptions options = new EMOptions();
// 默认添加好友时,是不需要验证的,改成需要验证
options.setAcceptInvitationAlways(false);
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(this.getPackageName())) {
Log.e(TAG, "enter the service process!");
// 则此application::onCreate 是被service 调用的,直接返回
return;
}
//初始化
EMClient.getInstance().init(this, options);
//在做打包混淆时,关闭debug模式,避免消耗不必要的资源
EMClient.getInstance().setDebugMode(true);
}
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;
}
}
2,登陆注册页面以及对应的布局:
package demo.com.hximdemo;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import com.hyphenate.EMCallBack;
import com.hyphenate.EMConnectionListener;
import com.hyphenate.EMError;
import com.hyphenate.chat.EMClient;
import com.hyphenate.exceptions.HyphenateException;
import com.hyphenate.util.NetUtils;
import butterknife.Bind;
import butterknife.ButterKnife;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "easeMob";
@Bind(R.id.etAccount)
EditText etAccount;
@Bind(R.id.etPassword)
EditText etPassword;
private String account = null;
private String password = null;
private SharedPreferences userInfo;
private boolean isLogin = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
initialUserInfo();
//注册一个监听连接状态的listener
EMClient.getInstance().addConnectionListener(new MyConnectionListener());
}
private void initialUserInfo() {
userInfo = getSharedPreferences("userInfo", MODE_PRIVATE);
account = userInfo.getString("userName", "");
password = userInfo.getString("password", "");
etAccount.setText(account);
etPassword.setText(password);
}
//实现ConnectionListener接口
private class MyConnectionListener implements EMConnectionListener {
@Override
public void onConnected() {
}
@Override
public void onDisconnected(final int error) {
runOnUiThread(new Runnable() {
@Override
public void run() {
if (error == EMError.USER_REMOVED) {
// 显示帐号已经被移除
Log.e(TAG, "run: 显示帐号已经被服务器移除");
Toast.makeText(MainActivity.this, "显示帐号已经被服务器移除", Toast.LENGTH_SHORT).show();
} else if (error == EMError.USER_LOGIN_ANOTHER_DEVICE) {
// 显示帐号在其他设备登录
Log.e(TAG, "run: 显示帐号在其他设备登录");
Toast.makeText(MainActivity.this, "显示帐号在其他设备登录", Toast.LENGTH_SHORT).show();
} else {
if (NetUtils.hasNetwork(MainActivity.this)) {
//连接不到聊天服务器
Log.e(TAG, "run: 连接不到聊天服务器");
Toast.makeText(MainActivity.this, "连接不到聊天服务器", Toast.LENGTH_SHORT).show();
} else {
//当前网络不可用,请检查网络设置
Log.e(TAG, "run: 当前网络不可用,请检查网络设置");
Toast.makeText(MainActivity.this, "当前网络不可用,请检查网络设置", Toast.LENGTH_SHORT).show();
}
}
}
});
}
}
public void signIn(View view) {
if (checkInfo()) return;
new Thread(new Runnable() {
@Override
public void run() {
try {
//注册失败会抛出HyphenateException
// 因为是网路请求,所以要运行在Thread中:
EMClient.getInstance().createAccount(account, password);//同步方法
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "注册成功", Toast.LENGTH_SHORT).show();
initialAlertDialog();
}
});
} catch (final HyphenateException e) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "注册失败HyphenateException=" + e.getMessage(), Toast.LENGTH_SHORT).show();
}
});
e.printStackTrace();
Log.e(TAG, "signIn: HyphenateException=" + e.getMessage());
}
}
}).start();
}
private boolean checkInfo() {
account = etAccount.getText().toString();
password = etPassword.getText().toString();
account = account.trim();
password = password.trim();
if (account.equals("") || password.equals("")) {
Toast.makeText(MainActivity.this, "账号或密码输入有误", Toast.LENGTH_SHORT).show();
return true;
}
saveUserInfo();
return false;
}
private void saveUserInfo() {
SharedPreferences.Editor editor = userInfo.edit();
editor
.clear()
.putString("userName", account)
.putString("password", password)
.commit();
}
private void initialAlertDialog() {
AlertDialog alertDialog = new AlertDialog.Builder(MainActivity.this)
.setTitle("温馨提示")
.setCancelable(true)
.setMessage("账号已注册成功,需要马上登录吗?")
.setNegativeButton("NO", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
})
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
loginAccount();
dialog.dismiss();
}
})
.create();
alertDialog.show();
}
public void logIn(View view) {
loginAccount();
}
private void loginAccount() {
if (checkInfo()) return;
EMClient.getInstance().login(account, password, new EMCallBack() {//回调
@Override
public void onSuccess() {
EMClient.getInstance().groupManager().loadAllGroups();
EMClient.getInstance().chatManager().loadAllConversations();
Log.d("main", "登录聊天服务器成功!");
isLogin = true;
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "登录聊天服务器成功!", Toast.LENGTH_SHORT).show();
Intent intent = new Intent(MainActivity.this, ChatMateActivity.class);
startActivity(intent);
}
});
}
@Override
public void onProgress(int progress, String status) {
}
@Override
public void onError(int code, String message) {
Log.d("main", "登录聊天服务器失败!");
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "登录聊天服务器失败!", Toast.LENGTH_SHORT).show();
}
});
}
});
}
public void logOut(View view) {
if (!isLogin) {
Toast.makeText(MainActivity.this, "你还没登陆昵...", Toast.LENGTH_SHORT).show();
return;
}
EMClient.getInstance().logout(true, new EMCallBack() {
@Override
public void onSuccess() {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "登出成功", Toast.LENGTH_SHORT).show();
}
});
isLogin = false;
}
@Override
public void onProgress(int progress, String status) {
}
@Override
public void onError(int code, String message) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "登出失败", Toast.LENGTH_SHORT).show();
}
});
}
});
}
}
对应的布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="45dp"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2"
android:gravity="center"
android:text="账号" />
<EditText
android:id="@+id/etAccount"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="8"
android:hint="请输入电话号码作为登陆账号"
android:paddingLeft="10dp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="45dp"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2"
android:gravity="center"
android:text="密码" />
<EditText
android:id="@+id/etPassword"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="8"
android:hint="请输入1-16个数字和字母组合密码"
android:paddingLeft="10dp" />
</LinearLayout>
<Button
android:layout_width="match_parent"
android:layout_height="45dp"
android:layout_marginTop="10dp"
android:onClick="signIn"
android:text="注册" />
<Button
android:layout_width="match_parent"
android:layout_height="45dp"
android:layout_marginTop="10dp"
android:onClick="logIn"
android:text="登陆" />
<Button
android:layout_width="match_parent"
android:layout_height="45dp"
android:layout_marginTop="10dp"
android:onClick="logOut"
android:text="取消登录" />
</LinearLayout>
3,确定你要聊天的对方的账号,因为我这里只是一个简单的demo,所以这样做,实际开发自己按照实际需求做:
该页面的ativity以及对应页面:
package demo.com.hximdemo;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import butterknife.Bind;
import butterknife.ButterKnife;
public class ChatMateActivity extends AppCompatActivity {
@Bind(R.id.etChatMateAccount)
EditText etChatMateAccount;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat_mate);
ButterKnife.bind(this);
}
public void startChatWithInputedUser(View view) {
String id = etChatMateAccount.getText().toString();
id = id.trim();
if (id.equals("")) {
Toast.makeText(ChatMateActivity.this, "请输入正确的userId", Toast.LENGTH_SHORT).show();
return;
}
toChatActivity(id);
}
private void toChatActivity(String id) {
Intent intent = new Intent(this, ChatActivity.class);
intent.putExtra("chatMateId", id);
startActivity(intent);
}
public void back2LastPosition(View view) {
this.finish();
}
}
其对应的布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical"
tools:context="demo.com.hximdemo.ChatMateActivity">
<EditText
android:id="@+id/etChatMateAccount"
android:layout_width="match_parent"
android:layout_height="45dp"
android:hint="请输入想要聊天的用户的账号" />
<Button
android:layout_width="match_parent"
android:layout_height="45dp"
android:layout_marginTop="10dp"
android:onClick="startChatWithInputedUser"
android:text="开聊"
/>
<Button
android:layout_width="match_parent"
android:layout_height="45dp"
android:layout_marginTop="10dp"
android:onClick="back2LastPosition"
android:text="突然情绪泛滥,不想聊了..."
/>
</LinearLayout>
4,聊天Activity以及对应布局:
package demo.com.hximdemo;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import com.hyphenate.EMCallBack;
import com.hyphenate.EMMessageListener;
import com.hyphenate.chat.EMClient;
import com.hyphenate.chat.EMMessage;
import com.hyphenate.chat.EMTextMessageBody;
import java.util.List;
import butterknife.Bind;
import butterknife.ButterKnife;
import demo.com.hximdemo.utils.ThreadUtil;
public class ChatActivity extends AppCompatActivity {
private static final String TAG = "easeMob";
@Bind(R.id.llMsgContainer)
LinearLayout llMsgContainer;
@Bind(R.id.etMsg)
EditText etMsg;
@Bind(R.id.sv)
ScrollView sv;
private String chatMateId;
private EMMessageListener msgListener;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
ButterKnife.bind(this);
getChatMateId();
receiveMsg();
}
private void receiveMsg() {
msgListener = new EMMessageListener() {
@Override
public void onMessageReceived(List<EMMessage> messages) {
//收到消息
for (EMMessage message : messages) {
String s = ((EMTextMessageBody) message.getBody()).getMessage();
showMsgOnScreen("他说:"+s);
}
}
@Override
public void onCmdMessageReceived(List<EMMessage> messages) {
//收到透传消息
}
@Override
public void onMessageReadAckReceived(List<EMMessage> messages) {
//收到已读回执
}
@Override
public void onMessageDeliveryAckReceived(List<EMMessage> message) {
//收到已送达回执
}
@Override
public void onMessageChanged(EMMessage message, Object change) {
//消息状态变动
}
};
EMClient.getInstance().chatManager().addMessageListener(msgListener);
}
private void getChatMateId() {
Intent intent = getIntent();
chatMateId = intent.getStringExtra("chatMateId");
}
public void sendMsg(View view) {
final String msg = etMsg.getText().toString();
if (msg.equals("")) {
Toast.makeText(ChatActivity.this, "不能发空消息", Toast.LENGTH_SHORT).show();
return;
}
ThreadUtil.executeThreadTask(new Runnable() {
@Override
public void run() {
sendText(msg);
}
});
etMsg.setText("");
}
private void sendText(final String msg) {
//创建一条文本消息,content为消息文字内容,toChatUsername为对方用户或者群聊的id,后文皆是如此
EMMessage message = EMMessage.createTxtSendMessage(msg, chatMateId);
//如果是群聊,设置chattype,默认是单聊
message.setChatType(EMMessage.ChatType.Chat);
// if (chatType == EMMessage.ChatType.GroupChat)
// {
// message.setChatType(EMMessage.ChatType.GroupChat);
// }
//发送消息
EMClient.getInstance().chatManager().sendMessage(message);
// 监听发送信息的回调:
message.setMessageStatusCallback(new EMCallBack() {
@Override
public void onSuccess() {
Log.e(TAG, "onSuccess: Msg发送成功");
showMsgOnScreen("我说:"+msg);
}
@Override
public void onError(int i, String s) {
Log.e(TAG, "onSuccess: Msg发送失败");
}
@Override
public void onProgress(int i, String s) {
}
});
}
private void showMsgOnScreen(final String msg) {
runOnUiThread(new Runnable() {
@Override
public void run() {
TextView textView = new TextView(ChatActivity.this);
textView.setText(msg);
textView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT
, LinearLayout.LayoutParams.WRAP_CONTENT));
llMsgContainer.addView(textView);
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
@Override
public void run() {
sv.fullScroll(ScrollView.FOCUS_DOWN);//使ScrollView自动滚动到其底部;
}
}, 200);
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
// 记得在不需要的时候移除listener,如在activity的onDestroy()时
EMClient.getInstance().chatManager().removeMessageListener(msgListener);
}
}
其对应的布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical"
tools:context="demo.com.hximdemo.ChatActivity">
<ScrollView
android:id="@+id/sv"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<LinearLayout
android:id="@+id/llMsgContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
</ScrollView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@color/gray"
android:orientation="horizontal">
<EditText
android:id="@+id/etMsg"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="7" />
<Button
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2"
android:onClick="sendMsg"
android:text="发送" />
</LinearLayout>
</LinearLayout>
5,用到的工具类:
package demo.com.hximdemo.utils;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Created by Alv_chi on 2016/11/21.
*/
public class ThreadUtil {
private static ExecutorService executorService;
public static void executeThreadTask(Runnable r) {
if (executorService == null) {
executorService = Executors.newFixedThreadPool(15);
}
executorService.submit(r);
}
}
7,源码下载地址: 点击打开链接下载源码