环信实现基本聊天功能

用环信实现基本的聊天功能:

1、先到官网下载官方的demo:http://www.easemob.com/download/im

2、新建一个工程,然后将下面几个文件加到libs 里面,没有这个文件的可以在app下新建一个。


3、配置信息

在清单文件 AndroidManifest.xml 里加入以下权限,以及写上你注册的 AppKey。

<?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.example.apple.huanxin">

    <!-- Required -->
    <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" />
    <uses-permission android:name="android.permission.USE_CREDENTIALS" />
    <uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
    <uses-permission android:name="android.permission.BROADCAST_STICKY" />
    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
    <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
    <permission
        android:name="com.hyphenate.chatuidemo.permission.C2D_MESSAGE"
        android:protectionLevel="signature" />

    <uses-permission android:name="com.hyphenate.chatuidemo.permission.C2D_MESSAGE" />

    <permission
        android:name="com.hyphenate.chatuidemo.permission.MIPUSH_RECEIVE"
        android:protectionLevel="signatureOrSystem" />

    <uses-permission android:name="com.hyphenate.chatuidemo.permission.MIPUSH_RECEIVE" />

    <application
        android:name=".MyApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".Login">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <!-- 设置环信应用的appKey -->
        <meta-data
            android:name="EASEMOB_APPKEY"
            android:value="lzan13#hxsdkdemo" />
        <!-- 声明sdk所需的service -->
        <service
            android:name="com.hyphenate.chat.EMChatService"
            android:exported="true"
            tools:ignore="ExportedService" />

        <!-- 设置环信应用的 appkey 换成自己的 -->
        <meta-data
            android:name="EASEMOB_APPKEY"
            android:value="lzan13#hxsdkdemo" />
        <!-- 声明sdk所需的service SDK核心功能 -->
        <service
            android:name="com.hyphenate.chat.EMChatService"
            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>

        <activity android:name=".MainActivity" />

        <!-- android:windowSoftInputMode="adjustResize|stateHidden"-->
        <activity android:name=".Chat"></activity>

    </application>

</manifest>

新建一个MyApplication文件并继承application 然后配置信息初始化

public class MyApplication extends Application {

    // 上下文菜单
    private Context mContext;

    // 记录是否已经初始化
    private boolean isInit = false;

    @Override
    public void onCreate() {
        super.onCreate();
        mContext = this;

        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
        EMClient.getInstance().init(mContext, initOptions());

        // 设置开启debug模式
        EMClient.getInstance().setDebugMode(true);

        // 设置初始化已经完成
        isInit = true;
    }

    /**
     * SDK初始化的一些配置
     * 关于 EMOptions 可以参考官方的 API 文档
     * http://www.easemob.com/apidoc/android/chat3.0/classcom_1_1hyphenate_1_1chat_1_1_e_m_options.html
     */
    private EMOptions initOptions() {

        EMOptions options = new EMOptions();
        // 设置Appkey,如果配置文件已经配置,这里可以不用设置
        // options.setAppKey("lzan13#hxsdkdemo");
        // 设置自动登录
        /**
         * 自动登录在以下几种情况下会被取消:

         用户调用了 SDK 的登出动作;
         用户在别的设备上更改了密码,导致此设备上自动登录失败;
         用户的账号被从服务器端删除;
         用户从另一个设备登录,把当前设备上登录的用户踢出。
         */
        options.setAutoLogin(true);
        // 设置是否需要发送已读回执
        options.setRequireAck(true);
        // 设置是否需要发送回执,
        options.setRequireDeliveryAck(true);
        // 设置是否需要服务器收到消息确认
        options.setRequireServerAck(true);
        // 设置是否根据服务器时间排序,默认是true
        options.setSortMessageByServerTime(false);
        // 收到好友申请是否自动同意,如果是自动同意就不会收到好友请求的回调,因为sdk会自动处理,默认为true
        options.setAcceptInvitationAlways(false);
        // 设置是否自动接收加群邀请,如果设置了当收到群邀请会自动同意加入
        options.setAutoAcceptGroupInvitation(false);
        // 设置(主动或被动)退出群组时,是否删除群聊聊天记录
        options.setDeleteMessagesAsExitGroup(false);
        // 设置是否允许聊天室的Owner 离开并删除聊天室的会话
        options.allowChatroomOwnerLeave(true);
        // 设置google GCM推送id,国内可以不用设置
        // options.setGCMNumber(MLConstants.ML_GCM_NUMBER);
        // 设置集成小米推送的appid和appkey
        // options.setMipushConfig(MLConstants.ML_MI_APP_ID, MLConstants.ML_MI_APP_KEY);

        return options;
    }

