環信官方Demo源碼分析及SDK簡單應用-ChatDemoUI3.0
環信官方Demo源碼分析及SDK簡單應用-LoginActivity
環信官方Demo源碼分析及SDK簡單應用-主界面的三個fragment-會話界面
環信官方Demo源碼分析及SDK簡單應用-主界面的三個fragment-通訊錄界面
環信官方Demo源碼分析及SDK簡單應用-主界面的三個fragment-設置界面
環信官方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
各流程類
AndroidMainfest.xml
實際上沒什么好說的,不過我們還是細細的研究一番
解決sdk定義版本聲明的問題,我們在后面如果使用到了紅包的ui,出現了一些sdk的錯誤可以加上。
SDK常見的一大坨權限。其中Google Cloud Messaging還是別用吧,身在何處,能穩定么?
然后就是各種各樣的界面聲明
總共這么些個界面(Tips:由于本文是現閱讀現寫,所有未中文指出部分,后面代碼閱讀會去補上):
開屏頁
登陸頁
注冊
聊天
添加好友
群組邀請
群組列表
聊天室詳情
新建群組
退出群組提示框
群組選人
PickAtUserActivity
地圖
新的朋友邀請消息頁面
轉發消息用戶列表頁面
自定義的contextmenu
顯示下載大圖頁面
下載文件
黑名單
公開的群聊列表
PublicChatRoomsActivity
語音通話
視頻通話
群聊簡單信息
群組黑名單用戶列表
GroupBlacklistActivity
GroupSearchMessageActivity
PublicGroupsSeachActivity
EditActivity
EaseShowVideoActivity
ImageGridActivity
RecorderVideoActivity
DiagnoseActivity
OfflinePushNickActivity
robots list
RobotsActivity
UserProfileActivity
SetServersActivity
OfflinePushSettingsActivity
CallOptionActivity
發紅包
紅包詳情
紅包記錄
WebView
零錢
綁定銀行卡
群成員列表
支付寶h5支付頁面
轉賬頁面
轉賬詳情頁面
再往下就是相關的一些廣播接收者,服務,以及雜七雜八的東西了。有如下部分:開機自啟動
GCM
小米推送
華為推送
友盟
EMChat服務
EMJob服務
EMMonitor Receiver
百度地圖服務
其中比較重要的
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);? ? }}
第一句是分包,我們知道分包有以下兩種方式:
項目中的Application類繼承MultiDexApplication。
在自己的Application類的attachBaseContext方法中調用MultiDex.install(this);。
然后又做了幾件事
初始化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的界面,然后看代碼的方法,再一一對應。
來,我們來看界面,界面是這個樣子的。
三個界面
會話、通訊錄、設置
有了直觀的認識以后,我們再來看代碼。
我們來一段一段看代碼
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);? ? }?}
只有友盟的一些數據埋點,我們繼續往上挖看他爹。
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();? ? }}
這段代碼其實是用來解決重復實例化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 DemoHelper
registerBroadcastReceiver();
從英文注釋來看,字面意思來看是用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);
}
}
更新總計未讀數量
/**
* 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);
}
}
});
?
}
然后判斷廣播類型,如果當前的棧頂為主界面,則調用GroupsActivity的onResume方法。
如果為群紅包更新意圖則調用的converstationListFragment的refersh()方法
添加聯系人監聽
EMClient.getInstance().contactManager().setContactListener(new MyContactListener());
我們來看下這個MyContactListener()監聽方法。
我們發現是MyContactListener是繼承自EMContactListener的,我們再來看看EMContactListener和其官方文檔的解釋。
我們發現其定義了5個接口,這5個接口根據文檔釋義分別是如下含義:
void? ? onContactAdded (String username)//增加聯系人時回調此方法
void? ? onContactDeleted (String username)//被刪除時回調此方法
void? ? onContactInvited (String username, String reason)/**收到好友邀請 參數 username? 發起加為好友用戶的名稱 reason? 對方發起好友邀請時發出的文字性描述*/
void? ? onFriendRequestAccepted (String username)//對方同意好友請求
void? ? onFriendRequestDeclined (String username)//對方拒絕好友請求
從而我們得知,我們demo中的自定義監聽接口在被刪除回調時,做了如下操作:
如果你正在和這個刪除你的人聊天就提示你這個人已把你從他好友列表里移除并且結束掉聊天界面。
測試用廣播監聽?
//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方法中所有涉及到的代碼我們均已看完。
其他方法
接下來我們來撿漏,看看還有剩余哪些方法沒有去看。
判斷當前賬號是否移除
/**
* 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();反注冊廣播接收者。
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及置空,反注冊廣播接收者。
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() 中做了一些例如更新未讀應用事件消息,并且push當前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);
}
存一下沖突和賬戶移除的標志位
onKeyDown();判斷按了回退的時候。 moveTaskToBack(false);僅當前Activity為task根時,將activity退到后臺而非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模式下重用該實例,onNewIntent()->onResart()->onStart()->onResume()這么個順序原地復活。
至此,我們的MainActivity就全部閱讀完畢了。
我們是在已經登錄的情況下來到的MainActivity,那么我們在沒有登錄情況下呢,當然是來登陸頁面。下面我們來看登錄頁。