SoFunction
Updated on 2025-03-02

Android XMPP communication custom Packet&Provider

summary

During the xmpp communication process, the Packet components provided in Asmack are three types: IQ, Message, and Presence: IQ is used for querying Message is used for message delivery Presence is used for state interaction They are all subclasses of Packets, which are essentially used to encapsulate messages into response XML format for data exchange, and they all have good scalability.

Introduction

Let's take the open source project androidpn as an example:

androidpn (Android Push Notification) is a Java open source Android push notification implementation based on the XMPP protocol. It contains the complete client and server side.

AndroidPn includes the Server side and Client side, and the project names are androidpn-server and androidpn-client.

In fact, AndroidPn-server can support apps to run on iOS, UWP, Windows, Linux and other platforms, not limited to Android. Therefore, it seems that the name of the project will be changed to XPN (Xmpp Push Notification) and it seems to be more in line with its actual scenario. When we come to Android Push Notification in the future, we will collectively refer to XPN.

XNP Current Status

The project has been stopped from being updated since January 2014. In addition, the asmack project has also stopped being updated. The author recommends using openfire's official smack4.0, but in this way, the jars will be introduced, which are particularly large. Of course, we downloaded the newer stable version of asmack8.10.0, which can be used for learning and extension.

Project related download site

- asmack project address

- asmack image address

androidpn(XPN)-- AndroidPN download address

1. About Packet Data Packet

Packet is the parent class of IQ, Message, and Presence, used to implement message component types.

Message Semantics message

message is a basic method of pushing messages, which does not require response. Mainly used in applications such as IM, groupChat, alert and notification.
The main attributes are as follows:

type attribute, it has 5 main types:
normal: Similar to email, the main feature is that it does not require response;
chat: Similar to live chats of friends in QQ, the main feature is real-time communication;
groupchat: similar to group chat in a chat room;
headline: used to send alert and notification;
error: If an error occurs when sending a message, the wrong entity will use this category to notify the sender of an error;

to attribute: Identifies the receiver of the message.

from attribute: refers to the name or label of the sender. To prevent address leakage, this address is usually filled in by the sender's server, not the sender.
payload: for example body, subject

<message to="lily@/contact" 
 type="chat" > 
  <body> Hello,Are you busy</body> 
</message>

Attendance to information semantics presentation

Presence is used to indicate the user's status, such as online, away, dnd (do not disturb), etc. When changing one's state, a Presence element will be inserted into the context of the stream to indicate one's state. To accept a present message, you must go through an authorization process called present subscription.
property:

type attribute, not required. There are the following categories
subscribe: Subscribe to the status of other users
probe: Request to get the status of other users
unavailable: unavailable, offline status

to attribute: Identifies the receiver of the message.

from attribute: refers to the name or label of the sender.

Payload:
    show:
chat: Chat
away: leave temporarily
xa: eXtend Away, leave for a long time
dnd: Do not disturb
status: Free format, readable text. Also called rich present or extended presence, it is often used to express the user's current mood, activities, songs you listen to, videos you watch, chat rooms you are in, web pages you visit, games you play, etc.
priority: range -128~127. A resource with a high priority can accept messages sent to the bare JID, while a resource with a low priority cannot. Priority is
<presence from="alice@/pda">
  <show>xa</show>
  <status>down the rabbit hole!</status>
</presence>

IQ Semantics

A request/response mechanism that sends a request from one entity, and another entity accepts a request and responds. For example, the client inserts an element in the context of the stream, requests the Server to get its own friend list, and the Server returns a one, which is the result of the request.
The main attribute is type. include:
Get: Get the current domain value. Similar to http get method.
Set: Set or replace the value of the get query. Similar to the http put method.
Result: Indicates that the previous query was successfully responded to. Similar to http status code 200.
Error: Error occurred in query and response.
<iq from="alice@/pda
   
    to="alice@
    type="get">
  <query xmlns="jabber:iq:roster"/>
</iq>

2. Custom Packet

Since the packets used by the server and the client are different, but the data formats they interact with are all xml, we can understand this process of the xml implementation process.

1. Define Packet encapsulation object

Due to the limitations of asmack tag parsing, we cannot customize the parsing unless the source code is modified. For simplicity, we can only inherit one of the existing tags.