    /**
     * 根据Pid获取当前进程的名字,一般就是当前app的包名
     *
     * @param pid 进程的id
     * @return 返回进程的名字
     */
    private String getAppName(int pid) {
        String processName = null;
        ActivityManager activityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
        List list = activityManager.getRunningAppProcesses();
        Iterator i = list.iterator();
        while (i.hasNext()) {
            ActivityManager.RunningAppProcessInfo info = (ActivityManager.RunningAppProcessInfo) (i.next());
            try {
                if (info.pid == pid) {
                    // 根据进程的信息获取当前进程的名字
                    processName = info.processName;
                    // 返回当前进程名
                    return processName;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        // 没有匹配的项,返回为null
        return null;
    }

}
4、实现注册

注册模式分两种,开放注册和授权注册。可以登录环信管理后台,在“应用概况”页,切换注册模式。

  • 只有开放注册模式下,才可以客户端注册。开放注册是为了测试使用,正式环境中不推荐使用该方式注册环信 ID。
  • 授权注册的流程应该是您服务器通过环信提供的 REST  API 注册,之后保存到您的服务器或返回给客户端。
注册用户名会自动转为小写字母,所以建议用户名均以小写注册(强烈建议开发者通过后台调用 REST  API  去注册环信 ID,客户端注册方法不提倡使用)。

new Thread(new Runnable() {
    public void run() {
      try {
         // 调用sdk注册方法
         EMChatManager.getInstance().createAccountOnServer(username, pwd);
      } catch (final EaseMobException e) {
      //注册失败
        int errorCode=e.getErrorCode();
        if(errorCode==EMError.NONETWORK_ERROR){
            Toast.makeText(getApplicationContext(), "网络异常,请检查网络!", Toast.LENGTH_SHORT).show();
        }else if(errorCode==EMError.USER_ALREADY_EXISTS){
            Toast.makeText(getApplicationContext(), "用户已存在!", Toast.LENGTH_SHORT).show();
        }else if(errorCode==EMError.UNAUTHORIZED){
            Toast.makeText(getApplicationContext(), "注册失败,无权限!", Toast.LENGTH_SHORT).show();
        }else{
            Toast.makeText(getApplicationContext(), "注册失败: " + e.getMessage(), Toast.LENGTH_SHORT).show();
      }
   }
}).start();
5、登陆实现

登录成功后需要调用 EMGroupManager.getInstance().loadAllGroups(); 和 EMChatManager.getInstance().loadAllConversations();

以上两个方法是为了保证进入主页面后本地会话和群组都 load 完毕。

另外如果登录过,APP 长期在后台再进的时候也可能会导致加载到内存的群组和会话为空,可以在主页面的 oncreate 里也加上这两句代码,当然,更好的办法应该是放在程序的开屏页

EMChatManager.getInstance().login(userName,password,new EMCallBack() {//回调
    @Override
    public void onSuccess() {
        runOnUiThread(new Runnable() {
            public void run() {
                EMGroupManager.getInstance().loadAllGroups();
                EMChatManager.getInstance().loadAllConversations();
                Log.d("main", "登录聊天服务器成功!");        
            }
        });
    }
 
    @Override
    public void onProgress(int progress, String status) {
 
    }
 
    @Override
    public void onError(int code, String message) {
        Log.d("main", "登录聊天服务器失败!");
    }
});
完整代码:布局主要就是加两个输入框和两个按钮,并详细了错误的信息,具体错误可以到下面网址查看:http://www.easemob.com/apidoc/android/chat3.0/classcom_1_1hyphenate_1_1_e_m_error.html#a09893124e1fbcd100a9583a4293957af

public class Login extends AppCompatActivity {

    // 弹出框
    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 (!Login.this.isFinishing()) {
                                mDialog.dismiss();
                            }
                            Toast.makeText(Login.this, "注册成功", Toast.LENGTH_LONG).show();
                        }
                    });
                } catch (final HyphenateException e) {
                    e.printStackTrace();
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            if (!Login.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(Login.this, "网络错误 code: " + errorCode + ", message:" + message, Toast.LENGTH_LONG).show();
                                    break;
                                // 用户已存在
                                case EMError.USER_ALREADY_EXIST:
                                    Toast.makeText(Login.this, "用户已存在 code: " + errorCode + ", message:" + message, Toast.LENGTH_LONG).show();
                                    break;
                                // 参数不合法,一般情况是username 使用了uuid导致,不能使用uuid注册
                                case EMError.USER_ILLEGAL_ARGUMENT:
                                    Toast.makeText(Login.this, "参数不合法,一般情况是username 使用了uuid导致,不能使用uuid注册 code: " + errorCode + ", message:" + message, Toast.LENGTH_LONG).show();
                                    break;
                                // 服务器未知错误
                                case EMError.SERVER_UNKNOWN_ERROR:
                                    Toast.makeText(Login.this, "服务器未知错误 code: " + errorCode + ", message:" + message, Toast.LENGTH_LONG).show();
                                    break;
                                case EMError.USER_REG_FAILED:
                                    Toast.makeText(Login.this, "账户注册失败 code: " + errorCode + ", message:" + message, Toast.LENGTH_LONG).show();
                                    break;
                                default:
                                    Toast.makeText(Login.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();
        if (TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) {
            Toast.makeText(Login.this, "用户名和密码不能为空", Toast.LENGTH_LONG).show();
            return;
        }

//        Intent intent = new Intent(Login.this, MainActivity.class);
//        startActivity(intent);
//        finish();
        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(Login.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(Login.this, "网络错误 code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
                                break;
                            // 无效的用户名 101
                            case EMError.INVALID_USER_NAME:
                                Toast.makeText(Login.this, "无效的用户名 code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
                                break;
                            // 无效的密码 102
                            case EMError.INVALID_PASSWORD:
                                Toast.makeText(Login.this, "无效的密码 code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
                                break;
                            // 用户认证失败,用户名或密码错误 202
                            case EMError.USER_AUTHENTICATION_FAILED:
                                Toast.makeText(Login.this, "用户认证失败,用户名或密码错误 code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
                                break;
                            // 用户不存在 204
                            case EMError.USER_NOT_FOUND:
                                Toast.makeText(Login.this, "用户不存在 code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
                                break;
                            // 无法访问到服务器 300
                            case EMError.SERVER_NOT_REACHABLE:
                                Toast.makeText(Login.this, "无法访问到服务器 code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
                                break;
                            // 等待服务器响应超时 301
                            case EMError.SERVER_TIMEOUT:
                                Toast.makeText(Login.this, "等待服务器响应超时 code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
                                break;
                            // 服务器繁忙 302
                            case EMError.SERVER_BUSY:
                                Toast.makeText(Login.this, "服务器繁忙 code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
                                break;
                            // 未知 Server 异常 303 一般断网会出现这个错误
                            case EMError.SERVER_UNKNOWN_ERROR:
                                Toast.makeText(Login.this, "未知的服务器异常 code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
                                break;
                            default:
                                Toast.makeText(Login.this, "ml_sign_in_failed code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
                                break;
                        }
                    }
                });
            }

            @Override
            public void onProgress(int i, String s) {

            }
        });
    }
6、自动登陆

即首次登录成功后,不需要再次调用登录方法,在下次 APP 启动时,SDK 会自动为您登录。并且如果您自动登录失败,也可以读取到之前的会话信息(以上情况是在未调用登出的情况下实现的)。

SDK 中自动登录属性默认是 true 打开的,如果不需要自动登录,在初始化 SDK 之前,调用 EMChat.getInstance().setAutoLogin(false); 设置为 false 关闭。

自动登录在以下几种情况下会被取消:

  • 用户调用了 SDK 的登出动作;
  • 用户在别的设备上更改了密码,导致此设备上自动登录失败;
  • 用户的账号被从服务器端删除;
  • 用户从另一个设备登录,把当前设备上登录的用户踢出。
重连实现: 当掉线时,Android SDK 会自动重连,无需进行任何操作。

//注册一个监听连接状态的listener
EMChatManager.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.CONNECTION_CONFLICT) {
                    // 显示帐号在其他设备登录
                } else {
                if (NetUtils.hasNetwork(MainActivity.this))
                    //连接不到聊天服务器
                else
                    //当前网络不可用,请检查网络设置
                }
            }
        });
    }
}
退出登录实现

