而本文中咱们将讲解一下App的长链接实现。通常而言长链接已是App的标配了,推送功能的实现基础就是长链接,固然了咱们也能够经过轮训操做实现推送功能,可是轮训通常及时性比较差,并且网络消耗与电量销毁比较多,所以通常推送功能都是经过长链接实现的。java
那么如何实现长链接呢?如今通常有这么几种实现方式:服务器
使用第三方的长链接服务;网络
经过NIO等方案实现长链接服务;session
经过MINA等第三方框架实现长链接;框架
介绍:这是最简单的方式,咱们能够经过接入极光推送,百度推送,友盟等第三方服务实现长链接,经过接入第三方的API咱们能够很方便的接入第三方的长链接,推送服务,可是这种方式定制化程度不太好,若是对长链接服务不是要求特别高,对定制化要求不是很高的话基本能够考虑这种方式(目前主流的App都是使用第三方的长链接服务)
优点:简单,方便
劣势:定制化程度不高ide
介绍:经过NIO的方式实现长链接,这种方式对技术要求程度比较高,基本都是经过java API实现长链接,实现心跳包,实现异常状况的容错等操做,能够说经过NIO实现长链接对技术要求很高,通常若是没有成行的技术方案比建议这么作,就算实现了长链接,后期链接的维护,对电量,流量的损耗等都须要持续的优化。
优点:定制化比较高
劣势:技术要求高,须要持续的维护oop
介绍:MINA是一个第三方的NIO框架,该框架实现了一整套的长链接机制,包括长链接的创建,心跳包的实现,异常机制的容错等。使用MINA实现长链接能够定制化的实现一些特有的功能,而且比NIO方案较为简单,由于其已经封装了一些长链接的特有机制,好比心跳包,容错等。
优点:可定制,较NIO方法简单
劣势:也须要必定的技术储备优化
在咱们的Android客户端中长链接的实现机制采用–MINA方式。这里多说一句,一开始的长链接采用的是NIO方案,可是采用这种方案以后踩了不少坑,包括心跳,容错等机制都是本身写的,因此耗费了大量的时间,并且对手机电量的消耗很大,最后决定使用MINA NIO框架从新实现一遍长链接,后来通过实测,长链接的稳定性还有耗电量,流量的消耗等指标方面有了很大的提升。ui
下面我将简单的介绍一下经过NIO实现长链接的具体流程:this
引入MINA jar包,在App启动页面,登陆页面启动长链接;
建立后台服务,在服务中建立MINA长链接;
实现心跳包,重写一些容错机制;
实现长链接断了以后的重连机制,而且重连次数有限制不能一直重连;
长链接断了以后实现轮训操做,这里的轮训服务只有在长链接断了以后才启动,在长链接恢复以后关闭;
如下就是在长链接中实现的具体代码:
/**
* 在Application的onCreate方法中执行启动长链接的操做
**/
@Override
public void onCreate() {
... // 登陆后开启长链接 if (UserConfig.isPassLogined()) { L.i("用户已登陆,开启长链接..."); startLongConn(); } ... }
/** * 开始执行启动长链接服务 */ 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); }
/** * 后台长链接服务 **/ 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"); } }
/** * 具体实现长链接的管理对象 **/ 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); } } } }