Android SDK 基础功能


  • 同步方法:在调用 SDK 里面的方法时,如果方法对应的参数里面有 callback 回调,直接调用
  • 异步方法:调用 SDK 的方法里面没有对应 callback 参数,则需要开发者来异步操作

具体是异步还是同步,也可以参考相关文档介绍的方法,每一个方法后边都有注释信息提示开发者,未注释的则认为同步执行。

注:对于没有回调的异步方法执行过程,是通过 try catch 来判断异步方法执行成功或者失败。

要求在 application 的 oncreate 方法中做初始化。

EMChat.getInstance().init(applicationContext);

/**
 * debugMode == true 时为打开,SDK会在log里输入调试信息
 * @param debugMode
 * 在做代码混淆的时候需要设置成false
 */
EMChat.getInstance().setDebugMode(true);//在做打包混淆时,要关闭debug模式,避免消耗不必要的资源

注:如果你的 APP 中有第三方的服务启动,请在初始化 SDK(EMChat.getInstance().init(applicationContext))方法的前面添加以下相关代码(相应代码也可参考 Demo 的 application)。

appContext = this;
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("com.easemob.chatuidemo")) {
    Log.e(TAG, "enter the service process!");
    //"com.easemob.chatuidemo"为demo的包名,换到自己项目中要改成自己包名
    
    // 则此application::onCreate 是被service 调用的,直接返回
    return;
}

如何获取 processAppName 请参考以下方法。

private String getAppName(int pID) {
    String processName = null;
    ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
    List l = am.getRunningAppProcesses();
    Iterator i = l.iterator();
    PackageManager pm = this.getPackageManager();
    while (i.hasNext()) {
        ActivityManager.RunningAppProcessInfo info = (ActivityManager.RunningAppProcessInfo) (i.next());
        try {
            if (info.pid == pID) {
                CharSequence c = pm.getApplicationLabel(pm.getApplicationInfo(info.processName, PackageManager.GET_META_DATA));
                // Log.d("Process", "Id: "+ info.pid +" ProcessName: "+
                // info.processName +"  Label: "+c.toString());
                // processName = c.toString();
                processName = info.processName;
                return processName;
            }
        } catch (Exception e) {
            // Log.d("Process", "Error>> :"+ e.toString());
        }
    }
    return processName;
}

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

  • 只有开放注册模式下,才可以客户端注册。开放注册是为了测试使用,正式环境中不推荐使用该方式注册环信 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();

登录聊天服务器。

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

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

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

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", "登录聊天服务器失败!");
    }
});

即首次登录成功后,不需要再次调用登录方法,在下次 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
                    //当前网络不可用,请检查网络设置
                }
            }
        });
    }
}
//此方法为同步方法
EMChatManager.getInstance().logout();
//此方法为异步方法
EMChatManager.getInstance().logout(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
        
    }
});

监听事件注册(建议做成全局监听)

注册接收新消息的监听广播

//只有注册了广播才能接收到新消息,目前离线消息,在线消息都是走接收消息的广播(离线消息目前无法监听,在登录以后,接收消息广播会执行一次拿到所有的离线消息)
NewMessageBroadcastReceiver msgReceiver = new NewMessageBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter(EMChatManager.getInstance().getNewMessageBroadcastAction());
intentFilter.setPriority(3);
registerReceiver(msgReceiver, intentFilter);

private class NewMessageBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // 注销广播
        abortBroadcast();

        // 消息id(每条消息都会生成唯一的一个id,目前是SDK生成)
        String msgId = intent.getStringExtra("msgid");
        //发送方
        String username = intent.getStringExtra("from");
        // 收到这个广播的时候,message已经在db和内存里了,可以通过id获取message对象
        EMMessage message = EMChatManager.getInstance().getMessage(msgId);
        EMConversation	conversation = EMChatManager.getInstance().getConversation(username);
        // 如果是群聊消息,获取到group id
        if (message.getChatType() == ChatType.GroupChat) {
            username = message.getTo();
        }
        if (!username.equals(username)) {
            // 消息不是发给当前会话,return
            return;
        }
    }
}

注册接收 ack 回执消息的 BroadcastReceiver

EMChatManager.getInstance().getChatOptions().setRequireAck(flag)
//如果用到已读的回执需要把这个flag设置成true

IntentFilter ackMessageIntentFilter = new IntentFilter(EMChatManager.getInstance().getAckMessageBroadcastAction());
ackMessageIntentFilter.setPriority(3);
registerReceiver(ackMessageReceiver, ackMessageIntentFilter);

private BroadcastReceiver ackMessageReceiver = new BroadcastReceiver() {
    
    @Override
    public void onReceive(Context context, Intent intent) {
        abortBroadcast();
        String msgid = intent.getStringExtra("msgid");
        String from = intent.getStringExtra("from");
        EMConversation conversation = EMChatManager.getInstance().getConversation(from);
        if (conversation != null) {
            // 把message设为已读
            EMMessage msg = conversation.getMessage(msgid);
            if (msg != null) {
                msg.isAcked = true;
            }
        }
                
    }
};

注册一个消息送达的 BroadcastReceiver(在聊天界面注册)

EMChatManager.getInstance().getChatOptions().setRequireDeliveryAck(flag)
//如果用到已发送的回执需要把这个flag设置成true
     
IntentFilter deliveryAckMessageIntentFilter = new IntentFilter(EMChatManager.getInstance().getDeliveryAckMessageBroadcastAction());
deliveryAckMessageIntentFilter.setPriority(5);
registerReceiver(deliveryAckMessageReceiver, deliveryAckMessageIntentFilter);
        
