smack 源码分析- PacketWriter (android上实现长连接)
上一篇smack 源码分析一(android上实现长连接) 整体分析了smack长连接的流程, 本篇将详细介绍PacketWriter.
PacketWriter是在上篇在介绍initConnection()方法中创建的,那么详细看下PacketWriter的实现:
protected PacketWriter(XMPPConnection connection) { this.queue = new ArrayBlockingQueue<Packet>(500, true); this.connection = connection; init(); } protected void init() { this.writer = connection.writer; done = false; lastActive = System.currentTimeMillis(); //开辟一个写网络流数据线程 writerThread = new Thread() { public void run() { writePackets(this); } }; writerThread.setName("Smack Packet Writer (" + connection.connectionCounterValue + ")"); writerThread.setDaemon(true); } /** * 向网络流写数据包 * @param thisThread */ private void writePackets(Thread thisThread) { try { // Open the stream. openStream(); // Write out packets from the queue. while (!done && (writerThread == thisThread)) { Packet packet = nextPacket(); if (packet != null) { synchronized (writer) { writer.write(packet.toXML()); writer.flush(); // Keep track of the last time a stanza was sent to the // server lastActive = System.currentTimeMillis(); } } } synchronized (writer) { while (!queue.isEmpty()) { Packet packet = queue.remove(); writer.write(packet.toXML()); } writer.flush(); } queue.clear(); writer.write("</stream:stream>"); writer.flush(); } catch (IOException ioe) { if (!done) { done = true; connection.packetReader.notifyConnectionError(ioe); } } finally { try { if (writer != null) { writer.close(); } } catch (Exception e) { } } }
在PacketWriter构造方法中传递参数XMPPConnection作为PacketWriter成员变量, 然后调用init()方法 ,而在init()方法中, 开辟一个线程专门用于向服务器写数据. writerPackets()里面是一个无限循环体, 不断的从消息队列queue读取pakcet消息读取完之后调用wirter.flush()方法, 将消息发送的服务器. 这个过程就完成了终端完服务器发送消息的过程.
接下来是, 终端如何与云端保持连接呢, 那肯定是发送心跳包咯. 我们来看下PacketWriter是如何发送心跳包的:
/** * A TimerTask that keeps connections to the server alive by sending a space * character on an interval. */ private class KeepAliveTask implements Runnable { private int delay; private Thread thread; public KeepAliveTask(int delay) { this.delay = delay; } protected void setThread(Thread thread) { this.thread = thread; } public void run() { try { // Sleep 15 seconds before sending first heartbeat. This will give time to // properly finish TLS negotiation and then start sending heartbeats. Thread.sleep(15000); } catch (InterruptedException ie) { // Do nothing } while (!done && keepAliveThread == thread) { synchronized (writer) { // Send heartbeat if no packet has been sent to the server for a given time if (System.currentTimeMillis() - lastActive >= delay) { try { writer.write(" "); writer.flush(); } catch (Exception e) { // Do nothing } } } try { // Sleep until we should write the next keep-alive. Thread.sleep(delay); } catch (InterruptedException ie) { // Do nothing } } } }
心跳发送类: KeepAliveTask 是一个自定义线程, 它定义了心跳发送的时间间隔, 在其run方法里面一个无限循环体每隔delay毫秒就向云端发发送一个空消息(当然, 如需要你可以根据项目需求自定义一个心跳包). 整个PacketWriter介绍基本到此结束, 在你应用登陆成功之后就可以开启KeepAliveTask 线程定时发送心跳包到服务器保持终端与服务器的长连接.