zoukankan      html  css  js  c++  java
  • AndroidPn源码分析(二)

    接上篇:

    (一)客户端与服务器建立连接

    上一篇写到ClientSession createClientSession这里,创建一个客户端的session。在SessionManager类中创建了session之后,这里拼接了两个xml内容的text。一个是Build the start packet response,创建一个头条包,作为回应。另外一个是:XMPP 1.0 needs stream features,是xmpp1.0所需要的文件结构。两个消息的格式内容如下:

    <?xml version='1.0' encoding='UTF-8'?><stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" from="127.0.0.1" 
    id
    ="bdef9c6a" xml:lang="en" version="1.0">
    <stream:features>
    <starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"></starttls>
    <auth xmlns="http://jabber.org/features/iq-auth"/>
    <register xmlns="http://jabber.org/features/iq-register"/>
    </stream:features>

    然后,调用connection的deliverRawText方法,将这两个xml内容通过IOSession的writer方法,传输到mina里面。具体mina怎么处理,本人还没有研究过。

    到此,根据记录的log日志,此消息已经发布了。客户端与服务器建立连接的过程,这里已经完成。这里只是一部分,关于客户端消息的发送,这部分内容也应该不少。

    (二)服务器推送消息

    从服务器推送消息的时候,会调用NotificationManager类里面的方法,分别为广播和对单个用户发送。这里主要看广播。

    首先是构造IQ消息体,createNotificationIQ方法来构造。

        /**
         * Creates a new notification IQ and returns it.
         * 构造消息体格式
         */
        private IQ createNotificationIQ(String apiKey, String title,
                String message, String uri) {
            Random random = new Random();
            String id = Integer.toHexString(random.nextInt());
            // String id = String.valueOf(System.currentTimeMillis());
    
            Element notification = DocumentHelper.createElement(QName.get(
                    "notification", NOTIFICATION_NAMESPACE));
            notification.addElement("id").setText(id);
            notification.addElement("apiKey").setText(apiKey);
            notification.addElement("title").setText(title);
            notification.addElement("message").setText(message);
            notification.addElement("uri").setText(uri);
    
            IQ iq = new IQ();
            iq.setType(IQ.Type.set);
            iq.setChildElement(notification);
    
            return iq;
        }

    构造后的xml:

    <iq type="set" id="860-0" to="159b356f005e4710a1f1c8aa0547e4ce@127.0.0.1/AndroidpnClient">
        <notification xmlns="androidpn:iq:notification">
            <id>11218d6c</id>
            <apiKey>1234567890</apiKey>
            <title>你好</title>
            <message>你好啊</message>
            <uri></uri>
        </notification>
    </iq>            

    在ClientSession中查找指定用户名的session,如果存在,则发送此条IQ消息。调用connectin的deliver方法,通过ioSession.write(buffer),将xml信息传输给mina,并且将发送记录加1。

    客户端源码分析:

    客户端代码很简单的,依靠xmppmanager维持连接。这里说一下大概流程。

    当客户端推送消息过来的时候,NotificationReceiver类的onReceive方法接收到消息,在这里,这时候,已经获得了所有发过来的数据。在这里,已经可以做自己的事情了,因为已经有了需要的数据。不过貌似挺多人喜欢在notifier的notify方法中来进行处理。

    其他就是自己的业务了。

    还有一个是关于客户端的用户名和密码,这个默认是自动生成,也可以自动指定。在XMPPManager的run方法里面可以修改。修改后会出现一些问题,就是服务器端注册的时候,会出现用户名已经存在,重复插入的问题。这个需要在服务器端插入数据的时候修改一下代码,在UserServiceImpl中修改,为了业务需要,本人把hibernate修改为了mybatis,所以方法略有不同:

        public User saveUser(User user) throws UserExistsException {
            try {
                //修改为自己的用户登录
                try {
                    user=getUserByUsername(user.getUsername());
                    
                } catch (UserNotFoundException e) {
                    // TODO Auto-generated catch block
                    log.info(user.getUsername()+" is not exist in db ....");
                    userDao.saveUser(user);
                    e.printStackTrace();
                } 
            } catch (DataIntegrityViolationException e) {
                e.printStackTrace();
                log.warn(e.getMessage());
                throw new UserExistsException("User '" + user.getUsername()
                        + "' already exists!");
            } catch (EntityExistsException e) { // needed for JPA
                e.printStackTrace();
                log.warn(e.getMessage());
                throw new UserExistsException("User '" + user.getUsername()
                        + "' already exists!");
            } 
            return user;
        }

    这样就可以了。

    关于短线重连,网上也有很多解决方法,是客户端加一行代码:

     private void addTask(Runnable runnable) { 
             Log.d(LOGTAG, "addTask(runnable)..."); 
             taskTracker.increase(); 
             synchronized (taskList) { 
                 if (taskList.isEmpty() && !running) { 
                     running = true; 
                    futureTask = taskSubmitter.submit(runnable); 
                    if (futureTask == null) { 
                        taskTracker.decrease(); 
                     } 
                 } else { 
                 //解决服务器端重启后,客户端不能成功连接androidpn服务器 
                 runTask(); 
                     taskList.add(runnable); 
                 } 
             } 
             Log.d(LOGTAG, "addTask(runnable)... done"); 
        } 

    当然,其他还有许多细节,先到这里,感谢网上那么多博主写的一些资料。

  • 相关阅读:
    P2610 [ZJOI2012]旅游
    P2323 [HNOI2006]公路修建问题
    P3629 [APIO2010]巡逻
    ARC059F
    AGC004D Teleporter
    p3203 弹飞绵羊
    bzoj5450 轰炸
    bzoj4313 三维积木
    cf123E Maze
    bzoj4423 [AMPPZ2013]Bytehattan
  • 原文地址:https://www.cnblogs.com/juepei/p/3904357.html
Copyright © 2011-2022 走看看