使用环信sdk做一个简单的聊天APP

使用环信sdk做一个简单的即时聊天APP,实现简单的通讯

感悟

在很早以前就想做一个简单的即时聊天app。可能自己对即时聊天的三方SDK了解的不是特别深,在进行了一段时间的学习以后感觉自己的能力达到了一定的程度,可以做这种APP了,经过两天的努力终于成功了,现在就把遇到的困难和代码分享给大家,希望对大家能有帮助。

使用环信sdk的好处

对于市面上一些相同的sdk软件,经过对比,环信sdk的好处

  1. 完全免费不会收取多余的费用
  2. 经过简单的导入三方依赖,或者导入模块都可以实现。
  3. 基本常用的方法都已经写好,简单的初始化都可以实现功能
  4. 适合新手小白练手

实现过程

1.首先还是第一步导入sdk,在bulid.gradle添加即可

dependencies {
    //noinspection GradleCompatible
    implementation 'com.android.support:appcompat-v7:27.1.1'
    implementation 'com.hyphenate:hyphenate-sdk:3.6.1'
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
    implementation 'androidx.recyclerview:recyclerview:1.0.0'
}

2.新建三个布局,分别是登陆界面,添加界面,聊天界面,

登陆界面


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

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="72dp"
            android:layout_marginLeft="48dp"
            android:layout_marginTop="100dp"
            android:layout_marginRight="48dp"
            android:orientation="vertical">

            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="56dp"
                android:focusable="true"
                android:focusableInTouchMode="true">

                <EditText
                    android:id="@+id/ec_edit_username"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_alignParentBottom="true"
                    android:background="@drawable/selector_et_line"
                    android:hint=" 手机号"
                    android:maxLength="11"
                    android:nextFocusForward="@+id/et_password"
                    android:paddingTop="30dp"
                    android:paddingBottom="8dp"
                    android:singleLine="true"
                    android:textColor="#CDC9C9"
                    android:textColorHint="#CDC9C9"
                    android:textSize="15sp" />

                <ImageView
                    android:id="@+id/iv_username"
                    android:layout_width="24dp"
                    android:layout_height="24dp"
                    android:layout_alignParentEnd="true"
                    android:layout_alignParentBottom="true"
                    android:layout_marginEnd="2dp"
                    android:layout_marginBottom="4dp"
                    android:src="@drawable/svg_cancel_c126"
                    android:visibility="gone" />
            </RelativeLayout>


        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="72dp"
            android:layout_marginLeft="48dp"
            android:layout_marginRight="48dp"
            android:orientation="vertical">

            <RelativeLayout

                android:layout_width="match_parent"
                android:layout_height="56dp">

                <EditText
                    android:id="@+id/ec_edit_password"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_alignParentBottom="true"
                    android:background="@drawable/selector_et_line"
                    android:hint=" 密码"
                    android:imeOptions="actionDone"
                    android:inputType="textPassword"
                    android:maxLength="20"
                    android:paddingTop="30dp"
                    android:paddingBottom="8dp"
                    android:singleLine="true"
                    android:textColor="#CDC9C9"
                    android:textColorHint="#CDC9C9"
                    android:textSize="15sp" />

                <ImageView
                    android:id="@+id/iv_password"
                    android:layout_width="24dp"
                    android:layout_height="24dp"
                    android:layout_alignParentEnd="true"
                    android:layout_alignParentBottom="true"
                    android:layout_marginEnd="4dp"
                    android:layout_marginBottom="4dp"
                    android:src="@drawable/svg_gone_c126"
                    android:visibility="gone" />
            </RelativeLayout>

        </LinearLayout>

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="48dp"
            android:layout_marginTop="32dp"
            android:layout_marginRight="48dp">

            <Button
                android:id="@+id/ec_btn_sign_up"
                android:layout_width="match_parent"
                android:layout_height="36dp"
                android:layout_marginLeft="2dp"
                android:layout_marginTop="2dp"
                android:layout_marginRight="2dp"
                android:layout_marginBottom="2dp"
                android:background="@drawable/shape_rectangle_button_c129"
                android:text="注册" />


        </RelativeLayout>
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="48dp"
            android:layout_marginTop="32dp"
            android:layout_marginRight="48dp">

            <Button
                android:id="@+id/ec_btn_sign_in"
                android:layout_width="match_parent"
                android:layout_height="36dp"
                android:layout_marginLeft="2dp"
                android:layout_marginTop="2dp"
                android:layout_marginRight="2dp"
                android:layout_marginBottom="2dp"
                android:background="@drawable/shape_rectangle_button_c129"
                android:text="登陆" />


        </RelativeLayout>
    </LinearLayout>


