Android基于XMPP协议之asmack源码分析
来源:互联网 发布:mac修容 编辑:程序博客网 时间:2024/05/29 18:50
一、整体聊天功能实现的流程
1>与openfire服务器建立连接2>获取连接对象,建立输入输出流3>开启输入数据流,线程阻塞等待消息,发出消息4>关闭输入输出流,关闭连接
二、解析连接服务器的类XmppConnection
首先 XmppConnection调用connect方法连接服务器,这里调用connectUsingConfiguration(config)方法,这里我们看到了私有变量config,这里应该初始化了系统的配置信息,这里在初始化的时候调用了mConfiguration = new ConnectionConfiguration(HOST, PORT);下面接着看connectUsingConfiguration方法;
public void connect() throws XMPPException { // Establishes the connection, readers and writers connectUsingConfiguration(config); // Automatically makes the login if the user was previously connected successfully // to the server and the connection was terminated abruptly if (connected && wasAuthenticated) { // Make the login if (isAnonymous()) { // Make the anonymous login loginAnonymously(); } else { login(config.getUsername(), config.getPassword(), config.getResource()); } notifyReconnection(); } }
我们看到,这里获取的 this.socket存于XmppConnection中,每个XmppConnection对应的当前设备和服务器的链接,对应的一个socket对象,缓存于当前XmppConnection中,便于使用,这里新建socket连接后做了一个初始化连接的操作,即initConnection(),我们继续看这个操作
private void connectUsingConfiguration(ConnectionConfiguration config) throws XMPPException { Iterator<HostAddress> it = config.getHostAddresses().iterator(); List<HostAddress> failedAddresses = new LinkedList<HostAddress>(); boolean xmppIOError = false; while (it.hasNext()) { exception = null; HostAddress hostAddress = it.next(); String host = hostAddress.getFQDN(); int port = hostAddress.getPort(); try { if (config.getSocketFactory() == null) { this.socket = new Socket(host, port); } else { this.socket = config.getSocketFactory().createSocket(host, port); } } catch (UnknownHostException uhe) { String errorMessage = "Could not connect to " + host + ":" + port + "."; exception = new XMPPException(errorMessage, new XMPPError(XMPPError.Condition.remote_server_timeout, errorMessage), uhe); } catch (IOException ioe) { String errorMessage = "XMPPError connecting to " + host + ":" + port + "."; exception = new XMPPException(errorMessage, new XMPPError(XMPPError.Condition.remote_server_error, errorMessage), ioe); xmppIOError = true; } //此处省略部分代码 socketClosed = false; initConnection(); }
新建连接的socket后这里一开始便有一个initReaderAndWriter方法,这里初始化字符输入输出流, 这里创建了 packetWriter和packetReader ,用于向服务器发送和接收消息,当然下面对应的有程序异常后对于输入输出流和socket的关闭的操作,我们来看看packetWriter这个向服务器发送消息的类具体做了什么操作
private void initConnection() throws XMPPException { boolean isFirstInitialization = packetReader == null || packetWriter == null; compressionHandler = null; serverAckdCompression = false; initReaderAndWriter(); try { if (isFirstInitialization) { packetWriter = new PacketWriter(this); packetReader = new PacketReader(this); // If debugging is enabled, we should start the thread that will listen for if (config.isDebuggerEnabled()) { addPacketListener(debugger.getReaderListener(), null); if (debugger.getWriterListener() != null) { addPacketSendingListener(debugger.getWriterListener(), null); } } } else { packetWriter.init(); packetReader.init(); } packetWriter.startup(); packetReader.startup(); connected = true; if (isFirstInitialization) { // Notify listeners that a new connection has been established for (ConnectionCreationListener listener : getConnectionCreationListeners()) { listener.connectionCreated(this); } } } catch (XMPPException ex) { // An exception occurred in setting up the connection. Make sure we shut down the // readers and writers and close the socket. if (packetWriter != null) { try { packetWriter.shutdown(); } catch (Throwable ignore) { /* ignore */ } packetWriter = null; } if (packetReader != null) { try { packetReader.shutdown(); } catch (Throwable ignore) { /* ignore */ } packetReader = null; } if (reader != null) { try { reader.close(); } catch (Throwable ignore) { /* ignore */ } reader = null; } if (writer != null) { try { writer.close(); } catch (Throwable ignore) { /* ignore */} writer = null; } if (socket != null) { try { socket.close(); } catch (Exception e) { /* ignore */ } socket = null; } this.setWasAuthenticated(authenticated); chatManager = null; authenticated = false; connected = false; throw ex; // Everything stoppped. Now throw the exception. } }
三、PacketWriter(发送消息)
这里新建了一个消息队列,用于存放用户将要发送的消息,存放于这个消息列队中,然后初始化获取用户将要发送消息的接收过程init(),这里开启了一个线程writerThread调用writePackets方法,这个writerThread线程的开启在PacketWriter的startUp方法中,下面我们看下writePackets做了什么操作
protected PacketWriter(XMPPConnection connection) { this.queue = new ArrayBlockingQueue<Packet>(500, true); this.connection = connection; init(); }protected void init() { this.writer = connection.writer; done = false; writerThread = new Thread() { public void run() { writePackets(this); } }; writerThread.setName("Smack Packet Writer (" + connection.connectionCounterValue + ")"); writerThread.setDaemon(true); }
这里一开便调用了openStream,这里添加字符串去打开xmpp协议指定的输入输出流,以便于后面能发送数据,然后这里有一个while循环,循环的状态基本为阻塞状态,nextPacket去用户那里获取即将要发送的消息我们等会再看,如果拿到的消息不为空,则 writer.write(packet.toXML());直接传输到服务器,中间有一个 queue.clear();表示消息发送完成后清楚消息队列 writer.write(“”)结束消息的标志;完成后关闭输入输出流,接下来我们看下nextPacket方法具体的操作
void openStream() throws IOException { StringBuilder stream = new StringBuilder(); stream.append("<stream:stream"); stream.append(" to=\"").append(connection.getServiceName()).append("\""); stream.append(" xmlns=\"jabber:client\""); stream.append(" xmlns:stream=\"http://etherx.jabber.org/streams\""); stream.append(" version=\"1.0\">"); writer.write(stream.toString()); writer.flush(); } 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) { writer.write(packet.toXML()); if (queue.isEmpty()) { writer.flush(); } } } // Flush out the rest of the queue. If the queue is extremely large, it's possible // we won't have time to entirely flush it before the socket is forced closed // by the shutdown process. try { while (!queue.isEmpty()) { Packet packet = queue.remove(); writer.write(packet.toXML()); } writer.flush(); } catch (Exception e) { e.printStackTrace(); } // Delete the queue contents (hopefully nothing is left). queue.clear(); // Close the stream. try { writer.write("</stream:stream>"); writer.flush(); } catch (Exception e) { // Do nothing } finally { try { writer.close(); } catch (Exception e) { // Do nothing } } } catch (IOException ioe) { // The exception can be ignored if the the connection is 'done' // or if the it was caused because the socket got closed if (!(done || connection.isSocketClosed())) { done = true; // packetReader could be set to null by an concurrent disconnect() call. // Therefore Prevent NPE exceptions by checking packetReader. if (connection.packetReader != null) { connection.notifyConnectionError(ioe); } } } }
这里有一个循环,从消息队列中获取用户即将发送的消息,如果有,则从消息队列中取出来,没有则synchronized 同步中调用 queue.wait(),让当前的writerThread处于等待状态,等待用户将要发送的消息存入消息队列,这里我们看完了packetWriter,接下来我们看下packetReader
private Packet nextPacket() { Packet packet = null; // Wait until there's a packet or we're done. while (!done && (packet = queue.poll()) == null) { try { synchronized (queue) { queue.wait(); } } catch (InterruptedException ie) { // Do nothing } } return packet; }
三、PacketReader(接收消息)
同样这边的PacketReader构造方法中调用了初始化方法init,我们来看看,这里的主要两个方法parsePackets和resetParser,parsePackets则是为接收到的消息做解析的准备,暂时先放下,这里同样也是开启了一个线程 readerThread,resetParser则是初始化XML解析器和输入输出流parser.setInput(connection.reader),为我们parsePackets解析接收到的数据做铺垫,下面我们看下parsePackets方法,由于比较多,这里显示部分代码
protected void init() { done = false; connectionID = null; readerThread = new Thread() { public void run() { parsePackets(this); } }; readerThread.setName("Smack Packet Reader (" + connection.connectionCounterValue + ")"); readerThread.setDaemon(true); // Create an executor to deliver incoming packets to listeners. We'll use a single // thread with an unbounded queue. listenerExecutor = Executors.newSingleThreadExecutor(new ThreadFactory() { public Thread newThread(Runnable runnable) { Thread thread = new Thread(runnable, "Smack Listener Processor (" + connection.connectionCounterValue + ")"); thread.setDaemon(true); return thread; } }); resetParser(); }private void resetParser() { try { parser = XmlPullParserFactory.newInstance().newPullParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); parser.setInput(connection.reader); } catch (XmlPullParserException xppe) { xppe.printStackTrace(); } }
这里采用的pull解析xml数据,由于把这里的xml数据主要有三个节点,message、iq、presence,分别代表的是消息、请求响应、出席,我们先来看message节点的处理,这里调用的是PacketParserUtils.parseMessage,里面同样也是XmlPullParser解析器解析数据,拿到数据后将数据封装为Message对象后然后返回,获取id、to、from、type等数据,然后我们看看processPacket(packet)方法,具体做了哪些操作
public static Packet parseMessage(XmlPullParser parser) throws Exception { Message message = new Message(); String id = parser.getAttributeValue("", "id"); message.setPacketID(id == null ? Packet.ID_NOT_AVAILABLE : id); message.setTo(parser.getAttributeValue("", "to")); message.setFrom(parser.getAttributeValue("", "from")); message.setType(Message.Type.fromString(parser.getAttributeValue("", "type"))); String language = getLanguageAttribute(parser); return message; } private void parsePackets(Thread thread) { try { int eventType = parser.getEventType(); do { if (eventType == XmlPullParser.START_TAG) { int parserDepth = parser.getDepth(); ParsingExceptionCallback callback = connection.getParsingExceptionCallback(); if (parser.getName().equals("message")) { Packet packet; try { packet = PacketParserUtils.parseMessage(parser); } catch (Exception e) { String content = PacketParserUtils.parseContentDepth(parser, parserDepth); UnparsablePacket message = new UnparsablePacket(content, e); if (callback != null) { callback.handleUnparsablePacket(message); } continue; } processPacket(packet); } else if (parser.getName().equals("iq")) { IQ iq; try { iq = PacketParserUtils.parseIQ(parser, connection); } catch (Exception e) { String content = PacketParserUtils.parseContentDepth(parser, parserDepth); UnparsablePacket message = new UnparsablePacket(content, e); if (callback != null) { callback.handleUnparsablePacket(message); } continue; } processPacket(iq); } else if (parser.getName().equals("presence")) { Presence presence; try { presence = PacketParserUtils.parsePresence(parser); } catch (Exception e) { String content = PacketParserUtils.parseContentDepth(parser, parserDepth); UnparsablePacket message = new UnparsablePacket(content, e); if (callback != null) { callback.handleUnparsablePacket(message); } continue; } processPacket(presence); } }
这里出现listenerExecutor,存储了监听的消息池,然后调用了ListenerNotification通知了所有的消息监听器接收消息,并且自己去判断消息的类型的来源,而 collector.processPacket(packet)则是将当前接收到的消息存储到消息队列中,发送和接收的消息处于同一消息队列
private void processPacket(Packet packet) { if (packet == null) { return; } // Loop through all collectors and notify the appropriate ones. for (PacketCollector collector: connection.getPacketCollectors()) { collector.processPacket(packet); } // Deliver the incoming packet to listeners. listenerExecutor.submit(new ListenerNotification(packet)); } private class ListenerNotification implements Runnable { private Packet packet; public ListenerNotification(Packet packet) { this.packet = packet; } public void run() { for (ListenerWrapper listenerWrapper : connection.recvListeners.values()) { listenerWrapper.notifyListener(packet); } } } protected void processPacket(Packet packet) { if (packet == null) { return; } if (packetFilter == null || packetFilter.accept(packet)) { while (!resultQueue.offer(packet)) { // Since we know the queue is full, this poll should never actually block. resultQueue.poll(); } } }
- Android基于XMPP协议之asmack源码分析
- 基于XMPP协议的aSmack源码分析
- 基于XMPP协议的aSmack源码分析
- xmpp/aSmack源码分析(基于android客户端)
- 基于XMPP协议的aSmack源码分析【1】
- 基于XMPP协议的aSmack源码分析【2】PacketReader
- 基于XMPP协议的aSmack源码分析(链接)
- 基于XMPP协议的aSmack源码分析【1】
- 基于XMPP协议的aSmack源码分析【2】PacketReader
- 基于XMPP协议的aSmack源码分析【0】UML
- 基于XMPP协议的aSmack源码分析【3】register过程分析
- 基于XMPP协议的aSmack源码分析【3】register过程分析
- Android平台基于asmack实现XMPP协议中的PubSub机制
- 基于XMPP协议的Android IM研究(asmack)
- XMPP-Android基于openfire+asmack
- Android客户端基于XMPP的IM(openfire+asmack)的聊天工具之注册(二)
- Android客户端基于XMPP的IM(openfire+asmack)的聊天工具之登录(三)
- Xmpp协议 Asmack Android客户端 一些Bug的解决方法
- jquery表格动态增删改及取数据绑定数据完整方案
- 两个.c文件访问同一个全局变量的用法
- Mac Maven
- 实训--day19Python
- webview设置网页加载进度条和设置点击返回键
- Android基于XMPP协议之asmack源码分析
- Java 线程池艺术探索
- java.lang.NumberFormatException: null的解决方法
- 数组2
- leetcode第3个算法题目
- eclipse tomcat 插件
- Java日志框架梳理-Java日志框架总览
- 健康比什么都重要
- 图像处理(3)频率域滤波