 /**
     * 退出登陆
     */
    private void setLogout(){
      //  EMClient.getInstance().logout(true);//同步方法

        //异步方法
        // 调用sdk的退出登录方法,第一个参数表示是否解绑推送的token,没有使用推送或者被踢都要传false
        EMClient.getInstance().logout(false, new EMCallBack() {
            @Override
            public void onSuccess() {
                Log.e("lzan13", "logout success");
                // 调用退出成功,结束app
                finish();
            }

            @Override
            public void onError(int i, String s) {
                Log.e("lzan13", "logout error " + i + " - " + s);
            }

            @Override
            public void onProgress(int i, String s) {

            }
        });
    }
完整代码:

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    private Button button;
    private Button logout;
    private EditText userName;
    private final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 判断sdk是否登录成功过,并没有退出和被踢,否则跳转到登陆界面
        if (!EMClient.getInstance().isLoggedInBefore()) {
            Intent intent = new Intent(MainActivity.this, Login.class);
            startActivity(intent);
            finish();
            return;
        }

        initView();
    }

    private void initView() {

        button = (Button)findViewById(R.id.btn);
        button.setOnClickListener(this);

        logout = (Button)findViewById(R.id.logout);
        logout.setOnClickListener(this);

        userName = (EditText)findViewById(R.id.userName);

        //注册一个监听连接状态的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)){
                            Toast.makeText(getApplication(),"连接不到聊天服务器",Toast.LENGTH_SHORT).show();
                            Log.e(TAG,"连接不到聊天服务器");
                            //连接不到聊天服务器
                        } else{
                            Toast.makeText(getApplication(),"当前网络不可用,请检查网络设置",Toast.LENGTH_SHORT).show();
                            Log.e(TAG,"当前网络不可用,请检查网络设置");
                            //当前网络不可用,请检查网络设置
                        }

                    }
                }
            });
        }
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.logout:
                setLogout();

                break;

            case R.id.btn:
                if (!userName.getText().toString().equals("")) {
                    Intent intent = new Intent(this, Chat.class);
                    intent.putExtra("userName", userName.getText().toString());
                    startActivity(intent);
                }else {
                    Toast.makeText(getApplication(),"用户名不能为空!",Toast.LENGTH_SHORT).show();
                }

                break;
        }
    }

    /**
     * 退出登陆
     */
    private void setLogout(){
      //  EMClient.getInstance().logout(true);//同步方法

        //异步方法
        // 调用sdk的退出登录方法,第一个参数表示是否解绑推送的token,没有使用推送或者被踢都要传false
        EMClient.getInstance().logout(false, new EMCallBack() {
            @Override
            public void onSuccess() {
                Log.e("lzan13", "logout success");
                // 调用退出成功,结束app
                finish();
            }

            @Override
            public void onError(int i, String s) {
                Log.e("lzan13", "logout error " + i + " - " + s);
            }

            @Override
            public void onProgress(int i, String s) {

            }
        });
    }




}
7、实现基本的聊天功能