</RelativeLayout>

聊天请求界面

<?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>

聊天界面

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

    android:layout_height="match_parent">
    <RelativeLayout
        android:background="#404040"
        android:id="@+id/bt_xx"
        android:layout_width="match_parent"
        android:layout_height="50dp">
<TextView
    android:textColor="#ffffff"
    android:text="username"
    android:textSize="18sp"
    android:id="@+id/bt_name"
    android:layout_centerHorizontal="true"
    android:layout_centerVertical="true"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>
        <ImageView
            android:id="@+id/back"
            android:layout_centerVertical="true"
            android:background="@drawable/svg_back_c100"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    </RelativeLayout>
    <RelativeLayout
        android:id="@+id/ec_layout_input"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_alignParentBottom="true">

        <Button
            android:layout_marginLeft="5dp"
            android:layout_marginRight="5dp"
            android:layout_centerVertical="true"
            android:background="#A2CD5A"
            android:id="@+id/ec_btn_send"
            android:layout_width="80dp"
            android:layout_height="35dp"
            android:layout_alignParentRight="true"
            android:text="发送"/>

        <EditText
            android:layout_marginTop="5dp"
            android:layout_marginLeft="10dp"
            android:textColorHint="	#CCCCCC"
            android:textColor="	#CCCCCC"
            android:textSize="16sp"
            android:hint="请输入消息内容"
            android:layout_centerVertical="true"
            android:background="@android:drawable/edit_text"
            android:id="@+id/ec_edit_message_input"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:layout_alignParentLeft="true"
            android:layout_toLeftOf="@id/ec_btn_send"/>
    </RelativeLayout>
    <androidx.recyclerview.widget.RecyclerView
        android:layout_below="@id/bt_xx"
        android:layout_above="@id/ec_layout_input"
        android:id="@+id/mRecyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</RelativeLayout>

RecyclerView的item

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

    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="horizontal">

    <LinearLayout
      android:layout_alignParentLeft="true"
        android:id="@+id/ll_msg_left"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="?android:attr/selectableItemBackground"
        android:clickable="true"
        android:focusable="true"
        android:orientation="horizontal"
        android:padding="2dp">
        <ImageView
            android:layout_marginLeft="10dp"
            android:layout_gravity="center_vertical"
            android:id="@+id/from_person_avator"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:background="@drawable/tx" />
        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="60dp"
            android:background="@drawable/jeishou"
            android:orientation="horizontal">
            <TextView
                android:singleLine="false"
                android:layout_marginLeft="60dp"
                android:id="@+id/tv_msg_left"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:textColor="#000000" />
        </LinearLayout>



    </LinearLayout>

    <LinearLayout

        android:layout_alignParentRight="true"
        android:id="@+id/ll_msg_right"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"

        android:background="?android:attr/selectableItemBackground"
        android:clickable="true"
        android:focusable="true"
        android:orientation="horizontal"
        android:padding="2dp">

        <LinearLayout

            android:layout_width="wrap_content"
            android:layout_height="60dp"
            android:background="@drawable/fasong"
            android:orientation="horizontal">

            <TextView
                android:singleLine="false"
                android:layout_marginLeft="35dp"
                android:id="@+id/tv_msg_right"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:paddingRight="70dp"
                android:textColor="#000000" />

        </LinearLayout>

        <ImageView
            android:layout_marginRight="10dp"
            android:layout_gravity="center_vertical"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:background="@drawable/tx" />

    </LinearLayout>