/**
* 消息送达BroadcastReceiver
*/
private BroadcastReceiver deliveryAckMessageReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        abortBroadcast();

        String msgid = intent.getStringExtra("msgid");
        String from = intent.getStringExtra("from");
        EMConversation conversation = EMChatManager.getInstance().getConversation(from);
        if (conversation != null) {
            // 把message设为已读
            EMMessage msg = conversation.getMessage(msgid);
            if (msg != null) {
                msg.isDelivered = true;
            }
        }
    }
};

监听联系人的变化等

EMContactManager.getInstance().setContactListener(new MyContactListener());

private class MyContactListener implements EMContactListener {

    @Override
    public void onContactAdded(List<String> usernameList) {
        // 保存增加的联系人
            
    }

    @Override
    public void onContactDeleted(final List<String> usernameList) {
        // 被删除

    }

    @Override
    public void onContactInvited(String username, String reason) {
        // 接到邀请的消息,如果不处理(同意或拒绝),掉线后,服务器会自动再发过来,所以客户端不要重复提醒
            
    }

    @Override
    public void onContactAgreed(String username) {
        //同意好友请求
    }

    @Override
    public void onContactRefused(String username) {
        // 拒绝好友请求

    }

}

注册一个监听连接状态的 listener

  • 在聊天过程中难免会遇到网络问题,在此 SDK 为您提供了网络监听接口,实时监听
  • 对于同一个账号在多处登录,则根据本监听事件中的 onDisConnected 方法传递的 int 类型参数 error 来进行判断是否同一个账号在其它地方进行了登录和账号是否被删除,若服务器返回的参数值为 EMError.CONNECTION_CONFLICT,则认为是有同一个账号异地登录,若服务器返回的参数值为 EMError.USER_REMOVED,则是账号在后台被删除。
//注册一个监听连接状态的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
                    //当前网络不可用,请检查网络设置
                }
            }
        });
    }
}

注册群聊相关的 listener

EMGroupManager.getInstance().addGroupChangeListener(new MyGroupChangeListener());

private class MyGroupChangeListener implements GroupChangeListener {

    @Override
    public void onInvitationReceived(String groupId, String groupName,String inviter, String reason) {

        //收到加入群聊的邀请

        boolean hasGroup = false;
        for (EMGroup group : EMGroupManager.getInstance().getAllGroups()) {
            if (group.getGroupId().equals(groupId)) {
                hasGroup = true;
                break;
            }
        }
        if (!hasGroup)
            return;

        // 被邀请
        EMMessage msg = EMMessage.createReceiveMessage(Type.TXT);
        msg.setChatType(ChatType.GroupChat);
        msg.setFrom(inviter);
        msg.setTo(groupId);
        msg.setMsgId(UUID.randomUUID().toString());
        msg.addBody(new TextMessageBody(inviter + "邀请你加入了群聊"));
        // 保存邀请消息
        EMChatManager.getInstance().saveMessage(msg);
        // 提醒新消息
        EMNotifier.getInstance(getApplicationContext()).notifyOnNewMsg();

    }

    @Override
    public void onInvitationAccpted(String groupId, String inviter,
                String reason) {
            //群聊邀请被接受
    }

    @Override
    public void onInvitationDeclined(String groupId, String invitee,
                String reason) {
        //群聊邀请被拒绝
    }

    @Override
    public void onUserRemoved(String groupId, String groupName) {
        //当前用户被管理员移除出群聊
            
    }

    @Override
    public void onGroupDestroy(String groupId, String groupName) {
        //群聊被创建者解散
        // 提示用户群被解散

    }

    @Override
    public void onApplicationReceived(String groupId, String groupName,String applyer, String reason) {
        // 用户申请加入群聊,收到加群申请
    }

    @Override
    public void onApplicationAccept(String groupId, String groupName,String accepter) {
        // 加群申请被同意
        EMMessage msg = EMMessage.createReceiveMessage(Type.TXT);
        msg.setChatType(ChatType.GroupChat);
        msg.setFrom(accepter);
        msg.setTo(groupId);
        msg.setMsgId(UUID.randomUUID().toString());
        msg.addBody(new TextMessageBody(accepter + "同意了你的群聊申请"));
        // 保存同意消息
        EMChatManager.getInstance().saveMessage(msg);
        // 提醒新消息
        EMNotifier.getInstance(getApplicationContext()).notifyOnNewMsg();
    }

    @Override
    public void onApplicationDeclined(String groupId, String groupName,String decliner, String reason) {
        // 加群申请被拒绝
    }

}

注:最后要通知 SDK,UI 已经初始化完毕,注册了相应的 receiver 和 listener,可以接受 broadcast 了。

EMChat.getInstance().setAppInited();

回调监听事件注册

EMChatManager.getInstance().registerEventListener(new EMEventListener() {
            
    @Override
    public void onEvent(EMNotifierEvent event) {

        switch (event.getEvent()) {	
        case EventNewMessage: // 接收新消息
        {
        EMMessage message = (EMMessage) event.getData();
            break;
        }
        case EventDeliveryAck:{//接收已发送回执
            
            break;
        }
        
        case EventNewCMDMessage:{//接收透传消息
            
            break;
        }
        
        case EventReadAck:{//接收已读回执
            
            break;
        }

        case EventOfflineMessage: {//接收离线消息
            List<EMMessage> messages = (List<EMMessage>) event.getData();
            break;
        }

        case EventConversationListChanged: {//通知会话列表通知event注册(在某些特殊情况,SDK去删除会话的时候会收到回调监听)
            
            break;
        }
        
        default:
            break;
        }
    }
    }
});

注:以上两种方式都可以执行相同的操作,但是广播和监听事件不可同时混用。


上一页:Android SDK 导入

下一页:消息