I followed the project code NotificationIQ as an example. I did not inherit Packet, but IQ

import ;
/** 
 * This class represents a notifcatin IQ packet.
 *
 * @author Sehwan Noh (devnoh@)
 */
public class NotificationIQ extends IQ {
  private String id;
  private String apiKey;
  private String title;
  private String message;
  private String uri;
  public NotificationIQ() {
  }
  @Override
  public String getChildElementXML() {
    StringBuilder buf = new StringBuilder();
    ("<").append("notification").append(" xmlns=\"").append(
        "androidpn:iq:notification").append("\">");
    if (id != null) {
      ("<id>").append(id).append("</id>");
    }
    ("</").append("notification").append("> ");
    return ();
  }
  public String getId() {
    return id;
  }
  public void setId(String id) {
     = id;
  }
  public String getApiKey() {
    return apiKey;
  }
  public void setApiKey(String apiKey) {
     = apiKey;
  }
  public String getTitle() {
    return title;
  }
  public void setTitle(String title) {
     = title;
  }
  public String getMessage() {
    return message;
  }
  public void setMessage(String message) {
     = message;
  }
  public String getUri() {
    return uri;
  }
  public void setUri(String url) {
     = url;
  }
}

Among them, getChildElementXml() is a subclass of IQ, used to splice into direct points under <iq>.

public abstract class IQ extends Packet {
  private Type type = ;
  public IQ() {
    super();
  }
  public IQ(IQ iq) {
    super(iq);
    type = ();
  }
  /**
   * Returns the type of the IQ packet.
   *
   * @return the type of the IQ packet.
   */
  public Type getType() {
    return type;
  }
  /**
   * Sets the type of the IQ packet.
   *
   * @param type the type of the IQ packet.
   */
  public void setType(Type type) {
    if (type == null) {
       = ;
    }
    else {
       = type;
    }
  }
  public String toXML() {
    StringBuilder buf = new StringBuilder();
    ("<iq ");
    if (getPacketID() != null) {
      ("id=\"" + getPacketID() + "\" ");
    }
    if (getTo() != null) {
      ("to=\"").append((getTo())).append("\" ");
    }
    if (getFrom() != null) {
      ("from=\"").append((getFrom())).append("\" ");
    }
    if (type == null) {
      ("type=\"get\">");
    }
    else {
      ("type=\"").append(getType()).append("\">");
    }
    // Add the query section if there is one.
    String queryXML = getChildElementXML();
    if (queryXML != null) {
      (queryXML);
    }
    // Add the error sub-packet, if there is one.
    XMPPError error = getError();
    if (error != null) {
      (());
    }
    ("</iq>");
    return ();
  }
  /**
   * Returns the sub-element XML section of the IQ packet, or <tt>null</tt> if there
   * isn't one. Packet extensions <b>must</b> be included, if any are defined.<p>
   *
   * Extensions of this class must override this method.
   *
   * @return the child element section of the IQ XML.
   */
  public abstract String getChildElementXML();
  /**
   * Convenience method to create a new empty {@link Type#RESULT }
   * IQ based on a {@link Type#GET } or {@link Type#SET }
   * IQ. The new packet will be initialized with:<ul>
   *   <li>The sender set to the recipient of the originating IQ.
   *   <li>The recipient set to the sender of the originating IQ.
   *   <li>The type set to {@link Type#RESULT }.
   *   <li>The id set to the id of the originating IQ.
   *   <li>No child element of the IQ element.
   * </ul>
   *
   * @param iq the {@link Type#GET } or {@link Type#SET } IQ packet.
   * @throws IllegalArgumentException if the IQ packet does not have a type of
   *   {@link Type#GET } or {@link Type#SET }.
   * @return a new {@link Type#RESULT } IQ based on the originating IQ.
   */
  public static IQ createResultIQ(final IQ request) {
    if (!(() ==  || () == )) {
      throw new IllegalArgumentException(
          "IQ must be of type 'set' or 'get'. Original IQ: " + ());
    }
    final IQ result = new IQ() {
      public String getChildElementXML() {
        return null;
      }
    };
    ();
    (());
    (());
    (());
    return result;
  }
  /**
   * Convenience method to create a new {@link Type#ERROR } IQ
   * based on a {@link Type#GET } or {@link Type#SET }
   * IQ. The new packet will be initialized with:<ul>
   *   <li>The sender set to the recipient of the originating IQ.
   *   <li>The recipient set to the sender of the originating IQ.
   *   <li>The type set to {@link Type#ERROR }.
   *   <li>The id set to the id of the originating IQ.
   *   <li>The child element contained in the associated originating IQ.
   *   <li>The provided {@link XMPPError XMPPError}.
   * </ul>
   *
   * @param iq the {@link Type#GET } or {@link Type#SET } IQ packet.
   * @param error the error to associate with the created IQ packet.
   * @throws IllegalArgumentException if the IQ packet does not have a type of
   *   {@link Type#GET } or {@link Type#SET }.
   * @return a new {@link Type#ERROR } IQ based on the originating IQ.
   */
  public static IQ createErrorResponse(final IQ request, final XMPPError error) {
    if (!(() ==  || () == )) {
      throw new IllegalArgumentException(
          "IQ must be of type 'set' or 'get'. Original IQ: " + ());
    }
    final IQ result = new IQ() {
      public String getChildElementXML() {
        return ();
      }
    };
    ();
    (());
    (());
    (());
    (error);
    return result;
  }
  /**
   * A class to represent the type of the IQ packet. The types are:
   *
   * <ul>
   *   <li>
   *   <li>
   *   <li>
   *   <li>
   * </ul>
   */
  public static class Type {
    public static final Type GET = new Type("get");
    public static final Type SET = new Type("set");
    public static final Type RESULT = new Type("result");
    public static final Type ERROR = new Type("error");
    /**
     * Converts a String into the corresponding types. Valid String values
     * that can be converted to types are: "get", "set", "result", and "error".
     *
     * @param type the String value to covert.
     * @return the corresponding Type.
     */
    public static Type fromString(String type) {
      if (type == null) {
        return null;
      }
      type = ();
      if (().equals(type)) {
        return GET;
      }
      else if (().equals(type)) {
        return SET;
      }
      else if (().equals(type)) {
        return ERROR;
      }
      else if (().equals(type)) {
        return RESULT;
      }
      else {
        return null;
      }
    }
    private String value;
    private Type(String value) {
       = value;
    }
    public String toString() {
      return value;
    }
  }
}