</RelativeLayout>

现在所有用到的html代码已经结束。

是不是很简单啊,其实真的没多少东西,代码里用到的图片自己随意更换就好

现在是java代码阶段,看仔细呦

sdk初始化


public class ECApplication 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);

        if (processAppName == null || !processAppName.equalsIgnoreCase(mContext.getPackageName())) {
            // 则此application的onCreate 是被service 调用的,直接返回
            return;
        }
        if (isInit) {
            return;
        }
    
        EMOptions options = new EMOptions();
        // 设置自动登录
        options.setAutoLogin(true);
        // 设置是否需要发送已读回执
        options.setRequireAck(true);
        // 设置是否需要发送回执,TODO 这个暂时有bug,上层收不到发送回执
        options.setRequireDeliveryAck(true);
        // 设置是否需要服务器收到消息确认
//        options.setRequireServerAck(true);
        // 收到好友申请是否自动同意,如果是自动同意就不会收到好友请求的回调,因为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);

        // 调用初始化方法初始化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 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;
    }
}

登陆界面

package com.example.hx.tongxun;

public class ECLoginActivity 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_login3);

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

    String username = mUsernameEdit.getText().toString().trim();
    String password = mPasswordEdit.getText().toString().trim();
    if (username.isEmpty()||password.isEmpty()){
        Toast.makeText(this, "用户名或密码不能为空", Toast.LENGTH_SHORT).show();
    } else {
        mDialog.setMessage("正在登陆,请稍后...");
        mDialog.show();
        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(ECLoginActivity.this, ECMainActivity.class);
                        startActivity(intent);
                        finish();
                    }
                });
            }

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

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

            }
        });
    }
 }
}

查找好友界面



public class ECMainActivity 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()) {
            Intent intent = new Intent(ECMainActivity.this, ECLoginActivity.class);
            startActivity(intent);
            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(ECMainActivity.this, "不能和自己聊天", Toast.LENGTH_SHORT).show();
                        return;
                    }
                    // 跳转到聊天界面,开始聊天
                    Intent intent = new Intent(ECMainActivity.this, ECChatActivity.class);
                    intent.putExtra("ec_chat_id", chatId);
                    startActivity(intent);
                } else {
                    Toast.makeText(ECMainActivity.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
               Intent intent=new Intent(ECMainActivity.this,ECLoginActivity.class);
               startActivity(intent);
               finish();
               return;
            }

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

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

            }
        });
    }
}

聊天界面

public class ECChatActivity extends AppCompatActivity implements EMMessageListener {
    // 聊天信息输入框
    private EditText mInputEdit;
    // 发送按钮
    private Button mSendBtn;
    // 显示内容的 TextView
    private TextView mContentText;

