環(huán)信官方Demo源碼分析及SDK簡(jiǎn)單應(yīng)用
環(huán)信官方Demo源碼分析及SDK簡(jiǎn)單應(yīng)用-ChatDemoUI3.0
環(huán)信官方Demo源碼分析及SDK簡(jiǎn)單應(yīng)用-LoginActivity
環(huán)信官方Demo源碼分析及SDK簡(jiǎn)單應(yīng)用-主界面的三個(gè)fragment-會(huì)話界面
環(huán)信官方Demo源碼分析及SDK簡(jiǎn)單應(yīng)用-主界面的三個(gè)fragment-通訊錄界面
環(huán)信官方Demo源碼分析及SDK簡(jiǎn)單應(yīng)用-主界面的三個(gè)fragment-設(shè)置界面
環(huán)信官方Demo源碼分析及SDK簡(jiǎn)單應(yīng)用-EaseUI
環(huán)信官方Demo源碼分析及SDK簡(jiǎn)單應(yīng)用-IM集成開發(fā)詳案及具體代碼實(shí)現(xiàn)
ChatDemoUI3.0
代碼結(jié)構(gòu)及邏輯分析
既然上面提到首先分析ChatDemoUI 3.0,那么我們來看看其目錄結(jié)構(gòu)
mainfests 清單文件我們稍后來看具體內(nèi)容
java 具體的代碼部分,其包名為com.hyphenate.chatuidemo.
有如下子包:
adapter 適配器
db 數(shù)據(jù)庫(kù)相關(guān)
domain 實(shí)體相關(guān)
parse 第三方庫(kù) parse(用于存儲(chǔ) Demo 中用戶的信息)管理包
receiver 廣播接收者
runtimepermissions 運(yùn)行時(shí)權(quán)限相關(guān)
ui 界面部分
utils 工具類
video.util 視頻錄制工具包
widget 自定義view
另有如下單獨(dú)非子包類:
Constant 常量類
DemoApplication application
DemoHelper Demo的幫助類
DemoModel 邏輯相關(guān)類
其中主要類有這么幾個(gè)
DemoApplication:繼承于系統(tǒng)的 Application 類,其 onCreate() 為整個(gè)程序的入口,相關(guān)的初始化操作都在這里面;
DemoHelper: Demo 全局幫助類,主要功能為初始化 EaseUI、環(huán)信 SDK 及 Demo 相關(guān)的實(shí)例,以及封裝一些全局使用的方法;
MainActivity: 主頁面,包含會(huì)話列表頁面(ConversationListFragment)、聯(lián)系人列表頁(ContactListFragment)、設(shè)置頁面(SettingsFragment),前兩個(gè)繼承自己 EaseUI 中的 fragment;
ChatActivity: 會(huì)話頁面,這個(gè)類代碼很少,主要原因是大部分邏輯寫在 ChatFragment 中。ChatFragment 繼承自 EaseChatFragment,做成 fragment 的好處在于用起來更靈活,可以單獨(dú)作為一個(gè)頁面使用,也可以和其他 fragment 一起放到一個(gè) Activity 中;
GroupDetailsActivity: 群組詳情頁面
我們通過代碼結(jié)構(gòu)能得到什么信息?可能你會(huì)有以下的比較直觀的感受。 ?
分包挺清晰
抓住了DemoHelper和DemoModel也就抓住了整個(gè)的綱領(lǐng)
其他的你就自己扯吧。
廢話不多說,我們來看代碼。
我們的閱讀的順序是這樣的
AndroidMainfest.xml
DemoApplication
SplashActivity
各流程類
AndroidMainfest.xml
實(shí)際上沒什么好說的,不過我們還是細(xì)細(xì)的研究一番
解決sdk定義版本聲明的問題,我們?cè)诤竺嫒绻褂玫搅思t包的ui,出現(xiàn)了一些sdk的錯(cuò)誤可以加上。
SDK常見的一大坨權(quán)限。其中Google Cloud Messaging還是別用吧,身在何處,能穩(wěn)定么?
然后就是各種各樣的界面聲明
總共這么些個(gè)界面(Tips:由于本文是現(xiàn)閱讀現(xiàn)寫,所有未中文指出部分,后面代碼閱讀會(huì)去補(bǔ)上):
開屏頁
登陸頁
注冊(cè)
聊天
添加好友
群組邀請(qǐng)
群組列表
聊天室詳情
新建群組
退出群組提示框
群組選人
PickAtUserActivity
地圖
新的朋友邀請(qǐng)消息頁面
轉(zhuǎn)發(fā)消息用戶列表頁面
自定義的contextmenu
顯示下載大圖頁面
下載文件
黑名單
公開的群聊列表
PublicChatRoomsActivity
語音通話
視頻通話
群聊簡(jiǎn)單信息
群組黑名單用戶列表
GroupBlacklistActivity
GroupSearchMessageActivity
PublicGroupsSeachActivity
EditActivity
EaseShowVideoActivity
ImageGridActivity
RecorderVideoActivity
DiagnoseActivity
OfflinePushNickActivity
robots list
RobotsActivity
UserProfileActivity
SetServersActivity
OfflinePushSettingsActivity
CallOptionActivity
發(fā)紅包
紅包詳情
紅包記錄
WebView
零錢
綁定銀行卡
群成員列表
支付寶h5支付頁面
轉(zhuǎn)賬頁面
轉(zhuǎn)賬詳情頁面
再往下就是相關(guān)的一些廣播接收者,服務(wù),以及雜七雜八的東西了。有如下部分:開機(jī)自啟動(dòng)
GCM
小米推送
華為推送
友盟
EMChat服務(wù)
EMJob服務(wù)
EMMonitor Receiver
百度地圖服務(wù)
其中比較重要的
android:name="EASEMOB_APPKEY"
android:value="你自己的環(huán)信Key" />
這樣,我們基本AndroidMainfest就閱讀完了。因?yàn)锳ndroidmainfest.xml指出主Activity為ui包下的SplashActivity。按理說我們應(yīng)該接著來看SplashActivity。但是別忘了App啟動(dòng)后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 : 初始化紅包上下文,開啟日志輸出開關(guān)? ? ? ? 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);? ? }}
第一句是分包,我們知道分包有以下兩種方式:
項(xiàng)目中的Application類繼承MultiDexApplication。
在自己的Application類的attachBaseContext方法中調(diào)用MultiDex.install(this);。
然后又做了幾件事
初始化DemoHelper
初始化紅包并開啟日志輸出
Application就這樣沒了,我們繼續(xù)看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部分我們不關(guān)心,我們來看下代碼邏輯部分
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();
在這里,我們看到了這個(gè)DemoHelper幫助類,起了個(gè)線程,判斷是否已經(jīng)登錄。我們來看看他是如何判斷的。
我們來看官方文檔中關(guān)于此isLoggedInBefore()的解釋。
我們?cè)倩仡^來看剛才的代碼,代碼中有句注釋,是這么寫到。
// auto login mode, make sure all group and conversation is loaed before enter the main screen
自動(dòng)登錄模式,請(qǐng)確保進(jìn)入主頁面后本地回話和群組都load完畢。
那么代碼中有兩句話就是干這個(gè)事情的
EMClient.getInstance().chatManager().loadAllConversations();
EMClient.getInstance().groupManager().loadAllGroups();
這里部分代碼最好是放在SplashActivity因?yàn)槿绻卿涍^,APP 長(zhǎng)期在后臺(tái)再進(jìn)的時(shí)候也可能會(huì)導(dǎo)致加載到內(nèi)存的群組和會(huì)話為空。
這里做了等待和判斷
如果棧頂?shù)腁ctivityName不為空而且頂棧的名字為語音通話的Activity或者棧頂?shù)拿值扔谡Z音通話的Activity。毛線都不做。這個(gè)地方猜測(cè)應(yīng)該是指語音通話掛起,重新調(diào)起界面的過程。
否則,跳到主界面。
那么我們接著看主界面。
MainActivity
那么這個(gè)時(shí)候,我們應(yīng)該怎樣去看主界面的代碼呢?
首先看Demo的界面,然后看代碼的方法,再一一對(duì)應(yīng)。
來,我們來看界面,界面是這個(gè)樣子的。
三個(gè)界面
會(huì)話、通訊錄、設(shè)置
有了直觀的認(rèn)識(shí)以后,我們?cè)賮砜创a。
我們來一段一段看代碼
BaseActivity
MainActivity繼承自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);? ? }?}
只有友盟的一些數(shù)據(jù)埋點(diǎn),我們繼續(xù)往上挖看他爹。
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/quest ... ffer/// 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();? ? }}
這段代碼其實(shí)是用來解決重復(fù)實(shí)例化Launch Activity的問題。喜歡打破砂鍋問到底的,可以自己去google。
至于hideSoftKeyboard則是常見的隱藏軟鍵盤
其中有一句
EaseUI.getInstance().getNotifier().reset();
其中Notifier()為新消息提醒類,reset()方法調(diào)用了resetNotificationCount()和cancelNotificaton()。重置消息提醒數(shù)和取消提醒。
public void reset(){
resetNotificationCount();
cancelNotificaton();
}
?
void resetNotificationCount() {
notificationNum = 0;
fromUsers.clear();
}
void cancelNotificaton() {
if (notificationManager != null)
notificationManager.cancel(notifyID);
}
耗電優(yōu)化
首先判斷系統(tǒng)版本是否大于6.0,如果是,則判斷是否忽略電池耗電的優(yōu)化。
說實(shí)話自己英文水平不是太好,沒搞懂為毛國(guó)人寫的代碼要用英文注釋,難道是外國(guó)人開發(fā)的?
注釋本身不就是讓人簡(jiǎn)單易懂代碼邏輯的??赡芨@個(gè)公司大了,這個(gè)心理上有些關(guān)系吧。
確保當(dāng)你在其他設(shè)備登陸或者登出的時(shí)候,界面不在后臺(tái)。大概我只能翻譯成這樣了。
但是看代碼的意思應(yīng)該是當(dāng)你再其他設(shè)備登陸的時(shí)候啊,你的app又在后臺(tái),那么這個(gè)時(shí)候呢,咱啊就你在當(dāng)前
設(shè)備點(diǎn)擊進(jìn)來的時(shí)候,我就判斷你這個(gè)saveInstanceState是不是為空。如果不為空而且得到賬號(hào)已經(jīng)
remove 標(biāo)識(shí)位為true的話,咱就把你當(dāng)前的界面結(jié)束掉。跳到登陸頁面去。
否則的話,如果savedInstanceState不為空,而且得到isConflict標(biāo)識(shí)位為true的話,也退出去跳到登陸頁面。
權(quán)限請(qǐng)求
我們繼續(xù)看下面的,封裝了請(qǐng)求權(quán)限的代碼。
繼續(xù),之后就是常規(guī)的界面初始化及其他設(shè)置了。
初始化界面方法
initView()
友盟的更新
沒用過友盟的東西
MobclickAgent.updateOnlineConfig(this);
?
UmengUpdateAgent.setUpdateOnlyWifi(false);
?
UmengUpdateAgent.update(this);
看字面意思第一句應(yīng)該是點(diǎn)擊數(shù)據(jù)埋起點(diǎn),后面應(yīng)該是設(shè)置僅wifi更新為false以及設(shè)置更新。
異常提示
從Intent中獲取的異常標(biāo)志位進(jìn)行一個(gè)彈窗提示
從字面上意思來看來應(yīng)該是當(dāng)賬號(hào)沖突,移除,禁止的時(shí)候去顯示異常。其中用到了showExceptionDialog()方法來顯示
我們來看看一下代碼
當(dāng)用戶遇到一些異常的時(shí)候顯示對(duì)話框,例如在其他設(shè)備登陸,賬號(hào)被移除或者禁止。
數(shù)據(jù)庫(kù)相關(guān)操作
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();
注冊(cè)廣播接收者?
//register broadcast receiver to receive the change of group from DemoHelper
registerBroadcastReceiver();
從英文注釋來看,字面意思來看是用DemoHelper來注冊(cè)廣播接收者來接受群變化通知。我們來看具體的代碼
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 : 處理紅包回執(zhí)透?jìng)飨?/p>
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包提供了一個(gè)工具,是用來在同一個(gè)應(yīng)用內(nèi)的不同組件間發(fā)送Broadcast的。
使用LocalBroadcastManager有如下好處:
發(fā)送的廣播只會(huì)在自己App內(nèi)傳播,不會(huì)泄露給其他App,確保隱私數(shù)據(jù)不會(huì)泄露 其他App也無法向你的App發(fā)送該廣播,不用擔(dān)心其他App會(huì)來搞破壞 比系統(tǒng)全局廣播更加高效
攔截了這么幾種廣播,按字面意思,應(yīng)該是這么幾類
Constant.ACTION_CONTACT_CHANAGED 聯(lián)系人變化廣播
Constant.ACTION_GROUP_CHANAGED 群組變化廣播
RPConstant.REFRESH_GROUP_RED_PACKET_ACTION 刷新群紅包廣播
接受了消息了以后調(diào)用了updateUnreadLabel();和updateUnreadAddressLable();方法
未讀消息數(shù)更新
/**
* 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);
}
}
更新總計(jì)未讀數(shù)量
/**
* 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);
}
}
});
?
}
然后判斷廣播類型,如果當(dāng)前的棧頂為主界面,則調(diào)用GroupsActivity的onResume方法。
如果為群紅包更新意圖則調(diào)用的converstationListFragment的refersh()方法
添加聯(lián)系人監(jiān)聽
EMClient.getInstance().contactManager().setContactListener(new MyContactListener());
我們來看下這個(gè)MyContactListener()監(jiān)聽方法。
我們發(fā)現(xiàn)是MyContactListener是繼承自EMContactListener的,我們?cè)賮砜纯碋MContactListener和其官方文檔的解釋。
我們發(fā)現(xiàn)其定義了5個(gè)接口,這5個(gè)接口根據(jù)文檔釋義分別是如下含義:
void? ? onContactAdded (String username)//增加聯(lián)系人時(shí)回調(diào)此方法
void? ? onContactDeleted (String username)//被刪除時(shí)回調(diào)此方法
void? ? onContactInvited (String username, String reason)/**收到好友邀請(qǐng) 參數(shù) username? 發(fā)起加為好友用戶的名稱 reason? 對(duì)方發(fā)起好友邀請(qǐng)時(shí)發(fā)出的文字性描述*/
void? ? onFriendRequestAccepted (String username)//對(duì)方同意好友請(qǐng)求
void? ? onFriendRequestDeclined (String username)//對(duì)方拒絕好友請(qǐng)求
從而我們得知,我們demo中的自定義監(jiān)聽接口在被刪除回調(diào)時(shí),做了如下操作:
如果你正在和這個(gè)刪除你的人聊天就提示你這個(gè)人已把你從他好友列表里移除并且結(jié)束掉聊天界面。
測(cè)試用廣播監(jiān)聽?
//debug purpose only
registerInternalDebugReceiver();
/**
* 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);
}
至此MainActivity的OnCreate方法中所有涉及到的代碼我們均已看完。
其他方法
接下來我們來撿漏,看看還有剩余哪些方法沒有去看。
判斷當(dāng)前賬號(hào)是否移除
/**
* check if current user account was remove
*/
public boolean getCurrentAccountRemoved() {
return isCurrentAccountRemoved;
}
oncreate()
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() {
runOnUiThread(new Runnable() {
public void run() {
// refresh unread count
updateUnreadLabel();
if (currentTabIndex == 0) {
// refresh conversation list
if (conversationListFragment != null) {
conversationListFragment.refresh();
}
}
}
});
}
registerBroadcastReceiver()
unregisterBroadcastReceiver();反注冊(cè)廣播接收者。
private void unregisterBroadcastReceiver(){
broadcastManager.unregisterReceiver(broadcastReceiver);
}
onDestory()
@Override
protected void onDestroy() {
super.onDestroy();
if (exceptionBuilder != null) {
exceptionBuilder.create().dismiss();
exceptionBuilder = null;
isExceptionDialogShow = false;
}
unregisterBroadcastReceiver();
?
try {
unregisterReceiver(internalDebugReceiver);
} catch (Exception e) {
}
}
異常的彈窗disimiss及置空,反注冊(cè)廣播接收者。
updateUnreadAddressLable()
getUnreadAddressCountTotal()
getUnreadMsgCountTotal()
getExceptionMessageId() 判斷異常的種類
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;
}
showExceptionDialog()
getUnreadAddressCountTotal()
getUnreadMsgCountTotal()
onResume() 中做了一些例如更新未讀應(yīng)用事件消息,并且push當(dāng)前Activity到easui的ActivityList中
@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);
}
onStop();
@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);
}
存一下沖突和賬戶移除的標(biāo)志位
onKeyDown();判斷按了回退的時(shí)候。 moveTaskToBack(false);僅當(dāng)前Activity為task根時(shí),將activity退到后臺(tái)而非finish();
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
moveTaskToBack(false);
return true;
}
return super.onKeyDown(keyCode, event);
}
getExceptionMessageId()
showExceptionDialog()
showExceptionDialogFromIntent()
onNewIntent() Activity在singleTask模式下重用該實(shí)例,onNewIntent()->onResart()->onStart()->onResume()這么個(gè)順序原地復(fù)活。
至此,我們的MainActivity就全部閱讀完畢了。
我們是在已經(jīng)登錄的情況下來到的MainActivity,那么我們?cè)跊]有登錄情況下呢,當(dāng)然是來登陸頁面。下面我們來看登錄頁。