Android WebSocket實現即時通訊/推送
阿新 • • 發佈:2019-01-10
使用java-websocket實現即時通訊/推送模組;
支援即時發訊息和收訊息,訊息型別有開發者自定義;;
該開源專案支援客戶端client和服務端server的配置使用,並提供示例test;
1,Android 客戶端使用需要配置網路許可權;
2,需要寫一個自己的client類來繼承WebsocketClient;實現websocket的狀態回撥和新訊息的解析動作;
3,需要監控自己的client的連結狀態,維持長連結;
4,傳送和接收
下面貼出部分相關程式碼;
網路許可權的不用說了吧!
client類:
常用狀態回撥方法有4個;
自己可以在對應的函式裡面做響應的處理,比如當連結發生錯誤時要重新去開啟該連結,收到訊息時即時的儲存聊天記錄和傳送系統通知來提醒使用者檢視新訊息等等;
<pre name="code" class="html"><span style="font-size:18px;">public class TestClient extends WebSocketClient { public TestClient(URI serverURI) { super(serverURI); } /*** * 連結關閉 */ @Override public void onClose(int arg0, String arg1, boolean arg2) { } /*** * 連結發生錯誤 */ @Override public void onError(Exception arg0) { } /** * 新訊息 */ @Override public void onMessage(String arg0) { } /*** * 連結開啟 */ @Override public void onOpen(ServerHandshake arg0) { // TODO Auto-generated method stub } } </span>
下面是我聊天部分程式碼,有離線訊息/PC同步/多對多的聊天;
僅供參考;
至於資料庫的一部分程式碼就不貼出了,無非是增刪改查。/*** * <h2>WebSocket Android 客戶端</h2> * <ol> * <li>Socket連結打開回調 {@link WebSocket#onOpen(ServerHandshake)},此處有 * {@link SocketConstant#ON_OPEN} 廣播發出; * <li>Socket連結出現異常錯誤時回撥 {@link WebSocket#onError(Exception)},此處有 * {@link SocketConstant#ON_ERROR}廣播發出; * <li>Socket連結關閉回撥 {@link WebSocket #onClose(int, String, boolean)},此處有 * {@link SocketConstant#ON_CLOSES}廣播發出; * <li>Socket連結接收訊息回撥 {@link WebSocket#onMessage(String)} * ,此處做收訊息的邏輯的處理;包括髮送訊息伺服器返回的傳送結果,PC端同步接收的新訊息,及外來的新訊息; * <li>檢測是否有訊息遺漏 {@link WebSocket#checkMsgWebId(String, String)},引數為聯絡人和webId; * <li>取得正在桌面執行的activity的名稱 {@link WebSocket#getRunningActivityName()} * <li>接收到的訊息的處理 {@link WebSocket#messageHandle(MessageEntity, String)} * ,引數為訊息實體和訊息型別 (buyer/server) * <li>傳送新訊息的系統通知 {@link WebSocket#sendNotification(String)},引數為聯絡人; * <li>儲存離線訊息 {@link WebSocket#saveOffLineMsg(HashMap)},引數為接收到的離線訊息集合; * <li>儲存從服務端獲取的聯絡人的webId {@link WebSocket#saveContactsWebID(HashMap)} * ,引數為以聯絡人為key以最大webId為值得map集合; * </ol> * * @author li'mingqi <a> 2014-3-19</a> * */ public class WebSocket extends WebSocketClient { // 登陸返回的back_type欄位 public static final String LOGIN_RETURN_TYPE = "login"; // 傳送資訊的back_type欄位 public static final String SEND_RETURN_TYPE = "send_result"; // 接收資訊的back_type欄位 public static final String RECEIVER_RETURN_TYPE = "msg"; // 接收客服的資訊的back_type欄位 public static final String GET_SERVER_RETURN_TYPE = "server_info"; // 接收服務端返回對應聯絡人的最大順序ID public static final String CONTACTS_MAX_WEBID_TYPE = "max_id_return"; // 接收使用者的離線訊息 public static final String USER_OFFLINE_MSG_TYPE = "offline"; // 上下文物件 private Context mContext; // socket返回json解析類物件 private WebSocketParser mParser; // 系統通知管理 public NotificationManager mNotificationManager; // 系統通知 private Notification mNoti; // 意圖 private PendingIntent mIntent; // 該系統通知的 id public static final int NOTIFICATION_ID = 100; @SuppressWarnings("deprecation") public SGWebSocket(Context context, URI serverUri, Draft draft) { super(serverUri, draft); this.mContext = context; //新訊息的解析類 this.mParser = WebSocketParser.getInstance(); //收到新訊息傳送的通知 this.mNotificationManager = (NotificationManager) this.mContext .getSystemService(Context.NOTIFICATION_SERVICE); this.mNoti = new Notification(R.drawable.system_info, "您有新訊息!", System.currentTimeMillis()); } /*** * send broadcast <SGSocketConstant>ON_CLOSES filter if this socket closed * socket 發生關閉時傳送的廣播,若想提示,可以接受並處理 * */ @Override public void onClose(int arg0, String arg1, boolean arg2) { // 更改儲存的連結狀態 UserInfoUtil.saveSocket(mContext, false); mNotificationManager.cancelAll(); Intent intent = new Intent(SocketConstant.ON_CLOSES); intent.putExtra(SocketConstant.ON_CLOSES, arg1.toString()); mContext.sendBroadcast(intent); } /*** * send broadcast <SGSocketConstant>ON_ERROR filter if this socket has error * socket 發生錯誤傳送的廣播,若想提示,可以接受並處理 * */ @Override public void onError(Exception arg0) { Intent intent = new Intent(SGSocketConstant.ON_ERROR); intent.putExtra(SGSocketConstant.ON_ERROR, arg0.toString()); mContext.sendBroadcast(intent); this.close(); } // 買家 public static final String MSG_BUYER_TYPE = "1"; // 客服 public static final String MSG_SERVCER_TYPE = "2"; // 遊客 // public static final String MSG_RANDOM_TYPE = "3"; /*** * receiver message from server 1,登陸返回 type * <WebSocket>LOGIN_RETURN_TYPE; 2,傳送返回 type * <WebSocket>SEND_RETURN_TYPE; 3,接收資訊返回 type * <WebSocket>RECEIVER_RETURN_TYPE; * * @throws InterruptedException */ @Override public void onMessage(String content) { // parser try { JSONObject object = new JSONObject(content); Log.i("json", "賣家--" + object.toString()); String back_type = object.getString("back_type"); String activity = getRunningActivityName(); if (SEND_RETURN_TYPE.equals(back_type)) {// 傳送具體訊息時返回傳送結果 // json解析 MessageEntity entity = mParser.sendMessageParser(mContext, content); if ("true".equals(entity.getSend_state())) {// 傳送成功 // 判斷是否是PC端傳送的訊息,若是PC端傳送的訊息,則在Android端做同步儲存處理 // 1,首先判斷資料庫中是否包含該條資訊 boolean has = MessageDB.getInstance(mContext) .findMessageByMsgId(entity.get_id()); if (has) { // Android端傳送 MessageDB.getInstance(mContext).update(entity.get_id(), true, entity.getReceiverTime(), entity.getWebId());// 更新發送狀態為已傳送 } else { // PC端傳送,將該訊息同步到Android端資料庫 entity.setSend_state(SocketConstant.MSG_SEND_SUCCESS_STATE); MessageDB.getInstance(mContext).insert(entity, SocketConstant.MSG_TYPE_BUYER);// 賣家傳送給買家的 // 通知聊天主頁面,更新聊天列表 pcSynAndroid(activity); } // 檢測是否有訊息遺漏 checkMsgWebId(entity.getContacts(), entity.getWebId()); Log.i("miss", "傳送返回或者PC同步--" + entity.getContacts() + "--" + entity.getWebId()); } else if ("false".equals(entity.getSend_state())) {// 傳送失敗 MessageDB.getInstance(mContext).update(entity.get_id(), false, entity.getReceiverTime(), entity.getWebId()); Toast.makeText(mContext, entity.getErrorText(), Toast.LENGTH_SHORT).show(); } // 登陸返回 記錄session } else if (LOGIN_RETURN_TYPE.equals(back_type)) { KApplication.session = object.getString("session_id"); String str = object.getString("login_status"); if ("true".equals(str)) { UserInfoUtil.saveSocket(mContext, true); // 生成json請求字串 String maxIdstring = SocketJsonUtil .getContactsCurrentWebId(UserInfoUtil .getUser(mContext)[0], "2", MessageDB .getInstance(mContext) .findAllContactsAndType()); // 登陸成功,向伺服器索取聯絡人的最大webId send(maxIdstring); Log.i("send", maxIdstring); } else if ("false".equals(str)) { UserInfoUtil.saveSocket(mContext, false); } } else if (RECEIVER_RETURN_TYPE.equals(back_type)) {// 接收到的具體聊天的資訊 // json解析 MessageEntity entity = mParser.receiverMessagePrser(mContext, content); // 判斷資料庫中是否有該條訊息,有則不處理,無則處理訊息; if (!MessageDB.getInstance(mContext).findMessageByMsgId( entity.get_id())) { // 訊息處理 if (MSG_BUYER_TYPE.equals(entity.getSenderType())) { // 買家 messageHandle(entity, SocketConstant.MSG_TYPE_BUYER); } else if (MSG_SERVCER_TYPE.equals(entity.getSenderType())) { // 賣家,客服 messageHandle(entity, SocketConstant.MSG_TYPE_SERVER); } Log.i("miss", "沒有該條訊息"); // 檢測是否有訊息遺漏 checkMsgWebId(entity.getContacts(), entity.getWebId()); } } else if (GET_SERVER_RETURN_TYPE.equals(back_type)) {// 獲取閃聊客服返回的資料 // 客服 ServerEntity entity = mParser.serverInfoParser(content);// 客服物件 Intent intent = new Intent(SocketConstant.GET_SERVER_INFO); intent.putExtra("server_info", entity); mContext.sendBroadcast(intent); } else if (CONTACTS_MAX_WEBID_TYPE.equals(back_type)) { // 返回的聯絡人最大的訊息id HashMap<String, String> map = mParser.contactsMaxWebId(content); // 將聯絡人和其最大webId存入臨時集合 saveContactsWebID(map); // 開始請求伺服器,釋放離線訊息給客戶端; send(SocketJsonUtil.getOffLine( UserInfoUtil.getUser(mContext)[0], "2")); Log.i("send", SocketJsonUtil.getOffLine( UserInfoUtil.getUser(mContext)[0], "2")); } else if (USER_OFFLINE_MSG_TYPE.equals(back_type)) { // 使用者的離線訊息 HashMap<String, ArrayList<MessageEntity>> map = mParser .offLineMsg(mContext, content); // 將離線訊息入庫 saveOffLineMsg(map); } } catch (JSONException e) { this.close(); } } /*** * send broadcast <SocketConstant>ON_OPEN filter if this socket opened * socket 開啟時傳送的廣播,若想提示,可以接受並處理 * */ @Override public void onOpen(ServerHandshake arg0) { Intent intent = new Intent(SGSocketConstant.ON_OPEN); mContext.sendBroadcast(intent); } /*** * 檢測正在執行tasktop的activity * @return current running activity name * */ private String getRunningActivityName() { ActivityManager activityManager = (ActivityManager) mContext .getSystemService(Context.ACTIVITY_SERVICE); String runningActivity = activityManager.getRunningTasks(1).get(0).topActivity .getClassName(); return runningActivity; } /*** * send notification for this contacts * 傳送通知 * @param contacts * */ @SuppressWarnings("deprecation") private void sendNotification(String contacts) { Intent intent = new Intent(mContext, MainActivity.class); mIntent = PendingIntent.getActivity(mContext, 100, intent, 0); mNoti.flags = Notification.FLAG_AUTO_CANCEL; mNoti.defaults = Notification.DEFAULT_VIBRATE; mNoti.setLatestEventInfo(mContext, "標題", "您有新訊息!", mIntent); mNoti.contentView = new RemoteViews(mContext.getApplicationContext() .getPackageName(), R.layout.notification_item); mNoti.contentView.setTextViewText(R.id.noti_message, "收到來自" + contacts + "的新訊息"); mNotificationManager.notify(NOTIFICATION_ID, mNoti); } /*** * 具體聊天收到的外來訊息處理 * * @param entity * 訊息實體 * @param messageType * 訊息型別(買家/客服) */ private void messageHandle(MessageEntity entity, String messageType) { String activity = getRunningActivityName(); // 處於聊天的頁面 if ("com.ui.activity.ManageChartActivity".equals(activity)) { // 處於正在聊天物件的頁面,將資料寫入資料庫,併發送廣播更新頁面資料 if (KApplication.crurentContacts.equals(entity.getContacts())) { /** * 接收到的訊息,訊息實體entity的send_state欄位狀態設定為 * MSG_SEND_SUCCESS_STATE(即201) **/ entity.setSend_state(SocketConstant.MSG_SEND_SUCCESS_STATE);// 收到的資訊,設定資訊的狀態 entity.setRead(SocketConstant.READ_STATE); MessageDB.getInstance(mContext).insert(entity, messageType);// 將資料寫入資料庫, Intent intent = new Intent(SocketConstant.NEW_MESSAGE); intent.putExtra("newmsg", entity); mContext.sendBroadcast(intent); // 沒有處於閃聊物件的頁面,將資料寫入資料庫,傳送系統通知 } else { entity.setSend_state(SocketConstant.MSG_SEND_SUCCESS_STATE);// 收到的資訊,設定資訊的狀態 entity.setRead(SocketConstant.DEFAULT_READ_STATE); MessageDB.getInstance(mContext).insert(entity, messageType); if (KApplication.sp.getBoolean(RefreshUtils.noteFlag, false)) { sendNotification(entity.getContacts()); } Intent intent = new Intent( SocketConstant.RECEIVER_NEW_MESSAGE); mContext.sendBroadcast(intent); } // 將資料寫入資料庫,傳送系統通知 } else { entity.setSend_state(SocketConstant.MSG_SEND_SUCCESS_STATE);// 收到的資訊,設定資訊的狀態 entity.setRead(SocketConstant.DEFAULT_READ_STATE); MessageDB.getInstance(mContext).insert(entity, messageType); Intent intent = new Intent(); if ("com.ui.activity.ManageConversationActivity" .equals(activity) || "com.ui.activity.MainActivity" .equals(activity)) { intent.setAction(SocketConstant.RECEIVER_NEW_MESSAGE);// 聊天頁面 } else { intent.setAction(SocketConstant.RECEIVER_NEW_MESSAGE_OTHER);// 其他頁面 } mContext.sendBroadcast(intent); if (KApplication.sp.getBoolean(RefreshUtils.noteFlag, false)) { sendNotification(entity.getContacts()); } } } /*** * 電腦與手機同步資訊 * * @param currentActivity */ public void pcSynAndroid(String currentActivity) { if ("com.iflashseller.ui.activity.ManageChartActivity" .equals(currentActivity)) { // 正好與該聯絡人對話的頁面 Intent intent = new Intent(SocketConstant.CHART_ACTIVITY); mContext.sendBroadcast(intent); } else { // 其他頁面 Intent intent = new Intent(SocketConstant.GROUPS_ACTIVITY); mContext.sendBroadcast(intent); } } /*** * 檢測是否有訊息遺漏 * * @param contacts * 聯絡人 * @param webId * 服務端給出的訊息Id */ public void checkMsgWebId(String contacts, int webId) { // 集合中含有該聯絡人 if (KApplication.webIds.containsKey(contacts)) { Log.i("miss", "儲存的--" + KApplication.webIds.get(contacts)); // 臨時集合中儲存的webId int c = KApplication.webIds.get(contacts); /*** * 如果新收到的訊息的webId大於臨時集合中儲存的改聯絡人的webId,且他們之間的差值大於1, * 則請求伺服器推送疑似丟失的webId對應的訊息 */ if (webId > c && (webId - 1) != c) { // id不連續 for (int i = c + 1; i < webId; i++) { // 向伺服器傳送請求,獲取遺漏的訊息 String miss = SocketJsonUtil.getMissMsg( UserInfoUtil.getUser(mContext)[0], "2", contacts, "1", i + ""); this.send(miss); Log.i("miss", miss); } /*** * 如果他們之間的差值正好為1,則修改臨時集合的改聯絡人的webId, */ } else if (webId > c && (webId - 1) == c) { KApplication.webIds.put(contacts, webId); Log.i("miss", "修改的--" + contacts + "--" + webId); } /**** * 臨時集合中沒有改聯絡人的資訊,則將該聯絡人的webId存入臨時集合. */ } else { KApplication.webIds.put(contacts, webId); Log.i("miss", "新增--" + contacts + "--" + webId); } } /*** * 將從服務端獲取的聯絡人的webId存入臨時集合 * * @param map */ public void saveContactsWebID(HashMap<String, String> map) { Iterator<Entry<String, String>> iter = map.entrySet().iterator(); while (iter.hasNext()) { Entry<String, String> es = iter.next(); String contacts = es.getKey(); String maxWebID = es.getValue(); KApplication.webIds.put(contacts, Integer.parseInt(maxWebID)); } } /*** * 將離線訊息入庫 * * @param map */ public void saveOffLineMsg(HashMap<String, ArrayList<MessageEntity>> map) { Iterator<Entry<String, ArrayList<MessageEntity>>> iter = map.entrySet() .iterator(); while (iter.hasNext()) { ArrayList<MessageEntity> msgs = iter.next().getValue(); for (int i = 0; i < msgs.size(); i++) { threadSleep(100); MessageDB.getInstance(mContext).insert(msgs.get(i), SocketConstant.MSG_TYPE_BUYER); Log.i("write", "離線資料入庫---" + msgs.get(i).toString()); } /*** * 如果服務端一次釋放的離線訊息大於等於10條,則繼續請求釋放離線訊息. */ if (msgs.size() >= 10) { send(SocketJsonUtil.getOffLine( UserInfoUtil.getUser(mContext)[0], "2")); Log.i("send", SocketJsonUtil.getOffLine( UserInfoUtil.getUser(mContext)[0], "2")); } } // 一輪訊息入庫結束,傳送通知,更新UI; mContext.sendBroadcast(new Intent( SocketConstant.OFFLINE_MSG_RECEIVER_SUCCESS)); } private void threadSleep(long time) { try { Thread.currentThread(); Thread.sleep(time); } catch (InterruptedException e) { e.printStackTrace(); } } }
下面貼出部分監控連結狀態的程式碼,以保證能即時的收到訊息;
public class LApplication extends Application {
public static String TAG = LApplication.class.getSimpleName();
/** 接收訊息廣播 **/
private LPullReceiver mPullReceiver;
/** 是否是正在登入 **/
public static boolean isLoging = false;
/** socket管理類 **/
private LPushManager mWebSocket;
@Override
public void onCreate() {
super.onCreate();
/***
* 註冊接收訊息的廣播
*/
mPullReceiver = new LPullReceiver();
// 廣播過濾
IntentFilter filter = new IntentFilter();
// 時鐘資訊發生變化
filter.addAction(Intent.ACTION_TIME_TICK);
// 開機廣播
filter.addAction(Intent.ACTION_BOOT_COMPLETED);
// 網路狀態發生變化
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
// 螢幕開啟
filter.addAction(Intent.ACTION_SCREEN_ON);
// 註冊廣播
registerReceiver(mPullReceiver, filter);
// 例項化socket管理類
mWebSocket = new LPushManager(getApplicationContext());
// 應用重啟一次,預設socket為關閉狀態
LPushUser.saveSocket(getApplicationContext(), false);
// 預設連結沒有被拒絕
LPushUser.saveConnect(getApplicationContext(), false);
// 1,獲取當前時間
long currentTime = System.currentTimeMillis();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date(currentTime);
String time = sdf.format(date);
// 修改標記的時間,保證5分鐘內連結一次
LPushUser.saveOpenTime(getApplicationContext(), time);
}
/**
* 廣播介面類
* <ol>
* <li>接收時鐘發生變化的廣播
* <li>接收網路發生變化的廣播
* <li>接收開機發送廣播
* <li>接收使用者登入後傳送的廣播
* </ol>
*
* @author li'mingqi
*
*/
class LPullReceiver extends BroadcastReceiver {
@SuppressLint("SimpleDateFormat")
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Intent.ACTION_TIME_TICK.equals(action)
|| ConnectivityManager.CONNECTIVITY_ACTION.equals(action)
|| Intent.ACTION_BOOT_COMPLETED.equals(action)
|| Intent.ACTION_SCREEN_ON.equals(action)) {
if (LPushUser.GETLOG(getApplicationContext()))
Log.i("lpush",
"socket的連結狀態----"
+ LPushUser
.getSocket(getApplicationContext())
+ "是否正在連結--" + isLoging + "----" + action);
// 當時鍾或者網路發生變化或者socket關閉或者發生異常時或者開機 時,判斷socket連線是否出現異常
if (!LPushUser.getSocket(getApplicationContext()) && !isLoging
&& LPushUser.getSocketEnable(getApplicationContext())
&& !"".equals(IP) && !"".equals(PORT)
&& !LPushUser.getConnectState(getApplicationContext())) {
// 掉線,執行登入動作
if (LNetworkUtil.netIsEnable(getApplicationContext())) {
mWebSocket.secondMethod(IP, PORT);
// 開始登入了,標記開啟時間
// 1,獲取當前時間
long currentTime = System.currentTimeMillis();
SimpleDateFormat sdf = new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss");
Date date = new Date(currentTime);
String time = sdf.format(date);
// 修改標記的時間,保證5分鐘嗅探連結一次
LPushUser.saveOpenTime(getApplicationContext(), time);
}
} else {
// APP端已經處於連結正常狀態 -----5分鐘嗅探連結一次
// 1,獲取當前時間
long currentTime = System.currentTimeMillis();
SimpleDateFormat sdf = new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss");
Date date = new Date(currentTime);
String time = sdf.format(date);
// 2,比對連結開啟時間
long minTime = LStringManager.dateDifference(
LPushUser.getOpenTime(getApplicationContext()),
time);
if (LPushUser.GETLOG(getApplicationContext())) {
Log.i("lpush",
"連結時長----現在時間:"
+ time
+ ";儲存時間:"
+ LPushUser
.getOpenTime(getApplicationContext())
+ ";時差" + minTime + "分鐘");
}
if (minTime >= 5) {
// 大於等於5分鐘,則重新連結
// 5分鐘之後重新連結
// 修改被拒絕狀態
if (LPushUser.getConnectState(getApplicationContext())) {
LPushUser.saveConnect(getApplicationContext(),
false);
}
if (LNetworkUtil.netIsEnable(getApplicationContext())
&& LPushUser
.getSocketEnable(getApplicationContext())) {
mWebSocket.secondMethod(IP, PORT);
// 修改標記的時間,保證5分鐘嗅探連結一次
LPushUser.saveOpenTime(getApplicationContext(),
time);
}
}
}
}
}
}
/***
* 設定推送功能的使用與否,預設使用推送功能,若是關閉推送功能請設定false;
*
* @param enable
* 是否使用
*
* li'mingqi
*/
protected void setLPushEnable(boolean enable) {
LPushUser.saveSocketEnable(getApplicationContext(), enable);
}
/***
*
*
* @param ip
* ip資訊
* @param port
* 埠資訊
*
* li'mingqi
*/
protected void setSocketIPInfo(String ip, String port) {
this.IP = ip;
this.PORT = port;
}
/**
* 設定使用者的Uid
*
* @param uid
* li'mingqi
*/
public void setUserInfo(int uid, String code) {
/***
* 資料驗證
*/
// if (0 == uid || null == code || "".equals(code)) {
// Log.e(TAG, "您輸入的使用者ID或者CODE值為空");
// new NullPointerException("您輸入的使用者ID或者CODE值為空").printStackTrace();
// return;
// }
// 儲存使用者ID
LPushUser.saveUserID(getApplicationContext(), uid);
// 儲存使用者CODE
LPushUser.saveUserCode(getApplicationContext(), code);
// 重啟連結
mWebSocket.close();
}
/***
* 設定是否檢視日誌
*
* @param flag
* 是否檢視日誌
* @version 1.2 li'mingqi
*/
public void openLogInfo(boolean flag) {
LPushUser.SAVELOG(getApplicationContext(), flag);
}
/***
* socket連結重置,伺服器一直處於拒絕連結狀態,客戶端連結一次遭拒後標記了遭拒的狀態,重置之後可進行再次開啟連結;
*
* @version 1.3 li'mingqi
*/
private void reset() {
LPushUser.saveConnect(getApplicationContext(), false);
}
}
UI介面顯示部分就不貼出了,後面貼出Application類的目的就是監控各種手機系統的廣播來嗅探自己的websocket連結的狀態;用來維持它以保證能即時的收到訊息;