环信官方Demo源码分析及SDK简单应用-ChatDemoUI3.0
环信官方Demo源码分析及SDK简单应用
环信官方Demo源码分析及SDK简单应用-ChatDemoUI3.0
环信官方Demo源码分析及SDK简单应用-LoginActivity
环信官方Demo源码分析及SDK简单应用-主界面的三个fragment-会话界面
环信官方Demo源码分析及SDK简单应用-主界面的三个fragment-通讯录界面
环信官方Demo源码分析及SDK简单应用-主界面的三个fragment-设置界面
环信官方Demo源码分析及SDK简单应用-EaseUI
环信官方Demo源码分析及SDK简单应用-IM集成开发详案及具体代码实现
ChatDemoUI3.0
代码结构及逻辑分析
既然上面提到首先分析ChatDemoUI 3.0,那么我们来看看其目录结构
mainfests 清单文件我们稍后来看具体内容
java 具体的代码部分,其包名为com.hyphenate.chatuidemo.
有如下子包:
解决sdk定义版本声明的问题,我们在后面如果使用到了红包的ui,出现了一些sdk的错误可以加上。
SDK常见的一大坨权限。其中Google Cloud Messaging还是别用吧,身在何处,能稳定么?然后就是各种各样的界面声明总共这么些个界面(Tips:由于本文是现阅读现写,所有未中文指出部分,后面代码阅读会去补上):[list=1]开屏页登陆页注册聊天添加好友群组邀请群组列表聊天室详情新建群组退出群组提示框群组选人PickAtUserActivity地图新的朋友邀请消息页面转发消息用户列表页面自定义的contextmenu显示下载大图页面下载文件黑名单公开的群聊列表PublicChatRoomsActivity语音通话视频通话群聊简单信息群组黑名单用户列表GroupBlacklistActivityGroupSearchMessageActivityPublicGroupsSeachActivityEditActivityEaseShowVideoActivityImageGridActivityRecorderVideoActivityDiagnoseActivityOfflinePushNickActivityrobots listRobotsActivityUserProfileActivitySetServersActivityOfflinePushSettingsActivityCallOptionActivity发红包红包详情红包记录WebView零钱绑定银行卡群成员列表支付宝h5支付页面转账页面转账详情页面再往下就是相关的一些广播接收者,服务,以及杂七杂八的东西了。有如下部分:开机自启动[list=1]GCM小米推送华为推送友盟EMChat服务EMJob服务EMMonitor Receiver百度地图服务其中比较重要的
我们来看官方文档中关于此isLoggedInBefore()的解释。
我们再回头来看刚才的代码,代码中有句注释,是这么写到。
这里做了等待和判断如果栈顶的ActivityName不为空而且顶栈的名字为语音通话的Activity或者栈顶的名字等于语音通话的Activity。毛线都不做。这个地方猜测应该是指语音通话挂起,重新调起界面的过程。否则,跳到主界面。那么我们接着看主界面。MainActivity那么这个时候,我们应该怎样去看主界面的代码呢?首先看Demo的界面,然后看代码的方法,再一一对应。来,我们来看界面,界面是这个样子的。
三个界面会话、通讯录、设置有了直观的认识以后,我们再来看代码。 我们来一段一段看代码BaseActivityMainActivity继承自BaseActivity。
这段代码其实是用来解决重复实例化Launch Activity的问题。喜欢打破砂锅问到底的,可以自己去google。至于hideSoftKeyboard则是常见的隐藏软键盘其中有一句
说实话自己英文水平不是太好,没搞懂为毛国人写的代码要用英文注释,难道是外国人开发的?注释本身不就是让人简单易懂代码逻辑的。可能跟这个公司大了,这个心理上有些关系吧。
确保当你在其他设备登陆或者登出的时候,界面不在后台。大概我只能翻译成这样了。但是看代码的意思应该是当你再其他设备登陆的时候啊,你的app又在后台,那么这个时候呢,咱啊就你在当前设备点击进来的时候,我就判断你这个saveInstanceState是不是为空。如果不为空而且得到账号已经remove 标识位为true的话,咱就把你当前的界面结束掉。跳到登陆页面去。否则的话,如果savedInstanceState不为空,而且得到isConflict标识位为true的话,也退出去跳到登陆页面。权限请求我们继续看下面的,封装了请求权限的代码。
继续,之后就是常规的界面初始化及其他设置了。
初始化界面方法initView()友盟的更新没用过友盟的东西
从字面上意思来看来应该是当账号冲突,移除,禁止的时候去显示异常。其中用到了showExceptionDialog()方法来显示我们来看看一下代码
当用户遇到一些异常的时候显示对话框,例如在其他设备登陆,账号被移除或者禁止。数据库相关操作
接受了消息了以后调用了updateUnreadLabel();和updateUnreadAddressLable();方法
未读消息数更新
如果为群红包更新意图则调用的converstationListFragment的refersh()方法
添加联系人监听
我们发现是MyContactListener是继承自EMContactListener的,我们再来看看EMContactListener和其官方文档的解释。
我们发现其定义了5个接口,这5个接口根据文档释义分别是如下含义:
如果你正在和这个删除你的人聊天就提示你这个人已把你从他好友列表里移除并且结束掉聊天界面。
测试用广播监听
其他方法
接下来我们来捡漏,看看还有剩余哪些方法没有去看。
判断当前账号是否移除
requestPermission()
initView()
界面切换方法
unregisterBroadcastReceiver();反注册广播接收者。
updateUnreadAddressLable()
getUnreadAddressCountTotal()
getUnreadMsgCountTotal()
getExceptionMessageId() 判断异常的种类
getUnreadAddressCountTotal()
getUnreadMsgCountTotal()
onResume() 中做了一些例如更新未读应用事件消息,并且push当前Activity到easui的ActivityList中
onSaveInstanceState
onKeyDown();判断按了回退的时候。 moveTaskToBack(false);仅当前Activity为task根时,将activity退到后台而非finish();
showExceptionDialog()
showExceptionDialogFromIntent()
onNewIntent() Activity在singleTask模式下重用该实例,onNewIntent()->onResart()->onStart()->onResume()这么个顺序原地复活。
至此,我们的MainActivity就全部阅读完毕了。
我们是在已经登录的情况下来到的MainActivity,那么我们在没有登录情况下呢,当然是来登陆页面。下面我们来看登录页。
环信官方Demo源码分析及SDK简单应用-LoginActivity
环信官方Demo源码分析及SDK简单应用-ChatDemoUI3.0
环信官方Demo源码分析及SDK简单应用-LoginActivity
环信官方Demo源码分析及SDK简单应用-主界面的三个fragment-会话界面
环信官方Demo源码分析及SDK简单应用-主界面的三个fragment-通讯录界面
环信官方Demo源码分析及SDK简单应用-主界面的三个fragment-设置界面
环信官方Demo源码分析及SDK简单应用-EaseUI
环信官方Demo源码分析及SDK简单应用-IM集成开发详案及具体代码实现
ChatDemoUI3.0
代码结构及逻辑分析
既然上面提到首先分析ChatDemoUI 3.0,那么我们来看看其目录结构
mainfests 清单文件我们稍后来看具体内容
java 具体的代码部分,其包名为com.hyphenate.chatuidemo.
有如下子包:
- adapter 适配器
- db 数据库相关
- domain 实体相关
- parse 第三方库 parse(用于存储 Demo 中用户的信息)管理包
- receiver 广播接收者
- runtimepermissions 运行时权限相关
- ui 界面部分
- utils 工具类
- video.util 视频录制工具包
- widget 自定义view
- Constant 常量类
- DemoApplication application
- DemoHelper Demo的帮助类
- DemoModel 逻辑相关类
- DemoApplication:继承于系统的 Application 类,其 onCreate() 为整个程序的入口,相关的初始化操作都在这里面;
- DemoHelper: Demo 全局帮助类,主要功能为初始化 EaseUI、环信 SDK 及 Demo 相关的实例,以及封装一些全局使用的方法;
- MainActivity: 主页面,包含会话列表页面(ConversationListFragment)、联系人列表页(ContactListFragment)、设置页面(SettingsFragment),前两个继承自己 EaseUI 中的 fragment;
- ChatActivity: 会话页面,这个类代码很少,主要原因是大部分逻辑写在 ChatFragment 中。ChatFragment 继承自 EaseChatFragment,做成 fragment 的好处在于用起来更灵活,可以单独作为一个页面使用,也可以和其他 fragment 一起放到一个 Activity 中;
- GroupDetailsActivity: 群组详情页面
- 分包挺清晰
- 抓住了DemoHelper和DemoModel也就抓住了整个的纲领
- 其他的你就自己扯吧。
- AndroidMainfest.xml
- DemoApplication
- SplashActivity
- 各流程类
解决sdk定义版本声明的问题,我们在后面如果使用到了红包的ui,出现了一些sdk的错误可以加上。
SDK常见的一大坨权限。其中Google Cloud Messaging还是别用吧,身在何处,能稳定么?然后就是各种各样的界面声明总共这么些个界面(Tips:由于本文是现阅读现写,所有未中文指出部分,后面代码阅读会去补上):[list=1]开屏页登陆页注册聊天添加好友群组邀请群组列表聊天室详情新建群组退出群组提示框群组选人PickAtUserActivity地图新的朋友邀请消息页面转发消息用户列表页面自定义的contextmenu显示下载大图页面下载文件黑名单公开的群聊列表PublicChatRoomsActivity语音通话视频通话群聊简单信息群组黑名单用户列表GroupBlacklistActivityGroupSearchMessageActivityPublicGroupsSeachActivityEditActivityEaseShowVideoActivityImageGridActivityRecorderVideoActivityDiagnoseActivityOfflinePushNickActivityrobots listRobotsActivityUserProfileActivitySetServersActivityOfflinePushSettingsActivityCallOptionActivity发红包红包详情红包记录WebView零钱绑定银行卡群成员列表支付宝h5支付页面转账页面转账详情页面再往下就是相关的一些广播接收者,服务,以及杂七杂八的东西了。有如下部分:开机自启动[list=1]GCM小米推送华为推送友盟EMChat服务EMJob服务EMMonitor Receiver百度地图服务其中比较重要的
<!-- 设置环信应用的appkey --> <meta-data android:name="EASEMOB_APPKEY" android:value="你自己的环信Key" />这样,我们基本AndroidMainfest就阅读完了。因为Androidmainfest.xml指出主Activity为ui包下的SplashActivity。按理说我们应该接着来看SplashActivity。但是别忘了App启动后DemoApplication是在主界面之前的。我们将在阅读完Application后再来看SplashActivity。DemoApplication上代码:
/** * Copyright (C) 2016 Hyphenate Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.hyphenate.chatuidemo;import android.app.Application;import android.content.Context;import android.support.multidex.MultiDex;import com.easemob.redpacketsdk.RedPacket;public class DemoApplication extends Application { public static Context applicationContext; private static DemoApplication instance; // login user name public final String PREF_USERNAME = "username"; /** * nickname for current user, the nickname instead of ID be shown when user receive notification from APNs */ public static String currentUserNick = ""; @Override public void onCreate() { MultiDex.install(this); super.onCreate(); applicationContext = this; instance = this; //init demo helper DemoHelper.getInstance().init(applicationContext); //red packet code : 初始化红包上下文,开启日志输出开关 RedPacket.getInstance().initContext(applicationContext); RedPacket.getInstance().setDebugMode(true); //end of red packet code } public static DemoApplication getInstance() { return instance; } @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); MultiDex.install(this); }}第一句是分包,我们知道分包有以下两种方式:[list=1]项目中的Application类继承MultiDexApplication。在自己的Application类的attachBaseContext方法中调用MultiDex.install(this);。然后又做了几件事[list=1]初始化DemoHelper初始化红包并开启日志输出Application就这样没了,我们继续看SplashActivity。SplashActivity我们来看代码:
package com.hyphenate.chatuidemo.ui;import android.content.Intent;import android.os.Bundle;import android.view.animation.AlphaAnimation;import android.widget.RelativeLayout;import android.widget.TextView;import com.hyphenate.chat.EMClient;import com.hyphenate.chatuidemo.DemoHelper;import com.hyphenate.chatuidemo.R;import com.hyphenate.util.EasyUtils;/** * 开屏页 * */public class SplashActivity extends BaseActivity { private static final int sleepTime = 2000; @Override protected void onCreate(Bundle arg0) { setContentView(R.layout.em_activity_splash); super.onCreate(arg0); RelativeLayout rootLayout = (RelativeLayout) findViewById(R.id.splash_root); TextView versionText = (TextView) findViewById(R.id.tv_version); versionText.setText(getVersion()); AlphaAnimation animation = new AlphaAnimation(0.3f, 1.0f); animation.setDuration(1500); rootLayout.startAnimation(animation); } @Override protected void onStart() { super.onStart(); new Thread(new Runnable() { public void run() { if (DemoHelper.getInstance().isLoggedIn()) { // auto login mode, make sure all group and conversation is loaed before enter the main screen long start = System.currentTimeMillis(); EMClient.getInstance().chatManager().loadAllConversations(); EMClient.getInstance().groupManager().loadAllGroups(); long costTime = System.currentTimeMillis() - start; //wait if (sleepTime - costTime > 0) { try { Thread.sleep(sleepTime - costTime); } catch (InterruptedException e) { e.printStackTrace(); } } String topActivityName = EasyUtils.getTopActivityName(EMClient.getInstance().getContext()); if (topActivityName != null && (topActivityName.equals(VideoCallActivity.class.getName()) || topActivityName.equals(VoiceCallActivity.class.getName()))) { // nop // avoid main screen overlap Calling Activity } else { //enter main screen startActivity(new Intent(SplashActivity.this, MainActivity.class)); } finish(); }else { try { Thread.sleep(sleepTime); } catch (InterruptedException e) { } startActivity(new Intent(SplashActivity.this, LoginActivity.class)); finish(); } } }).start(); } /** * get sdk version */ private String getVersion() { return EMClient.getInstance().VERSION; }}UI部分我们不关心,我们来看下代码逻辑部分
new Thread(new Runnable() { public void run() { if (DemoHelper.getInstance().isLoggedIn()) { // auto login mode, make sure all group and conversation is loaed before enter the main screen long start = System.currentTimeMillis(); EMClient.getInstance().chatManager().loadAllConversations(); EMClient.getInstance().groupManager().loadAllGroups(); long costTime = System.currentTimeMillis() - start; //wait if (sleepTime - costTime > 0) { try { Thread.sleep(sleepTime - costTime); } catch (InterruptedException e) { e.printStackTrace(); } } String topActivityName = EasyUtils.getTopActivityName(EMClient.getInstance().getContext()); if (topActivityName != null && (topActivityName.equals(VideoCallActivity.class.getName()) || topActivityName.equals(VoiceCallActivity.class.getName()))) { // nop // avoid main screen overlap Calling Activity } else { //enter main screen startActivity(new Intent(SplashActivity.this, MainActivity.class)); } finish(); }else { try { Thread.sleep(sleepTime); } catch (InterruptedException e) { } startActivity(new Intent(SplashActivity.this, LoginActivity.class)); finish(); } } }).start();在这里,我们看到了这个DemoHelper帮助类,起了个线程,判断是否已经登录。我们来看看他是如何判断的。
我们来看官方文档中关于此isLoggedInBefore()的解释。
我们再回头来看刚才的代码,代码中有句注释,是这么写到。
// auto login mode, make sure all group and conversation is loaed before enter the main screen自动登录模式,请确保进入主页面后本地回话和群组都load完毕。那么代码中有两句话就是干这个事情的
EMClient.getInstance().chatManager().loadAllConversations();EMClient.getInstance().groupManager().loadAllGroups();这里部分代码最好是放在SplashActivity因为如果登录过,APP 长期在后台再进的时候也可能会导致加载到内存的群组和会话为空。
这里做了等待和判断如果栈顶的ActivityName不为空而且顶栈的名字为语音通话的Activity或者栈顶的名字等于语音通话的Activity。毛线都不做。这个地方猜测应该是指语音通话挂起,重新调起界面的过程。否则,跳到主界面。那么我们接着看主界面。MainActivity那么这个时候,我们应该怎样去看主界面的代码呢?首先看Demo的界面,然后看代码的方法,再一一对应。来,我们来看界面,界面是这个样子的。
三个界面会话、通讯录、设置有了直观的认识以后,我们再来看代码。 我们来一段一段看代码BaseActivityMainActivity继承自BaseActivity。
/** * Copyright (C) 2016 Hyphenate Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.hyphenate.chatuidemo.ui;import android.annotation.SuppressLint;import android.os.Bundle;import com.hyphenate.easeui.ui.EaseBaseActivity;import com.umeng.analytics.MobclickAgent;@SuppressLint("Registered")public class BaseActivity extends EaseBaseActivity { @Override protected void onCreate(Bundle arg0) { super.onCreate(arg0); } @Override protected void onResume() { super.onResume(); // umeng MobclickAgent.onResume(this); } @Override protected void onStart() { super.onStart(); // umeng MobclickAgent.onPause(this); }}只有友盟的一些数据埋点,我们继续往上挖看他爹。EaseBaseActivity
/** * Copyright (C) 2016 Hyphenate Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.hyphenate.easeui.ui;import android.annotation.SuppressLint;import android.content.Context;import android.content.Intent;import android.os.Bundle;import android.support.v4.app.FragmentActivity;import android.view.View;import android.view.WindowManager;import android.view.inputmethod.InputMethodManager;import com.hyphenate.easeui.controller.EaseUI;@SuppressLint({"NewApi", "Registered"})public class EaseBaseActivity extends FragmentActivity { protected InputMethodManager inputMethodManager; @Override protected void onCreate(Bundle arg0) { super.onCreate(arg0); //http://stackoverflow.com/questions/4341600/how-to-prevent-multiple-instances-of-an-activity-when-it-is-launched-with-differ/ // should be in launcher activity, but all app use this can avoid the problem if(!isTaskRoot()){ Intent intent = getIntent(); String action = intent.getAction(); if(intent.hasCategory(Intent.CATEGORY_LAUNCHER) && action.equals(Intent.ACTION_MAIN)){ finish(); return; } } inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); } @Override protected void onResume() { super.onResume(); // cancel the notification EaseUI.getInstance().getNotifier().reset(); } protected void hideSoftKeyboard() { if (getWindow().getAttributes().softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN) { if (getCurrentFocus() != null) inputMethodManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); } } /** * back * * @param view */ public void back(View view) { finish(); }}
这段代码其实是用来解决重复实例化Launch Activity的问题。喜欢打破砂锅问到底的,可以自己去google。至于hideSoftKeyboard则是常见的隐藏软键盘其中有一句
EaseUI.getInstance().getNotifier().reset();其中Notifier()为新消息提醒类,reset()方法调用了resetNotificationCount()和cancelNotificaton()。重置消息提醒数和取消提醒。
public void reset(){ resetNotificationCount(); cancelNotificaton(); } void resetNotificationCount() { notificationNum = 0; fromUsers.clear(); } void cancelNotificaton() { if (notificationManager != null) notificationManager.cancel(notifyID); }耗电优化首先判断系统版本是否大于6.0,如果是,则判断是否忽略电池耗电的优化。
说实话自己英文水平不是太好,没搞懂为毛国人写的代码要用英文注释,难道是外国人开发的?注释本身不就是让人简单易懂代码逻辑的。可能跟这个公司大了,这个心理上有些关系吧。
确保当你在其他设备登陆或者登出的时候,界面不在后台。大概我只能翻译成这样了。但是看代码的意思应该是当你再其他设备登陆的时候啊,你的app又在后台,那么这个时候呢,咱啊就你在当前设备点击进来的时候,我就判断你这个saveInstanceState是不是为空。如果不为空而且得到账号已经remove 标识位为true的话,咱就把你当前的界面结束掉。跳到登陆页面去。否则的话,如果savedInstanceState不为空,而且得到isConflict标识位为true的话,也退出去跳到登陆页面。权限请求我们继续看下面的,封装了请求权限的代码。
继续,之后就是常规的界面初始化及其他设置了。
初始化界面方法initView()友盟的更新没用过友盟的东西
MobclickAgent.updateOnlineConfig(this);UmengUpdateAgent.setUpdateOnlyWifi(false);UmengUpdateAgent.update(this);看字面意思第一句应该是点击数据埋起点,后面应该是设置仅wifi更新为false以及设置更新。异常提示从Intent中获取的异常标志位进行一个弹窗提示
从字面上意思来看来应该是当账号冲突,移除,禁止的时候去显示异常。其中用到了showExceptionDialog()方法来显示我们来看看一下代码
当用户遇到一些异常的时候显示对话框,例如在其他设备登陆,账号被移除或者禁止。数据库相关操作
inviteMessgeDao = new InviteMessgeDao(this);UserDao userDao = new UserDao(this);初始化Fragment
conversationListFragment = new ConversationListFragment(); contactListFragment = new ContactListFragment(); SettingsFragment settingFragment = new SettingsFragment(); fragments = new Fragment { conversationListFragment, contactListFragment, settingFragment}; getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, conversationListFragment) .add(R.id.fragment_container, contactListFragment).hide(contactListFragment).show(conversationListFragment) .commit();注册广播接收者
//register broadcast receiver to receive the change of group from DemoHelperregisterBroadcastReceiver();从英文注释来看,字面意思来看是用DemoHelper来注册广播接收者来接受群变化通知。我们来看具体的代码
private void registerBroadcastReceiver() { broadcastManager = LocalBroadcastManager.getInstance(this); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Constant.ACTION_CONTACT_CHANAGED); intentFilter.addAction(Constant.ACTION_GROUP_CHANAGED); intentFilter.addAction(RPConstant.REFRESH_GROUP_RED_PACKET_ACTION); broadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { updateUnreadLabel(); updateUnreadAddressLable(); if (currentTabIndex == 0) { // refresh conversation list if (conversationListFragment != null) { conversationListFragment.refresh(); } } else if (currentTabIndex == 1) { if(contactListFragment != null) { contactListFragment.refresh(); } } String action = intent.getAction(); if(action.equals(Constant.ACTION_GROUP_CHANAGED)){ if (EaseCommonUtils.getTopActivity(MainActivity.this).equals(GroupsActivity.class.getName())) { GroupsActivity.instance.onResume(); } } //red packet code : 处理红包回执透传消息 if (action.equals(RPConstant.REFRESH_GROUP_RED_PACKET_ACTION)){ if (conversationListFragment != null){ conversationListFragment.refresh(); } } //end of red packet code } }; broadcastManager.registerReceiver(broadcastReceiver, intentFilter); }LocalBroadcastManager是Android Support包提供了一个工具,是用来在同一个应用内的不同组件间发送Broadcast的。使用LocalBroadcastManager有如下好处:发送的广播只会在自己App内传播,不会泄露给其他App,确保隐私数据不会泄露 其他App也无法向你的App发送该广播,不用担心其他App会来搞破坏 比系统全局广播更加高效 拦截了这么几种广播,按字面意思,应该是这么几类
- Constant.ACTION_CONTACT_CHANAGED 联系人变化广播
- Constant.ACTION_GROUP_CHANAGED 群组变化广播
- RPConstant.REFRESH_GROUP_RED_PACKET_ACTION 刷新群红包广播
接受了消息了以后调用了updateUnreadLabel();和updateUnreadAddressLable();方法
未读消息数更新
更新总计未读数量
/**
* update unread message count
*/
public void updateUnreadLabel() {
int count = getUnreadMsgCountTotal();
if (count > 0) {
unreadLabel.setText(String.valueOf(count));
unreadLabel.setVisibility(View.VISIBLE);
} else {
unreadLabel.setVisibility(View.INVISIBLE);
}
}
/**然后判断广播类型,如果当前的栈顶为主界面,则调用GroupsActivity的onResume方法。
* update the total unread count
*/
public void updateUnreadAddressLable() {
runOnUiThread(new Runnable() {
public void run() {
int count = getUnreadAddressCountTotal();
if (count > 0) {
unreadAddressLable.setVisibility(View.VISIBLE);
} else {
unreadAddressLable.setVisibility(View.INVISIBLE);
}
}
});
}
如果为群红包更新意图则调用的converstationListFragment的refersh()方法
添加联系人监听
EMClient.getInstance().contactManager().setContactListener(new MyContactListener());我们来看下这个MyContactListener()监听方法。
我们发现是MyContactListener是继承自EMContactListener的,我们再来看看EMContactListener和其官方文档的解释。
我们发现其定义了5个接口,这5个接口根据文档释义分别是如下含义:
void onContactAdded (String username)//增加联系人时回调此方法从而我们得知,我们demo中的自定义监听接口在被删除回调时,做了如下操作:
void onContactDeleted (String username)//被删除时回调此方法
void onContactInvited (String username, String reason)/**收到好友邀请 参数 username 发起加为好友用户的名称 reason 对方发起好友邀请时发出的文字性描述*/
void onFriendRequestAccepted (String username)//对方同意好友请求
void onFriendRequestDeclined (String username)//对方拒绝好友请求
如果你正在和这个删除你的人聊天就提示你这个人已把你从他好友列表里移除并且结束掉聊天界面。
测试用广播监听
//debug purpose only
registerInternalDebugReceiver();
/**至此MainActivity的OnCreate方法中所有涉及到的代码我们均已看完。
* debug purpose only, you can ignore this
*/
private void registerInternalDebugReceiver() {
internalDebugReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
DemoHelper.getInstance().logout(false,new EMCallBack() {
@Override
public void onSuccess() {
runOnUiThread(new Runnable() {
public void run() {
finish();
startActivity(new Intent(MainActivity.this, LoginActivity.class));
}
});
}
@Override
public void onProgress(int progress, String status) {}
@Override
public void onError(int code, String message) {}
});
}
};
IntentFilter filter = new IntentFilter(getPackageName() + ".em_internal_debug");
registerReceiver(internalDebugReceiver, filter);
}
其他方法
接下来我们来捡漏,看看还有剩余哪些方法没有去看。
判断当前账号是否移除
/**oncreate()
* check if current user account was remove
*/
public boolean getCurrentAccountRemoved() {
return isCurrentAccountRemoved;
}
requestPermission()
initView()
界面切换方法
/**消息刷新
* on tab clicked
*
* @param view
*/
public void onTabClicked(View view) {
switch (view.getId()) {
case R.id.btn_conversation:
index = 0;
break;
case R.id.btn_address_list:
index = 1;
break;
case R.id.btn_setting:
index = 2;
break;
}
if (currentTabIndex != index) {
FragmentTransaction trx = getSupportFragmentManager().beginTransaction();
trx.hide(fragments[currentTabIndex]);
if (!fragments[index].isAdded()) {
trx.add(R.id.fragment_container, fragments[index]);
}
trx.show(fragments[index]).commit();
}
mTabs[currentTabIndex].setSelected(false);
// set current tab selected
mTabs[index].setSelected(true);
currentTabIndex = index;
}
private void refreshUIWithMessage() {registerBroadcastReceiver()
runOnUiThread(new Runnable() {
public void run() {
// refresh unread count
updateUnreadLabel();
if (currentTabIndex == 0) {
// refresh conversation list
if (conversationListFragment != null) {
conversationListFragment.refresh();
}
}
}
});
}
unregisterBroadcastReceiver();反注册广播接收者。
onDestory()
private void unregisterBroadcastReceiver(){
broadcastManager.unregisterReceiver(broadcastReceiver);
}
@Override异常的弹窗disimiss及置空,反注册广播接收者。
protected void onDestroy() {
super.onDestroy();
if (exceptionBuilder != null) {
exceptionBuilder.create().dismiss();
exceptionBuilder = null;
isExceptionDialogShow = false;
}
unregisterBroadcastReceiver();
try {
unregisterReceiver(internalDebugReceiver);
} catch (Exception e) {
}
}
updateUnreadAddressLable()
getUnreadAddressCountTotal()
getUnreadMsgCountTotal()
getExceptionMessageId() 判断异常的种类
showExceptionDialog()
private int getExceptionMessageId(String exceptionType) {
if(exceptionType.equals(Constant.ACCOUNT_CONFLICT)) {
return R.string.connect_conflict;
} else if (exceptionType.equals(Constant.ACCOUNT_REMOVED)) {
return R.string.em_user_remove;
} else if (exceptionType.equals(Constant.ACCOUNT_FORBIDDEN)) {
return R.string.user_forbidden;
}
return R.string.Network_error;
}
getUnreadAddressCountTotal()
getUnreadMsgCountTotal()
onResume() 中做了一些例如更新未读应用事件消息,并且push当前Activity到easui的ActivityList中
onStop();
@Override
protected void onResume() {
super.onResume();
if (!isConflict && !isCurrentAccountRemoved) {
updateUnreadLabel();
updateUnreadAddressLable();
}
// unregister this event listener when this activity enters the
// background
DemoHelper sdkHelper = DemoHelper.getInstance();
sdkHelper.pushActivity(this);
EMClient.getInstance().chatManager().addMessageListener(messageListener);
}
@Override做了一些销毁的活。
protected void onStop() {
EMClient.getInstance().chatManager().removeMessageListener(messageListener);
DemoHelper sdkHelper = DemoHelper.getInstance();
sdkHelper.popActivity(this);
super.onStop();
}
onSaveInstanceState
@Override存一下冲突和账户移除的标志位
protected void onSaveInstanceState(Bundle outState) {
outState.putBoolean("isConflict", isConflict);
outState.putBoolean(Constant.ACCOUNT_REMOVED, isCurrentAccountRemoved);
super.onSaveInstanceState(outState);
}
onKeyDown();判断按了回退的时候。 moveTaskToBack(false);仅当前Activity为task根时,将activity退到后台而非finish();
@OverridegetExceptionMessageId()
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
moveTaskToBack(false);
return true;
}
return super.onKeyDown(keyCode, event);
}
showExceptionDialog()
showExceptionDialogFromIntent()
onNewIntent() Activity在singleTask模式下重用该实例,onNewIntent()->onResart()->onStart()->onResume()这么个顺序原地复活。
至此,我们的MainActivity就全部阅读完毕了。
我们是在已经登录的情况下来到的MainActivity,那么我们在没有登录情况下呢,当然是来登陆页面。下面我们来看登录页。
环信官方Demo源码分析及SDK简单应用-LoginActivity