    // 消息监听器
    private EMMessageListener mMessageListener;
    // 当前聊天的 ID
    private String mChatId;
    // 当前会话对象
    private EMConversation mConversation;
    private RecyclerView mRecyclerView;
    private List mList = new ArrayList();
    private RecyclerView.Adapter<RecyclerView.ViewHolder> mAdapter;
    private MsgAdapter adapter;
    private ImageView back;
    private TextView bt_name;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_chat2);
        // 获取当前会话的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);
        mRecyclerView = (RecyclerView) findViewById(R.id.mRecyclerView);
        back = (ImageView) findViewById(R.id.back);
        bt_name = (TextView) findViewById(R.id.bt_name);
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        mRecyclerView.setLayoutManager(layoutManager);
        back.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                finish();
            }
        });
        // 设置发送按钮的点击事件
        mSendBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String content = mInputEdit.getText().toString().trim();
                if (!TextUtils.isEmpty(content)) {
//                ...//环信部分的发送消息
                    MSG msg = new MSG(content, MSG.TYPE_SEND);
                    mList.add(msg);
                    adapter = new MsgAdapter((List<MSG>) mList);
                    mRecyclerView.setAdapter(adapter);
                    //当有新消息时,刷新RecyclerView中的显示
                    adapter.notifyItemInserted(mList.size() - 1);
                    //使用RecyclerView加载新聊天页面
                    mRecyclerView.scrollToPosition(mList.size() - 1);
                    mInputEdit.setText("");
                    EMMessage message = EMMessage.createTxtSendMessage(content, mChatId);
                    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);
        }


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

    @Override
    protected void onStop() {
        super.onStop();
        // 移除消息监听
        EMClient.getInstance().chatManager().removeMessageListener(mMessageListener);
    }
    /**
     * --------------------------------- Message Listener -------------------------------------
     * 环信消息监听主要方法
     */
    /**
     * 收到新消息
     * <p>
     * //     * @param list 收到的新消息集合
//     */

    @Override
    public void onMessageReceived(List<EMMessage> messages) {
        //收到消息
        String result = messages.get(0).getBody().toString();
        String msgReceived = result.substring(5, result.length() - 1);
//        bt_name.setText(messages.get(0).);
        final MSG msg = new MSG(msgReceived, MSG.TYPE_RECEIVED);
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mList.add(msg);
                adapter = new MsgAdapter((List<MSG>) mList);
                mRecyclerView.setAdapter(adapter);
                mRecyclerView.scrollToPosition(mList.size() - 1);
            }
        });
    }

    /**
     * 收到新的 CMD 消息
     */
    @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", "收到 CMD 透传消息" + body.action());
        }
    }

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

    /**
     * 收到新的发送回执
     * TODO 无效 暂时有bug
     *
     * @param list 收到发送回执的消息集合
     */
    @Override
    public void onMessageDelivered(List<EMMessage> list) {
    }

    /**
     * 消息撤回回调
     *
     * @param list 撤回的消息列表
     */
    @Override
    public void onMessageRecalled(List<EMMessage> list) {
    }

    /**
     * 消息的状态改变
     *
     * @param message 发生改变的消息
     * @param object  包含改变的消息
     */
    @Override
    public void onMessageChanged(EMMessage message, Object object) {
    }
}

MsgAdapter


public class MsgAdapter extends RecyclerView.Adapter<MsgAdapter.MyViewHolder> {

    private List<MSG> mMsgList;

    public MsgAdapter(List<MSG> mMsgList) {
        this.mMsgList = mMsgList;
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = View.inflate(parent.getContext(), R.layout.recyclerview_item, null);
        MyViewHolder holder = new MyViewHolder(view);
        return holder;
    }


    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        MSG msg = mMsgList.get(position);
        if (msg.getType() == MSG.TYPE_RECEIVED) {
            //如果是收到的消息,显示左边布局,隐藏右边布局
            holder.llLeft.setVisibility(View.VISIBLE);
            holder.llRight.setVisibility(View.GONE);
            holder.tv_Left.setText(msg.getContent());
        } else if (msg.getType() == MSG.TYPE_SEND) {
            //如果是发送的消息,显示右边布局,隐藏左边布局
            holder.llLeft.setVisibility(View.GONE);
            holder.llRight.setVisibility(View.VISIBLE);
            holder.tv_Right.setText(msg.getContent());
        }
    }

    @Override
    public int getItemCount() {

        return mMsgList.size();
    }

    static class MyViewHolder extends RecyclerView.ViewHolder {

        RelativeLayout llLeft;
        RelativeLayout llRight;
        TextView tv_Left;
        TextView tv_Right;

        public MyViewHolder(View itemView) {
            super(itemView);
            llLeft = (RelativeLayout) itemView.findViewById(R.id.ll_msg_left);
            llRight = (RelativeLayout) itemView.findViewById(R.id.ll_msg_right);
            tv_Left = (TextView) itemView.findViewById(R.id.tv_msg_left);
            tv_Right = (TextView) itemView.findViewById(R.id.tv_msg_right);

        }
    }
}