布局代码

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:layout_height="match_parent">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <LinearLayout
            android:id="@+id/ec_layout_input"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:orientation="horizontal">

            <EditText
                android:id="@+id/input_text"
                android:layout_width="match_parent"
                android:layout_height="50dip"
                android:layout_weight="3" />

            <Button
                android:id="@+id/send"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="发送" />

        </LinearLayout>

        <TextView
            android:id="@+id/text_content"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:maxLines="15"
            android:layout_marginBottom="100dip"
            android:scrollbars="vertical" />
    </RelativeLayout>


</LinearLayout>

首先获取到从上一界面传递过来的用户id

 Intent intent = getIntent();
        userName = intent.getStringExtra("userName");
        mMessageListener = this;
实现发送消息功能

 private void sendMessage(){

        String content = input_text.getText().toString();

        //创建一条文本消息,content为消息文字内容,toChatUsername为对方用户或者群聊的id,后文皆是如此
        EMMessage message = EMMessage.createTxtSendMessage(content, userName);
      //  text_content.setText(content);
        text_content.setText(text_content.getText() + "\n"+ mConversation.getAllMessages().get(0).getFrom()+":"+ content + " - time: " + message.getMsgTime());
        //如果是群聊,设置chattype,默认是单聊
       // if (chatType == CHATTYPE_GROUP)
        //message.setChatType(EMMessage.ChatType.GroupChat);
        //发送消息
        EMClient.getInstance().chatManager().sendMessage(message);

        // 为消息设置回调
        message.setMessageStatusCallback(new EMCallBack() {
            @Override
            public void onSuccess() {
                // 消息发送成功,打印下日志,正常操作应该去刷新ui
                Log.e(TAG, "send message on success");
            }

            @Override
            public void onError(int i, String s) {
                // 消息发送失败,打印下失败的信息,正常操作应该去刷新ui
                Log.e(TAG, "send message on error " + i + " - " + s);
            }

            @Override
            public void onProgress(int i, String s) {
                // 消息发送进度,一般只有在发送图片和文件等消息才会有回调,txt不回调
            }
        });
    }
