Android產品研發(十二)-->App長鏈接實現

 

而本文中咱們將講解一下App的長鏈接實現。通常而言長鏈接已是App的標配了,推送功能的實現基礎就是長鏈接,固然了咱們也能夠經過輪訓操做實現推送功能,可是輪訓通常及時性比較差,並且網絡消耗與電量銷燬比較多,所以通常推送功能都是經過長鏈接實現的。java

那麼如何實現長鏈接呢?如今通常有這麼幾種實現方式:服務器

  • 使用第三方的長鏈接服務;網絡

  • 經過NIO等方案實現長鏈接服務;session

  • 經過MINA等第三方框架實現長鏈接;框架

幾種長鏈接服務的具體實現,以及各自的優缺點。

1. 使用第三方的長鏈接服務

介紹:這是最簡單的方式,咱們能夠經過接入極光推送,百度推送,友盟等第三方服務實現長鏈接,經過接入第三方的API咱們能夠很方便的接入第三方的長鏈接,推送服務,可是這種方式定製化程度不太好,若是對長鏈接服務不是要求特別高,對定製化要求不是很高的話基本能夠考慮這種方式(目前主流的App都是使用第三方的長鏈接服務) 
優點:簡單,方便 
劣勢:定製化程度不高ide

2. 使用NIO等方案實現長鏈接服務

介紹:經過NIO的方式實現長鏈接,這種方式對技術要求程度比較高,基本都是經過java API實現長鏈接,實現心跳包,實現異常狀況的容錯等操做,能夠說經過NIO實現長鏈接對技術要求很高,通常若是沒有成行的技術方案比建議這麼作,就算實現了長鏈接,後期鏈接的維護,對電量,流量的損耗等都須要持續的優化。 
優點:定製化比較高 
劣勢:技術要求高,須要持續的維護oop

3. 使用MINA等第三方框架實現長鏈接

介紹:MINA是一個第三方的NIO框架,該框架實現了一整套的長鏈接機制,包括長鏈接的創建,心跳包的實現,異常機制的容錯等。使用MINA實現長鏈接能夠定製化的實現一些特有的功能,而且比NIO方案較爲簡單,由於其已經封裝了一些長鏈接的特有機制,好比心跳包,容錯等。 
優點:可定製,較NIO方法簡單 
劣勢:也須要必定的技術儲備優化

長鏈接具體實現

在咱們的Android客戶端中長鏈接的實現機制採用–MINA方式。這裏多說一句,一開始的長鏈接採用的是NIO方案,可是採用這種方案以後踩了不少坑,包括心跳,容錯等機制都是本身寫的,因此耗費了大量的時間,並且對手機電量的消耗很大,最後決定使用MINA NIO框架從新實現一遍長鏈接,後來通過實測,長鏈接的穩定性還有耗電量,流量的消耗等指標方面有了很大的提升。ui

下面我將簡單的介紹一下經過NIO實現長鏈接的具體流程:this

  • 引入MINA jar包,在App啓動頁面,登陸頁面啓動長鏈接;

  • 建立後臺服務,在服務中建立MINA長鏈接;

  • 實現心跳包,重寫一些容錯機制;

  • 實現長鏈接斷了以後的重連機制,而且重連次數有限制不能一直重連;

  • 長鏈接斷了以後實現輪訓操做,這裏的輪訓服務只有在長鏈接斷了以後才啓動,在長鏈接恢復以後關閉;

如下就是在長鏈接中實現的具體代碼:

  • 在Application的onCreate方法中檢測App是否登陸,若登陸的話啓動長鏈接
/**
 * 在Application的onCreate方法中執行啓動長鏈接的操做
 **/