Msg

public class MSG {
    public static final int TYPE_RECEIVED = 0;//消息的类型:接收
    public static final int TYPE_SEND = 1;    //消息的类型:发送

    private String content;//消息的内容
    private int type;      //消息的类型

    public MSG(String content, int type) {
        this.content = content;
        this.type = type;
    }

    public String getContent() {
        return content;
    }

    public int getType() {
        return type;
    }
}

最后千万别忘了注册添加权限

<?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.hx">
    <uses-permission android:name="android.permission.CAMERA"/>
    <!-- 网络 -->
    <uses-permission android:name="android.permission.INTERNET"/>
    <!-- 录音 -->
    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
    <!-- 震动 -->
    <uses-permission android:name="android.permission.VIBRATE"/>
    <!-- 访问网络状态 -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <!-- 访问WIFI状态 -->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
    <!-- 读取手机状态 -->
    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <!--唤醒锁屏-->
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    <!-- 写入外部存储 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <!-- 访问精确定位 -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <!-- 修改音频设置 -->
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
    <!-- 允许读写系统设置项 使用设置时需要 -->
    <uses-permission android:name="android.permission.WRITE_SETTINGS"/>
    <!-- 读取启动设置 -->
    <uses-permission android:name="com.android.launcher.permission.READ_SETTINGS"/>

    <!-- 音视频通话所需权限 -->
    <!-- 唤醒锁屏 -->
    <uses-permission android:name="android.permission.WAKE_LOCK"/>

    <!-- 非必需权限 -->
    <!--获取当前和最近执行的任务
    TODO 已被弃用后期可能会添加另一个权限 REAL_GET_TASKS
    TODO https://android.googlesource.com/platform/frameworks/base/+/2d7576b%5E!/-->
    <uses-permission android:name="android.permission.GET_TASKS"/>
    <!-- 读取联系人 -->
    <uses-permission android:name="android.permission.READ_CONTACTS"/>

    <!-- Demo新添加的一些属性 -->
    <!-- 访问GMail账户列表 -->
    <uses-permission android:name="android.permission.GET_ACCOUNTS"/>
    <!-- 使用证书 -->
    <uses-permission android:name="android.permission.USE_CREDENTIALS"/>
    <!-- 管理账户 -->
    <uses-permission android:name="android.permission.MANAGE_ACCOUNTS"/>
    <!-- 验证账户 -->
    <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
    <!-- 连续广播(允许一个程序收到广播后快速收到下一个广播) -->
    <uses-permission android:name="android.permission.BROADCAST_STICKY"/>
    <application

        android:name=".tongxun.ECApplication"
        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"
        tools:ignore="GoogleAppIndexingWarning">
        <activity android:name=".tongxun.ECMainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".tongxun.ECLoginActivity">
        </activity>
        <activity
            android:name=".tongxun.ECChatActivity"
            android:screenOrientation="portrait"
            android:windowSoftInputMode="adjustResize">
        </activity>
        <meta-data
            android:name="EASEMOB_APPKEY"
            android:value="你的appkey"/>
<!--         声明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>
    </application>

</manifest>

哈哈是不是很简单啊,现在晒上效果图,由于时间较短页面还是比较粗糙的

凑合看吧哈哈哈

结束语

通过一个简单的导入sdk,就可以使用环信的三方sdk功能了,是不是很强大,当然还是有很多小细节得,一定要初始化,重要的事情说三遍!!!,好了,这次的文章就到这里了,到家有问题可以问我,也可以找我要源码。

如果转载请标明链接,禁止盗版!!

  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 15
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值