接收消息,主要通过一个线程来接收并显示

 /**
     * 收到新消息
     * @param messages
     */
    @Override
    public void onMessageReceived(List<EMMessage> messages) {
        //收到消息
        // 循环遍历当前收到的消息
        for (EMMessage message : messages) {
            if (message.getFrom().equals(userName)) {
                // 设置消息为已读
                mConversation.markMessageAsRead(message.getMsgId());

                // 因为消息监听回调这里是非ui线程,所以要用handler去更新ui
                Message msg = mHandler.obtainMessage();
                msg.what = 0;
                msg.obj = message;
                mHandler.sendMessage(msg);
            } else {
                // 如果消息不是当前会话的消息发送通知栏通知
            }
        }
    }
/**
     * 自定义实现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();
                    // 将新的消息内容和时间加入到下边
                    Log.e("nsc", body.getMessage());
                    //text_content.setText(body.getMessage());
                    text_content.setText(text_content.getText() + "\n"+mConversation.getAllMessages().get(0).getTo()+":" + body.getMessage() + " - time: " + message.getMsgTime());
                    break;
            }
        }
    };
数据格式,即返回的数据格式

{
    "callId":"",//每个回调的ID都不一样
    "eventType":"chat_offline",//用于以后的扩展,现在只推送聊天消息(离线和所有),以后会加入更多,比如用户加入了某个群组
    "timestamp":0,//环信接收到此消息的时间
    "chat_type":"groupchat", // 群聊,如果是单聊则为"chat"
    "group_id":'',//群聊时才有此参数
    "from":"",//消息的发送方
    "to":"",//消息的接收方
    "msg_id":"",// 消息ID
    "payload":{//消息,与通过REST API发送过来的一致
    },
    "securityVersion":"",//安全校验版本,目前为1.0.0。忽略此参数,以后会改成Console后台做设置
    "security":""//签名。格式如下: MD5(callId+约定的key+timestamp),约定的key为123456,以后会改成Console后台做设置
}
完整代码
public class Chat extends AppCompatActivity implements View.OnClickListener,EMMessageListener{

    private EditText input_text;
    private Button send;
    private TextView text_content;

    private String userName;// 当前聊天的 ID

    // 当前会话对象
    private EMConversation mConversation;

    // 消息监听器
    private EMMessageListener mMessageListener;

    private final String TAG = "Chat";


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_chat);

        Intent intent = getIntent();
        userName = intent.getStringExtra("userName");
        mMessageListener = this;

        initView();
        initConversation();
    }

    private void initView() {


        input_text = (EditText)findViewById(R.id.input_text);
        send = (Button)findViewById(R.id.send);
        send.setOnClickListener(this);

        text_content = (TextView)findViewById(R.id.text_content);
        // 设置textview可滚动,需配合xml布局设置
        text_content.setMovementMethod(new ScrollingMovementMethod());

    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){

            case R.id.send:
                    sendMessage();
                break;

            default:

                break;

        }
    }

    /**
     * 初始化会话对象,并且根据需要加载更多消息
     */
    private void initConversation() {

        /**
         * 初始化会话对象,这里有三个参数么,
         * 第一个表示会话的当前聊天的 useranme 或者 groupid
         * 第二个是绘画类型可以为空
         * 第三个表示如果会话不存在是否创建
         */
        mConversation = EMClient.getInstance().chatManager().getConversation(userName, null, true);

        for (int i = 0; i<mConversation.getAllMessages().size();i++){
            Log.e(TAG,"mConversation="+mConversation.getAllMessages().get(i).toString()+"\n");
        }

        // 设置当前会话未读数为 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();
            // 将消息内容和时间显示出来
            //text_content.setText("聊天记录:" + body.getMessage() + " - time: " + 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();
                    // 将新的消息内容和时间加入到下边
                    Log.e("nsc", body.getMessage());
                    //text_content.setText(body.getMessage());
                    text_content.setText(text_content.getText() + "\n"+mConversation.getAllMessages().get(0).getTo()+":" + body.getMessage() + " - time: " + message.getMsgTime());
                    break;
            }
        }
    };

    private void sendMessage(){

        String content = input_text.getText().toString();

        //创建一条文本消息,content为消息文字内容,toChatUsername为对方用户或者群聊的id,后文皆是如此
        EMMessage message = EMMessage.createTxtSendMessage(content, userName);
      //  text_content.setText(content);
        text_content.setText(text_content.getText() + "\n"+ mConversation.getAllMessages().get(0).getFrom()+":"+ content + " - time: " + message.getMsgTime());
        //如果是群聊,设置chattype,默认是单聊
       // if (chatType == CHATTYPE_GROUP)
        //message.setChatType(EMMessage.ChatType.GroupChat);
        //发送消息
        EMClient.getInstance().chatManager().sendMessage(message);

        // 为消息设置回调
        message.setMessageStatusCallback(new EMCallBack() {
            @Override
            public void onSuccess() {
                // 消息发送成功,打印下日志,正常操作应该去刷新ui
                Log.e(TAG, "send message on success");
            }

            @Override
            public void onError(int i, String s) {
                // 消息发送失败,打印下失败的信息,正常操作应该去刷新ui
                Log.e(TAG, "send message on error " + i + " - " + s);
            }

            @Override
            public void onProgress(int i, String s) {
                // 消息发送进度,一般只有在发送图片和文件等消息才会有回调,txt不回调
            }
        });
    }



    @Override
    protected void onResume() {
        super.onResume();
        // 添加消息监听
        EMClient.getInstance().chatManager().addMessageListener(mMessageListener);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // 移除消息监听
        EMClient.getInstance().chatManager().removeMessageListener(mMessageListener);
    }

    /**
     * 收到新消息
     * @param messages
     */
    @Override
    public void onMessageReceived(List<EMMessage> messages) {
        //收到消息
        // 循环遍历当前收到的消息
        for (EMMessage message : messages) {
            if (message.getFrom().equals(userName)) {
                // 设置消息为已读
                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.e("nsc", body.action());
        }
    }

    /**
     * 收到新的已读回执
     * @param list
     */
    @Override
    public void onMessageReadAckReceived(List<EMMessage> list) {

    }

    /**
     * 收到新的发送回执
     * @param list
     */
    @Override
    public void onMessageDeliveryAckReceived(List<EMMessage> list) {

    }

    /**
     * 消息的状态改变
     * @param emMessage
     * @param o
     */
    @Override
    public void onMessageChanged(EMMessage emMessage, Object o) {

    }
到此就可以实现基本的聊天了。代码下载地址: 点击打开链接

















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值