@Override
    public void onCreate() {
        ... // 登陸後開啓長鏈接 if (UserConfig.isPassLogined()) { L.i("用戶已登陸,開啓長鏈接..."); startLongConn(); } ... }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 經過鬧鐘服務實現具體的啓動長鏈接service的操做,即每隔60秒鐘判斷長鏈接是否啓動,若未啓動則實現啓動操做
/** * 開始執行啓動長鏈接服務 */ public void startLongConn() { quitLongConn(); L.i("長鏈接服務已開啓"); AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(this, LongConnService.class); intent.setAction(LongConnService.ACTION); PendingIntent pendingIntent = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); long triggerAtTime = SystemClock.elapsedRealtime(); manager.setRepeating(AlarmManager.RTC_WAKEUP, triggerAtTime, 60 * 1000, pendingIntent); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 下面的代碼就是長鏈接服務的具體實現
/** * 後臺長鏈接服務 **/ public class LongConnService extends Service { public static String ACTION = "com.youyou.uuelectric.renter.Service.LongConnService"; private static MinaLongConnectManager minaLongConnectManager; public String tag = "LongConnService"; private Context context; @Override public int onStartCommand(Intent intent, int flags, int startId) { context = getApplicationContext(); // 執行啓動長鏈接的操做 startLongConnect(); ObserverManager.addObserver("LongConnService", stopListener); return START_STICKY; } public ObserverListener stopListener = new ObserverListener() { @Override public void observer(String from, Object obj) { closeConnect(); } }; @Override public void onDestroy() { super.onDestroy(); closeConnect(); } /** * 開始執行啓動長鏈接的操做 */ private void startLongConnect() { if (Config.isNetworkConnected(context)) { if (minaLongConnectManager != null && minaLongConnectManager.checkConnectStatus()) { L.i("長鏈接狀態正常..."); return; } if (minaLongConnectManager == null) { startThreadCreateConnect(); } else { if (minaLongConnectManager.connectIsNull() && minaLongConnectManager.isNeedRestart()) { L.i("session已關閉,須要從新建立一個session"); minaLongConnectManager.startConnect(); } else { L.i("長鏈接已關閉,須要重開一個線程來從新建立長鏈接"); startThreadCreateConnect(); } } } } private final AtomicInteger mCount = new AtomicInteger(1); private void startThreadCreateConnect() { if (UserConfig.getUserInfo().getB3Key() != null && UserConfig.getUserInfo().getSessionKey() != null) { System.gc(); new Thread(new Runnable() { @Override public void run() { // 執行具體啓動長鏈接操做 minaLongConnectManager = MinaLongConnectManager.getInstance(context); minaLongConnectManager.crateLongConnect(); } }, "longConnectThread" + mCount.getAndIncrement()).start(); } } private void closeConnect() { if (minaLongConnectManager != null) { minaLongConnectManager.closeConnect(); } minaLongConnectManager = null; // 中止長鏈接服務LongConnService stopSelf(); } @Override public IBinder onBind(Intent intent) { throw new UnsupportedOperationException("Not yet implemented"); } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 而下面的代碼就是長鏈接的具體實現操做,具體的代碼有相關注釋說明
/** * 具體實現長鏈接的管理對象 **/ public class MinaLongConnectManager { private static final String TAG = MinaLongConnectManager.class.getSimpleName(); /** * 服務器端口號 */ public static final int DEFAULT_PORT = 18156; /** * 鏈接超時時間,30 seconds */ public static final long SOCKET_CONNECT_TIMEOUT = 30 * 1000L; /** * 長鏈接心跳包發送頻率,60s */ public static final int KEEP_ALIVE_TIME_INTERVAL = 60; private static Context context; private static MinaLongConnectManager minaLongConnectManager; private static NioSocketConnector connector; private static ConnectFuture connectFuture; public static IoSession session; private static ExecutorService executorService = Executors.newSingleThreadExecutor(); /** * 長鏈接是否正在鏈接中... */ private static boolean isConnecting = false; private MinaLongConnectManager() { EventBus.getDefault().register(this); } public static synchronized MinaLongConnectManager getInstance(Context ctx) { if (minaLongConnectManager == null) { context = ctx; minaLongConnectManager = new MinaLongConnectManager(); } return minaLongConnectManager; } /** * 檢查長鏈接的各類對象狀態是否正常,正常狀況下無需再建立 * * @return */ public boolean checkConnectStatus() { if (connector != null && connector.isActive() && connectFuture != null && connectFuture.isConnected() && session != null && session.isConnected()) { return true; } else { return false; } } public boolean connectIsNull() { return connector != null; } /** * 建立長鏈接,配置過濾器鏈和心跳工廠 */ public synchronized void crateLongConnect() { // 若是是長鏈接正在建立中 if (isConnecting) { L.i("長鏈接正在建立中..."); return; } if (!Config.isNetworkConnected(context)) { L.i("檢測到網絡未打開,沒法正常啓動長鏈接,直接return..."); return; } // 檢查長鏈接的各類對象狀態是否正常,正常狀況下無需再建立 if (checkConnectStatus()) { return; } isConnecting = true; try { connector = new NioSocketConnector(); connector.setConnectTimeoutMillis(SOCKET_CONNECT_TIMEOUT); if (L.isDebug) { if (!connector.getFilterChain().contains("logger")) { // 設置日誌輸出工廠 connector.getFilterChain().addLast("logger", new LoggingFilter()); } } if (!connector.getFilterChain().contains("codec")) { // 設置請求和響應對象的編解碼操做 connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(new LongConnectProtocolFactory())); } // 建立心跳工廠 ClientKeepAliveMessageFactory heartBeatFactory = new ClientKeepAliveMessageFactory(); // 當讀操做空閒時發送心跳 KeepAliveFilter heartBeat = new KeepAliveFilter(heartBeatFactory, IdleStatus.READER_IDLE); // 設置是否將事件繼續往下傳遞 heartBeat.setForwardEvent(true); // 設置心跳包請求後超時無反饋狀況下的處理機制,默認爲關閉鏈接,在此處設置爲輸出日誌提醒 heartBeat.setRequestTimeoutHandler(KeepAliveRequestTimeoutHandler.LOG); //設置心跳頻率 heartBeat.setRequestInterval(KEEP_ALIVE_TIME_INTERVAL); if (!connector.getFilterChain().contains("keepAlive")) { connector.getFilterChain().addLast("keepAlive", heartBeat); } if (!connector.getFilterChain().contains("reconnect")) { // 設置長鏈接重連過濾器,當檢測到Session(會話)斷開後,重連長鏈接 connector.getFilterChain().addLast("reconnect", new LongConnectReconnectionFilter()); } // 設置接收和發送緩衝區大小 connector.getSessionConfig().setReceiveBufferSize(1024); connector.getSessionConfig().setSendBufferSize(1024); // 設置讀取空閒時間:單位爲s connector.getSessionConfig().setReaderIdleTime(60); // 設置長鏈接業務邏輯處理類Handler LongConnectHandler longConnectHandler = new LongConnectHandler(this, context); connector.setHandler(longConnectHandler); } catch (Exception e) { e.printStackTrace(); closeConnect(); } startConnect(); } /** * 開始或重連長鏈接 */ public synchronized void startConnect() { if (connector != null) { L.i("開始建立長鏈接..."); boolean isSuccess = beginConnect(); // 建立成功後,修改建立中狀態 if (isSuccess) { isNeedRestart = false; if (context != null) { // 長鏈接啓動成功後,主動拉取一次消息 LoopRequest.getInstance(context).sendLoopRequest(); } } else { // 啓動輪詢服務 startLoopService(); } isConnecting = false; // printProcessorExecutor(); } else { L.i("connector已爲null,不能執行建立鏈接動做..."); } } /** * 檢測MINA中線程池的活動狀態 */ private void printProcessorExecutor() { Class connectorClass = connector.getClass().getSuperclass(); try { L.i("connectorClass:" + connectorClass.getCanonicalName()); Field field = connectorClass.getDeclaredField("processor"); field.setAccessible(true); Object connectorObject = field.get(connector); if (connectorObject != null) { SimpleIoProcessorPool processorPool = (SimpleIoProcessorPool) connectorObject; Class processPoolClass = processorPool.getClass(); Field executorField = processPoolClass.getDeclaredField("executor"); executorField.setAccessible(true); Object executorObject = executorField.get(processorPool); if (executorObject != null) { ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executorObject; L.i("線程池中當前線程數:" + threadPoolExecutor.getPoolSize() + "\t 核心線程數:" + threadPoolExecutor.getCorePoolSize() + "\t 最大線程數:" + threadPoolExecutor.getMaximumPoolSize()); } } else { L.i("connectorObject = null"); } } catch (Exception e) { e.printStackTrace(); } } /** * 開始建立Session * * @return */ public boolean beginConnect() { if (session != null) { session.close(false); session = null; } if (connectFuture != null && connectFuture.isConnected()) { connectFuture.cancel(); connectFuture = null; } FutureTask<Boolean> futureTask = new FutureTask<>(new Callable<Boolean>() { @Override public Boolean call() { try { InetSocketAddress address = new InetSocketAddress(NetworkTask.getBASEURL(), DEFAULT_PORT); connectFuture = connector.connect(address); connectFuture.awaitUninterruptibly(3000L); session = connectFuture.getSession(); if (session == null) { L.i(TAG + "鏈接建立失敗...當前環境:" + NetworkTask.getBASEURL()); return false; } else { L.i(TAG + "長鏈接已啓動,鏈接已成功...當前環境:" + NetworkTask.getBASEURL()); return true; } } catch (Exception e) { return false; } } }); executorService.submit(futureTask); try { return futureTask.get(); } catch (Exception e) { return false; } } /** * 關閉鏈接,根據傳入的參數設置session是否須要從新鏈接 */ public synchronized void closeConnect() { if (session != null) { session.close(false); session = null; } if (connectFuture != null && connectFuture.isConnected()) { connectFuture.cancel(); connectFuture = null; } if (connector != null && !connector.isDisposed()) { // 清空裏面註冊的因此過濾器 connector.getFilterChain().clear(); connector.dispose(); connector = null; } isConnecting = false; L.i("長鏈接已關閉..."); } private volatile boolean isNeedRestart = false; public boolean isNeedRestart() { return isNeedRestart; } public void onEventMainThread(BaseEvent event) { if (event == null || TextUtils.isEmpty(event.getType())) return; if (EventBusConstant.EVENT_TYPE_NETWORK_STATUS.equals(event.getType())) { String status = (String) event.getExtraData(); // 當網絡狀態變化的時候請求startQuery接口 if (status != null && status.equals("open")) { if (isNeedRestart && UserConfig.getUserInfo().getB3Key() != null && UserConfig.getUserInfo().getSessionKey() != null) { L.i("檢測到網絡已打開且長鏈接處於關閉狀態,須要啓動長鏈接..."); Intent intent = new Intent(context, LongConnService.class); intent.setAction(LongConnService.ACTION); context.startService(intent); } } } } /** * 出現異常、session關閉後,接收事件進行長鏈接重連操做 */ public void onEventMainThread(LongConnectMessageEvent event) { if (event.getType() == LongConnectMessageEvent.TYPE_RESTART) { long currentTime = System.currentTimeMillis(); // 票據有效的狀況下進行重連長鏈接操做 if (UserConfig.getUserInfo().getB3Key() != null && UserConfig.getUserInfo().getSessionKey() != null && ((currentTime / 1000) < UserConfig.getUserInfo().getUnvalidSecs())) { // 等待2s後從新建立長鏈接 SystemClock.sleep(1000); if (Config.isNetworkConnected(context)) { L.i("出現異常狀況,須要自動重連長鏈接..."); startConnect(); } else { isNeedRestart = true; L.i("長鏈接出現異常,須要從新建立session會話..."); } } } else if (event.getType() == LongConnectMessageEvent.TYPE_CLOSE) { L.i("收到session屢次close的消息,此時須要關閉長鏈接,等待下次鬧鐘服務來啓動..."); closeConnect(); } } /** * 啓動輪詢服務 */ public void startLoopService() { // 啓動輪詢服務 // 暫時不考慮加入網絡狀況的判斷... if (!LoopService.isServiceRuning) { // 用戶是登陸態,啓動輪詢服務 if (UserConfig.isPassLogined()) { // 判斷當前長鏈接的狀態,若長鏈接已鏈接,則再也不開啓輪詢服務 if (MinaLongConnectManager.session != null && MinaLongConnectManager.session.isConnected()) { LoopService.quitLoopService(context); return; } LoopService.startLoopService(context); } else { LoopService.quitLoopService(context); } } } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325

以上是經過NIO實現App長鏈接的部分核心代碼,NIO中其實已經實現了長鏈接的核心流程,咱們須要作的就是按照其流程實現長鏈接,須要注意的是要處理好異常狀況,重連機制等。

當長鏈接建立成功以後須要從新拉取一次服務器端的長鏈接消息,而且這裏的長鏈接作了容錯處理,當長鏈接斷了以後須要有重連機制,一直啓動輪訓服務,當長鏈接修復以後輪訓服務退出。以上只是經過MINA框架實現的長鏈接操做的核心流程,還有一些長鏈接實現的操做細節這裏就不作過多的說明。

總結: 
基本上對於App來講長鏈接已是標配了,產品開發人員能夠根據具體的產品需求選擇不一樣的實現方式,通常而言使用第三方的推送服務已經能夠知足大部分的需求了,固然瞭如果相對技術有所追求的話也能夠選擇本身實現一套長鏈接服務,不過其中可能存在一些坑須要填,但願這裏的長鏈接實現可以對你們對長鏈接實現上有所幫助。

    • 能夠經過使用第三方長鏈接服務或者是本身實現鏈接的方式;

    • 自定義實現長鏈接能夠經過使用NIO或者是第三方NIO框架,好比MINA實現;

    • 長鏈接實現中經過心跳包的機制實現App與服務器的長時間鏈接;

    • 能夠經過鬧鐘的機制定時檢測長鏈接服務是否可靠,長鏈接是否出現異常等;

    • 爲了消息的及時性,在長鏈接出現異常狀況時可經過建立輪訓服務的機制實現對消息的獲取,待長鏈接恢復以後關閉輪訓服務;

相關文章
相關標籤/搜索