Finally, data with the following structure can be generated

<iq from="">
 <nofitication xlns="">
<iq>

It's very simple to use in the project

().sendPacket(<NotificationIQ>niq)

Of course, the above just implements object->xml, and next we implement xml->data

2. Implement IQProvider

Let's take a look at the IQProvider source code first

public interface IQProvider {

  /**
   * Parse the IQ sub-document and create an IQ instance. Each IQ must have a
   * single child element. At the beginning of the method call, the xml parser
   * will be positioned at the opening tag of the IQ child element. At the end
   * of the method call, the parser <b>must</b> be positioned on the closing tag
   * of the child element.
   *
   * @param parser an XML parser.
   * @return a new IQ instance.
   * @throws Exception if an error occurs parsing the XML.
   */
  public IQ parseIQ(XmlPullParser parser) throws Exception;
}

Implement custom parsing tools

public class NotificationIQProvider implements IQProvider {
  public NotificationIQProvider() {
  }
  @Override
  public IQ parseIQ(XmlPullParser parser) throws Exception {
    NotificationIQ notification = new NotificationIQ();
    for (boolean done = false; !done;) {
      int eventType = ();
      if (eventType == 2) {
        if ("id".equals(())) {
          (());
        }
        if ("apiKey".equals(())) {
          (());
        }
        if ("title".equals(())) {
          (());
        }
        if ("message".equals(())) {
          (());
        }
        if ("uri".equals(())) {
          (());
        }
      } else if (eventType == 3
          && "notification".equals(())) {
        done = true;
      }
    }
    return notification;
  }
}

How to use it in the project

().addIQProvider("notification",
              "androidpn:iq:notification",
              new NotificationIQProvider());

The following call will be made in the PacketParserUtils class in asmack

 Object provider = ().getIQProvider(elementName, namespace);
          if (provider != null) {
            if (provider instanceof IQProvider) {
              iqPacket = ((IQProvider)provider).parseIQ(parser);
            }
            else if (provider instanceof Class) {
              iqPacket = (IQ)(elementName,
                  (Class<?>)provider, parser);
            }
          }