2

我在我的 Android 应用程序中使用 aSmack 与我的 XMPP 服务器通信,并且我打开了 Smack 的调试,这样我就可以看到所有 XML 的来/去。我的问题是我正在使用 PacketListener 来获取服务器对我发送的包的响应,但是当我调用 Packet 的 toXML() 方法时,我得到了一个奇怪的输出。
下面的课程和更多详细信息。

我的服务器有 XEP-0136 实现,由于 aSmack 还没有它的代码,所以我正在制作 IQ 数据包并发送它,这是按预期工作的,正如您在我的 XML 数据包下面看到的那样,如XEP-0136,检索集合列表(http://xmpp.org/extensions/xep-0136.html#manage-list

xml包发送到服务器

06-13 14:11:21.769: D/SMACK(3018): 02:11:21 PM SENT (1079273464): 
<iq id="john@company.com/Smack/Conversations" type="get">
  <list with="john@company.com" xmlns="urn:xmpp:archive">
    <set xmlns="http://jabber.org/protocol/rsm">
      <max>30</max>
    </set>
  </list>
</iq>

为了创建这个 XML,我得到了 XEP-0136 中设计的 XML Schema 文件,并在 SimpleXML lib 的帮助下映射了所有元素,这是我用来创建和发送包的代码:

XMPPService.java

private static final int MAX_LIST = 30;

public void getConversations(String email, BaseActivity activity)
{
    if (isAuthenticated())
    {
        String packetId = connection.getUser() + "/Conversations";
        Set set = new Set();
        set.setMax(MAX_LIST);

        List list = new List();
        list.setWith(email);
        list.setSet(set);

        final IQ iq = new IQ();
        iq.setList(list);
        iq.setType(IQType.get);
        iq.setId(packetId);

        PacketIDFilter filter = new PacketIDFilter(packetId);
        connection.addPacketListener(new ChatListListener(activity), filter);
        sendPacket(iq);
    }
}

public void sendPacket(IQ iq)
{
    if (isAuthenticated())
    {
        connection.sendPacket(new IQPacket(iq));
    }
}

IQPacket.java

public class IQPacket extends Packet {

 private IQ iq;

 public IQPacket(IQ iq)
 {
    this.iq = iq;
 }

 public IQPacket(Packet packet, IQ iq)
 {
    super(packet);
    this.iq = iq;
 }

 @Override
 public String toXML()
 {
    StringWriter writer = new StringWriter();
    Serializer serializer = new Persister();
    try
    {
        serializer.write(iq, writer);
        return writer.getBuffer().toString();
    } catch (Exception e)
    {
        Log.e("COMPANY", "Error serializing xml", e);
    }
    return null;
 }

}

正如我所说,这部分有效,我的问题是监听器,当我为收到的包调用 toXML() 方法时,我无法获得有关聊天的重要信息,但是 Smack 调试输出向我打印所有我期待的信息如下所示:

接收到的 XML 的 Smack 调试

06-13 14:11:21.989: D/SMACK(3018): 02:11:21 PM RCV  (1079273464): 
<iq type="result" id="john@company.com/Smack/Conversations" to="john@company.com/Smack">
  <list xmlns="urn:xmpp:archive">
    <chat with="anotheruser@company.com" start="2013-06-10T13:19:25.000Z"/>
    <chat with="yetanotheruser@company.com" start="2013-06-10T13:36:50.876Z"/>
    <set xmlns="http://jabber.org/protocol/rsm">
      <first index="0">2</first>
      <last>3</last>
      <count>9</count>
    </set>
  </list>
</iq>

这个 XML 也是预期的答案,因为我将所有这些元素都映射为 JavaBeans,但这是我在 ChatListener 上收到 Packet 并调用 toXML() 方法时得到的结果:

06-13 14:11:22.009: I/System.out(3018): 
<iq id="john@company.com/Smack/Conversations" to="john@company.com/Smack" type="result">nullnullnullnullnullnull2nullnull3nullnull9nullnull</iq>

ChatListListener.java

public class ChatListListener implements PacketListener {

 private BaseActivity activity;

 public ChatListListener(BaseActivity activity)
 {
    this.activity = activity;
 }

 @Override
 public void processPacket(Packet packet)
 {
    activity.notifyPacketReceived();
    System.out.println(packet.toXML());
 }
}

Packet来自 org.jivesoftware.smack.packet.Packet,所以它是来自 aSmack 库的默认 Packet 。

所以我的问题是,我与 Smack 调试器有何不同?我查看了它的代码,我看到它还从数据包中调用 toXML() 方法并添加一个 ReceiveListener。我的想法是在调用 toXML() 之后,我可以使用 SimpleXML 将其转换为我映射的 IQ.java 并开始使用它的信息。

编辑

添加更多信息。因此,在查找 Smack 代码以及它如何处理收到的包之后,我发现也许我应该使用 IQProvider。所以我注册了我的 IQProvider

ProviderManager.getInstance().addIQProvider("list", "urn:xmpp:archive", new ListIQProvider());

之后,我在我的 IQProvider 的方法parseIQ(XmlPullParser arg0)上设置了一个断点,并且包实际上正在发送到我的提供者,但它仍然包含所有这些 null 元素。我现在有点迷茫,因为我需要它来继续工作,我将继续研究 Smack 源代码。

4

2 回答 2

4

经过大量研究和查找 Smack 资源后,我找到了解决方案。步骤是,添加 IQProvider,为您的数据包添加 PacketListener,发送数据包。等待 IQProvider 上的数据包,解析它,然后在 Listener 上获得响应。

所以addIQProvideraddPacketListener都是正确的,问题是,我需要对我的 ListIQProvider 上的 XML 进行完整解析,这听起来很简单,但我花了一些时间才弄清楚。

public class ListIQProvider implements IQProvider {

 public ListIQProvider()
 {
 }

 @Override
 public IQ parseIQ(XmlPullParser parser) throws Exception
 {
    Logger.d(String.format("Received iq packet, namespace[%s], name[%s]", parser.getNamespace(), parser.getName()));
    ListIQ iq = new ListIQ();
    ListIQ.Set set = new Set();
    boolean done = false;

    String with = "", start = "";
    while (!done)
    {
        int eventType = parser.next();
        if (eventType == XmlPullParser.START_TAG)
        {
            if (parser.getName().equals("chat"))
            {
                with = parser.getAttributeValue("", "with");
                start = parser.getAttributeValue("", "start");
                iq.addChat(new Chat(with, start));
            }
            else if (parser.getName().equals("first"))
            {
                int index = parseInt(parser.getAttributeValue("", "index"));
                set.setIndexAtt(index);
                int first = parseInt(parser.nextText());
                set.setFirst(first);
            }
            else if (parser.getName().equals("last"))
            {
                int last = parseInt(parser.nextText());
                set.setLast(last);
            }
            else if (parser.getName().equals("count"))
            {
                int count = parseInt(parser.nextText());
                set.setCount(count);
            }
        }
        else if (eventType == XmlPullParser.END_TAG)
        {
            if (parser.getName().equals("list"))
            {
                iq.setSet(set);
                done = true;
            }
        }
    }

    return iq;
 }

 private int parseInt(String integer)
 {
    return Integer.parseInt((integer != null ? integer : "0"));
 }
}

之后,我在ChatListListener上要做的就是将Packet转换为我的ListIQ类。而已。这里的问题是,在我的ChatListListener上接收到的数据包与在我的ListIQProvider上的parseIQ方法上返回的数据包相同。所以在这个问题/答案中,我们几乎拥有 XEP-0136 所需的一切,或者至少开始使用它。由于我没有在网络上找到任何好的和简单的资源来帮助我,我在这里分享我的。下面是 ListIQ 类:

public class ListIQ extends IQ {

 private List<Chat> chats;

 private Set set;

 public ListIQ()
 {
    this.chats = new ArrayList<ListIQ.Chat>();
 }

 public Set getSet()
 {
    return set;
 }

 public void setSet(Set set)
 {
    this.set = set;
 }

 public void addChat(Chat chat)
 {
    chats.add(chat);
 }

 public List<Chat> getChats()
 {
    return chats;
 }

 @Override
 public String getChildElementXML()
 {
    StringBuilder builder = new StringBuilder("<list xmlns=\"urn:xmpp:archive\">");
    for (Chat chat : chats)
    {
        builder.append(chat.toXml());
    }
    builder.append(set.toXml());
    builder.append("</list>");
    return builder.toString();
 }

 public static class Chat {
    private String with;
    private String start;

    public Chat()
    {
    }

    public Chat(String with, String start)
    {
        this.with = with;
        this.start = start;
    }

    public String getWith()
    {
        return with;
    }

    public void setWith(String with)
    {
        this.with = with;
    }

    public String getStart()
    {
        return start;
    }

    public void setStart(String start)
    {
        this.start = start;
    }

    public String toXml()
    {
        StringBuilder builder = new StringBuilder("<chat with=\"");
        builder.append(with).append("\"");
        builder.append(" start=\"");
        builder.append(start);
        builder.append("\"/>");
        return builder.toString();
    }

 }

 public static class Set {
    private int last;
    private int count;
    private int indexAtt;
    private int first;

    public Set()
    {
    }

    public int getLast()
    {
        return last;
    }

    public void setLast(int last)
    {
        this.last = last;
    }

    public int getCount()
    {
        return count;
    }

    public void setCount(int count)
    {
        this.count = count;
    }

    public int getIndexAtt()
    {
        return indexAtt;
    }

    public void setIndexAtt(int indexAtt)
    {
        this.indexAtt = indexAtt;
    }

    public int getFirst()
    {
        return first;
    }

    public void setFirst(int first)
    {
        this.first = first;
    }

    public String toXml()
    {
        StringBuilder builder = new StringBuilder("<set xmlns=\"http://jabber.org/protocol/rsm\">");
        builder.append("<first index=\"").append(indexAtt).append("\">").append(first).append("</first>");
        builder.append("<last>").append(last).append("</last>");
        builder.append("<count>").append(count).append("</count>");
        builder.append("</set>");
        return builder.toString();
    }
 }

}
于 2013-06-17T12:28:57.683 回答
1

嗨,我也遇到了同样的问题,我可以说这段代码有效,我只会改变你为服务器构建列表查询的方式。(至少在我的服务器上)

final IQ iq = new IQ()
    {

        @Override public String getChildElementXML()
        {

            return "<list xmlns='urn:xmpp:archive'/>";

        }
    };

    iq.setType(IQ.Type.GET);

    PacketTypeFilter filter = new PacketTypeFilter(Packet.class);


    connection.addPacketListener(new PacketListener()
    {
        @Override public void processPacket(Packet packet)
        {
            Log.i(TAG, packet.toXML());
        }
    }, filter);

    connection.sendPacket(iq);

正如您之前所说,这似乎很容易,但文档并不清楚如何构建 IQ 对象。我希望它有所帮助。

于 2014-02-12T09:07:18.860 回答