http://www.easemob.com/download/im
sourceSets{
main{
jniLibs.srcDir("jniLibs")
}
}
<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.ACCESS_MOCK_LOCATION" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<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" />
<meta-data android:name="EASEMOB_APPKEY" android:value="Your AppKey" />
<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"
/>
<receiver android:name="com.hyphenate.chat.EMMonitorReceiver">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REMOVED"/>
<data android:scheme="package"/>
intent-filter>
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.USER_PRESENT" />
intent-filter>
receiver>
EMOptions options = new EMOptions();
// 默认添加好友时,是不需要验证的,改成需要验证
options.setAcceptInvitationAlways(false);
//初始化
EMClient.getInstance().init(applicationContext, options);
//在做打包混淆时,关闭debug模式,避免消耗不必要的资源
EMClient.getInstance().setDebugMode(true);
注册模式分两种,开放注册和授权注册。只有开放注册时,才可以客户端注册。
开放注册是为了测试使用,正式环境中不推荐使用该方式注册环信账号;
授权注册的流程应该是您服务器通过环信提供的 REST API 注册,之后保存到您的服务器
或返回给客户端。
注册用户名会自动转为小写字母,所以建议用户名均以小写注册
//注册失败会抛出HyphenateException
EMClient.getInstance().createAccount(username, pwd);//同步方法
EMClient.getInstance().login(userName,password,new EMCallBack() {//回调
@Override
public void onSuccess() {
EMClient.getInstance().groupManager().loadAllGroups();
EMClient.getInstance().chatManager().loadAllConversations();
Log.d("main", "登录聊天服务器成功!");
}
@Override
public void onProgress(int progress, String status) {
}
@Override
public void onError(int code, String message) {
Log.d("main", "登录聊天服务器失败!");
}
});
EMClient.getInstance().chatManager().loadAllConversations();
EMClient.getInstance().groupManager().loadAllGroups();
另外如果登录过,APP 长期在后台再进的时候也可能会导致加载到内存的群组和会话为空,可
以在主页面的 oncreate 里也加上这两句代码,当然,更好的办法应该是放在程序的开屏页,
即首次登录成功后,不需要再次调用登录方法,在下次 APP 启动时,SDK 会自动为您登录。
并且如果您自动登录失败,也可以读取到之前的会话信息(以上情况是在未调用登出的情况下
实现的)。
SDK 中自动登录属性默认是 true 打开的,如果不需要自动登录,在初始化 SDK 初始化的时
候,调用options.setAutoLogin(false);设置为 false 关闭
用户调用了 SDK 的登出动作;
用户在别的设备上更改了密码,导致此设备上自动登录失败;
用户的账号被从服务器端删除;
用户从另一个设备登录,把当前设备上登录的用户踢出。
同步方法
EMClient.getInstance().logout(true);
异步方法
EMClient.getInstance().logout(true, new EMCallBack() {
@Override
public void onSuccess() {
// TODO Auto-generated method stub
}
@Override
public void onProgress(int progress, String status) {
// TODO Auto-generated method stub
}
@Override
public void onError(int code, String message) {
// TODO Auto-generated method stub
}
});
//注册一个监听连接状态的listener
EMClient.getInstance().addConnectionListener(new MyConnectionListener());
//实现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){
// 显示帐号已经被移除
}else if (error == EMError.USER_LOGIN_ANOTHER_DEVICE) {
// 显示帐号在其他设备登录
} else {
if (NetUtils.hasNetwork(MainActivity.this))
//连接不到聊天服务器
else
//当前网络不可用,请检查网络设置
}
}
});
}
}
http://docs.easemob.com/im/200androidclientintegration/50singlechat
http://docs.easemob.com/im/200androidclientintegration/60buddymgmt
public class MyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
EMOptions options = new EMOptions();
//在对方加你为好友时,需要验证吗?
options.setAcceptInvitationAlways(false);
//是否自动登录
options.setAutoLogin(false);
EMClient.getInstance().init(this, options);
}
}
public class MainActivity extends AppCompatActivity {
EditText et_username, et_password;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_username = (EditText) findViewById(R.id.et_username);
et_password = (EditText) findViewById(R.id.et_password);
}
public void login(View v) {
final String username = et_username.getText().toString();
final String password = et_password.getText().toString();
if (TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) {
showToast("用户名密码不能为空");
return;
}
EMClient.getInstance().login(username, password, new EMCallBack() {
@Override
public void onSuccess() {
EMClient.getInstance().groupManager().loadAllGroups();
EMClient.getInstance().chatManager().loadAllConversations();
showToast("登录成功");
startActivity(new Intent(getBaseContext(), HomeActivity.class).putExtra("username", username));
}
@Override
public void onError(int i, String s) {
showToast(s);
}
@Override
public void onProgress(int i, String s) {
Log.e("TAG", i + " >----" + s);
}
});
}
public void register(View v) {
//同步和异步
//同步方法是指需要等待方法执行完毕
//异步 开启线程去执行
final String username = et_username.getText().toString();
final String password = et_password.getText().toString();
if (TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) {
showToast("用户名密码不能为空");
return;
}
new Thread() {
@Override
public void run() {
super.run();
try {
//netwrok on main
EMClient.getInstance().createAccount(username, password);
//程序运行到此处 注册成功
showToast("注册成功!");
} catch (HyphenateException e) {
e.printStackTrace();
//注册失败
Log.e("TAG", e.getMessage());
showToast("注册失败!");
}
}
}.start();
}
public void showToast(final String msg) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();
}
});
}
}
public class HomeActivity extends AppCompatActivity implements EMContactListener, AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener, EMMessageListener {
ListView lv;
EditText et_friend;
List usernames;
ArrayAdapter adapter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
setTitle(getIntent().getStringExtra("username"));
lv = (ListView) findViewById(R.id.lv);
et_friend = (EditText) findViewById(R.id.et_friend);
initFriend();
//监听好友状态事件
EMClient.getInstance().contactManager().setContactListener(this);
lv.setOnItemClickListener(this);
lv.setOnItemLongClickListener(this);
//通过注册消息监听来接收消息
EMClient.getInstance().chatManager().addMessageListener(this);
}
private void initFriend() {
new Thread() {
@Override
public void run() {
super.run();
try {
//获取所有好友的集合
usernames = EMClient.getInstance().contactManager().getAllContactsFromServer();
} catch (HyphenateException e) {
e.printStackTrace();
}
if (usernames == null)
usernames = new ArrayList<>();
adapter = new ArrayAdapter(getBaseContext(), android.R.layout.simple_list_item_1, usernames);
runOnUiThread(new Runnable() {
@Override
public void run() {
lv.setAdapter(adapter);
}
});
}
}.start();
}
public void add(View v) {
//添加好友
final String toAddUsername = et_friend.getText().toString();//需要添加的名字
new Thread() {
@Override
public void run() {
super.run();
try {
//参数为要添加的好友的username和添加理由
EMClient.getInstance().contactManager().addContact(toAddUsername, "不加你试试!");
showToast("已发送请求");
} catch (HyphenateException e) {
e.printStackTrace();
showToast("添加失败");
}
}
}.start();
}
//监听好友状态事件
@Override
public void onContactAdded(String s) {
//好友请求被同意
usernames.add(s);
adapter.notifyDataSetChanged();
}
@Override
public void onContactDeleted(final String s) {
//被删除时回调此方法
showToast(s + "将你加入了黑名单");
runOnUiThread(new Runnable() {
@Override
public void run() {
usernames.remove(s);//用户名
adapter.notifyDataSetChanged();
}
});
}
@Override
public void onContactInvited(final String s, final String s1) {
//收到好友邀请
Log.e("TAG", "----->>>" + s + " " + s1);
runOnUiThread(new Runnable() {
@Override
public void run() {
new AlertDialog.Builder(HomeActivity.this).setTitle(s + "加你为好友")
.setMessage("添加理由:" + s1)
.setNeutralButton("拒绝", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
new Thread() {
@Override
public void run() {
super.run();
try {
//拒绝好友的请求
EMClient.getInstance().contactManager().declineInvitation(s);
} catch (HyphenateException e) {
showToast("拒绝失败");
e.printStackTrace();
}
}
}.start();
}
}).setPositiveButton("同意", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
new Thread() {
@Override
public void run() {
super.run();
try {
//同意好友请求
EMClient.getInstance().contactManager().acceptInvitation(s);
} catch (HyphenateException e) {
e.printStackTrace();
showToast("同意失败");
}
}
}.start();
}
})
.create().show(); }
});
}
@Override
public void onContactAgreed(String s) {
//增加了联系人时回调此方法
Log.e("TAG", "----" + s);
showToast(s + "同意了你的好友申请");
}
@Override
public void onContactRefused(String s) {
//好友请求被拒绝
showToast("对方拒绝了你的好友申请" + s);
}
public void showToast(final String msg) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(HomeActivity.this, msg, Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onItemClick(AdapterView parent, View view, final int position, long id) {
//发消息
new Thread(){
@Override
public void run() {
super.run();
//创建一条文本消息,content为消息文字内容,toChatUsername为对方用户或者群聊的id,后文皆是如此
EMMessage emMessage = EMMessage.createTxtSendMessage("你好,环信", usernames.get(position));
//发送本地图库中的图片
String path = Environment.getExternalStorageDirectory().getAbsolutePath()
+ "/Pictures/Screenshots/p18.jpg";
// EMMessage emMessage = EMMessage.createImageSendMessage(path, false, usernames.get(position));//发送图片
//设置聊天的类型,群聊,单聊,默认单聊
emMessage.setChatType(EMMessage.ChatType.Chat);
//发送消息
EMClient.getInstance().chatManager().sendMessage(emMessage);
}
}.start();
}
@Override
public boolean onItemLongClick(AdapterView parent, View view, final int position, long id) {
new Thread() {
@Override
public void run() {
super.run();
try {
//长按 删除这个好友
EMClient.getInstance().contactManager().deleteContact(usernames.get(position));
} catch (HyphenateException e) {
e.printStackTrace();
}
//改变适配器
usernames.remove(usernames.get(position));
runOnUiThread(new Runnable() {
@Override
public void run() {
adapter.notifyDataSetChanged();
}
});
}
}.start();
return true;
}
@Override
public void onMessageReceived(List list) {
//收到消息
for (EMMessage emMessage : list) {
// emMessage.getType() 发送的类型
// emMessage.getChatType() 群聊还是单聊 聊天室
// emMessage.getBody() 正文部分
if (emMessage.getType() == EMMessage.Type.TXT) {
EMTextMessageBody body = (EMTextMessageBody) emMessage.getBody();
String message = body.getMessage();
showToast(message);
} else if (emMessage.getType() == EMMessage.Type.IMAGE) {
Log.e("TAG", Thread.currentThread().getName());
final EMImageMessageBody body = (EMImageMessageBody) emMessage.getBody();
Log.e("TAG", body.getFileName() + " " + body.getThumbnailUrl() + " " + body.getLocalUrl() + " " + body.getRemoteUrl() + " "
+ body.getHeight() + " " + body.getWidth());
runOnUiThread(new Runnable() {
@Override
public void run() {
ImageView iv = new ImageView(HomeActivity.this);
//这里打印的是
// E/TAG: image-334580017.jpg https://a1.easemob.com/huary/demo/chatfiles/ebea2730-c1c8-11e6-84ec-4bbc6c562e3a
// /storage/emulated/0/Android/data/com.example.easemode/huary#demo/files/so2/so1/ebea2730-c1c8-11e6-84ec-4bbc6c562e3a.jpg
// https://a1.easemob.com/huary/demo/chatfiles/ebea2730-c1c8-11e6-84ec-4bbc6c562e3a 384 683
//图片返回的是一个json, 需要自己下载,也可以用流去转换
Glide.with(HomeActivity.this).load(body.getLocalUrl()).into(iv);
new AlertDialog.Builder(HomeActivity.this).setView(iv)
.create().show();
}
});
}
}
}
@Override
public void onCmdMessageReceived(List list) {
//收到透传消息
}
@Override
public void onMessageReadAckReceived(List list) {
//收到已读回执
}
@Override
public void onMessageDeliveryAckReceived(List list) {
//收到已送达回执
}
@Override
public void onMessageChanged(EMMessage emMessage, Object o) {
//消息状态变动
}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_main"
android:layout_width="match_parent" android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:padding="50dp"
>
<EditText
android:id="@+id/et_username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="用户名" />
<EditText
android:id="@+id/et_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="密码" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:onClick="login"
android:text="登录" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:onClick="register"
android:text="注册" />
LinearLayout>
LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="@+id/et_friend"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="add"
android:text="添加" />
LinearLayout>
<ListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="match_parent" />
LinearLayout>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.easemode">
<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.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<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" />
<application
android:name=".MyApp"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
intent-filter>
activity>
<activity android:name=".HomeActivity"/>
<meta-data
android:name="EASEMOB_APPKEY"
android:value="huary#demo" />
<service
android:name="com.hyphenate.chat.EMChatService"
android:exported="true" />
<service
android:name="com.hyphenate.chat.EMJobService"
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE" />
<receiver android:name="com.hyphenate.chat.EMMonitorReceiver">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REMOVED" />
<data android:scheme="package" />
intent-filter>
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.USER_PRESENT" />
intent-filter>
receiver>
application>
manifest>