AndroidPN系列之二-- 离线消息的推送

一. 原理图

  1. 没实现离线消息推送功能前,项目的架构图如下

  2. 实现离线消息推送功能后,项目的架构图如下 - 版本一 - 适用于新闻等app工作环境,对离线消息的到达率要求不高的环境

  3. 实现离线消息推送功能后,项目的架构图如下 - 版本二 - 适用于IM等app工作环境,对离线消息的到达率要求99.99%

二. 版本一的实现

  1. 创建数据表 - Notification

    a. 创建实体类

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

package org.androidpn.server.model;

 

import javax.persistence.Column;

import javax.persistence.Entity;

import javax.persistence.GeneratedValue;

import javax.persistence.GenerationType;

import javax.persistence.Id;

import javax.persistence.Table;

 

@Entity

@Table(name = "notification")

public class Notification {

     

    @Id

     @GeneratedValue(strategy = GenerationType.AUTO)

    private long id;

     

    @Column(name = "api_key", length = 64)

    private String apiKey;

     

    @Column(name = "username", nullable = false, length = 64)

    private String username;

     

    @Column(name = "title", nullable = false, length = 64)

    private String title;

     

    @Column(name = "message", nullable = false, length = 1024)

    private String message;

     

    @Column(name = "uri", length = 256)

    private String uri;

     

    public Notification() {

         

    }

 

    public long getId() {

        return id;

    }

 

    public void setId(long id) {

        this.id = id;

    }

 

    public String getApiKey() {

        return apiKey;

    }

 

    public void setApiKey(String apiKey) {

        this.apiKey = apiKey;

    }

 

    public String getUsername() {

        return username;

    }

 

    public void setUsername(String username) {

        this.username = username;

    }

 

    public String getTitle() {

        return title;

    }

 

    public void setTitle(String title) {

        this.title = title;

    }

 

    public String getMessage() {

        return message;

    }

 

    public void setMessage(String message) {

        this.message = message;

    }

 

    public String getUri() {

        return uri;

    }

 

    public void setUri(String uri) {

        this.uri = uri;

    }

 

}

    b. 修改Hibernate的配置文件hibernate.cfg.xml

1

2

3

4

<!-- Mapping Files -->

<mapping class="org.androidpn.server.model.User" />

<!-- 消息映射 -->

<mapping class="org.androidpn.server.model.Notification" />

  2. Dao层封装

    a. 新建一个NotificationDao接口及它的实现NotificationDaoHibernate.java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

package org.androidpn.server.dao;

 

import java.util.List;

 

import org.androidpn.server.model.Notification;

 

public interface NotificationDao {

     

    void saveNotification(Notification notification);

     

    List<Notification> findNotificationsByUsername(String username);

     

    void deleteNotification(Notification notification);

}

  

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

package org.androidpn.server.dao.hibernate;

 

import java.util.List;

 

import org.androidpn.server.dao.NotificationDao;

import org.androidpn.server.model.Notification;

import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

 

public class NotificationDaoHibernate extends HibernateDaoSupport implements

        NotificationDao {

 

    public void saveNotification(Notification notification) {

        getHibernateTemplate().saveOrUpdate(notification);

        getHibernateTemplate().flush();

    }

 

    public void deleteNotification(Notification notification) {

        // TODO Auto-generated method stub

        getHibernateTemplate().delete(notification);

    }

 

    @SuppressWarnings("unchecked")

    public List<Notification> findNotificationsByUsername(String username) {

        // TODO Auto-generated method stub

        List<Notification> list = getHibernateTemplate().find("from Notification where username=?", username);

        if(list != null && list.size()>0) {

            return list;

        }

        return null;

    }

 

}

    b. 修改Spring的配置文件-spring-config.xml

1

2

3

4

5

6

7

8

9

10

11

12

<!-- =============================================================== -->

<!-- Data Access Objects -->

<!-- =============================================================== -->

 

<bean id="userDao" class="org.androidpn.server.dao.hibernate.UserDaoHibernate">

    <property name="sessionFactory" ref="sessionFactory" />

</bean>

     

<!-- 消息 -->

<bean id="notificationDao" class="org.androidpn.server.dao.hibernate.NotificationDaoHibernate">

    <property name="sessionFactory" ref="sessionFactory" />

</bean>

  3. Service层封装

    a. 新建一个NotificationService接口及它的实现NotificationServiceImpl.java

1

2

3

4

5

6

7

8

9

10

11

12

13

package org.androidpn.server.service;

 

import java.util.List;

 

import org.androidpn.server.model.Notification;

 

public interface NotificationService {

    void saveNotification(Notification notification);

 

    List<Notification> findNotificationsByUsername(String username);

 

    void deleteNotification(Notification notification);

}

  

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

package org.androidpn.server.service.impl;

 

import java.util.List;

 

import org.androidpn.server.dao.NotificationDao;

import org.androidpn.server.model.Notification;

import org.androidpn.server.service.NotificationService;

import org.apache.commons.logging.Log;

import org.apache.commons.logging.LogFactory;

 

public class NotificationServiceImpl implements NotificationService {

     

    protected final Log log = LogFactory.getLog(getClass());

     

    private NotificationDao notificationDao;

     

    public NotificationDao getNotificationDao() {

        return notificationDao;

    }

 

    public void setNotificationDao(NotificationDao notificationDao) {

        this.notificationDao = notificationDao;

    }

 

    public void saveNotification(Notification notification) {

        // TODO Auto-generated method stub

        notificationDao.saveNotification(notification);

    }

 

    public List<Notification> findNotificationsByUsername(String username) {

        // TODO Auto-generated method stub

        return notificationDao.findNotificationsByUsername(username);

    }

 

    public void deleteNotification(Notification notification) {

        // TODO Auto-generated method stub

        notificationDao.deleteNotification(notification);

    }

 

}

    b. 修改Spring的配置文件-spring-config.xml

1

2

3

4

5

6

7

8

9

10

11

<!-- =============================================================== -->

<!-- Services -->

<!-- =============================================================== -->

 

<bean id="userService" class="org.androidpn.server.service.impl.UserServiceImpl">

    <property name="userDao" ref="userDao" />

</bean>

     

<bean id="notificationService" class="org.androidpn.server.service.impl.NotificationServiceImpl">

    <property name="notificationDao" ref="notificationDao" />

</bean>

    c. 修改ServiceLocator.java,提供对外调用的应用

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

package org.androidpn.server.service;

 

import org.androidpn.server.xmpp.XmppServer;

 

/**

 * This is a helper class to look up service objects.

 *

 * @author Sehwan Noh (devnoh@gmail.com)

 */

public class ServiceLocator {

 

    public static String USER_SERVICE = "userService";

     

    public static String NOTIFICATION_SERVICE = "notificationService";

 

    /**

     * Generic method to obtain a service object for a given name.

     *

     * @param name the service bean name

     * @return

     */

    public static Object getService(String name) {

        return XmppServer.getInstance().getBean(name);

    }

 

    /**

     * Obtains the user service.

     *

     * @return the user service

     */

    public static UserService getUserService() {

        return (UserService) XmppServer.getInstance().getBean(USER_SERVICE);

    }

     

    /**

     * Obtains the notification service.

     *

     * @return the notification service

     */

    public static NotificationService getNotificationService() {

        return (NotificationService) XmppServer.getInstance().getBean(NOTIFICATION_SERVICE);

    }

 

}

  4. 业务逻辑层实现

    a. 修改NotificationManager.java,添加一个存储消息Notification的方法

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

/**

* 存储推送消息

* @param apiKey

* @param username

* @param title

* @param message

* @param uri

*/

private void saveNotification(String apiKey, String username, String title,

            String message, String uri) {

    Notification notification = new Notification();

    notification.setApiKey(apiKey);

    notification.setUri(uri);

    notification.setUsername(username);

    notification.setTitle(title);

    notification.setMessage(message);

    // ServiceLocator.getNotificationService().saveNotification(notification);

        notificationService.saveNotification(notification);

}

    b. 修改发送推送消息逻辑

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

public void sendBroadcast(String apiKey, String title, String message,

            String uri) {

    log.debug("sendBroadcast()...");

    IQ notificationIQ = createNotificationIQ(apiKey, title, message, uri);

         

    // 通过遍历数据库的用户,发送推送消息

    List<User> allUser = userService.getUsers();

    for(User user : allUser) {

            ClientSession session = sessionManager.getSession(user.getUsername());

        if(session != null && session.getPresence().isAvailable()) {

            notificationIQ.setTo(session.getAddress());

            session.deliver(notificationIQ);

        } else {

            saveNotification(apiKey, user.getUsername(), title, message, uri);

        }

    }

         

    // 仅仅遍历在线用户

//  for (ClientSession session : sessionManager.getSessions()) {

//      if (session.getPresence().isAvailable()) {

//          notificationIQ.setTo(session.getAddress());

//          session.deliver(notificationIQ);

//      }

//  }

}

 

public void sendNotifcationToUser(String apiKey, String username,

            String title, String message, String uri) {

    log.debug("sendNotifcationToUser()...");

    IQ notificationIQ = createNotificationIQ(apiKey, title, message, uri);

    ClientSession session = sessionManager.getSession(username);

    if (session != null) {

        if (session.getPresence().isAvailable()) {

            notificationIQ.setTo(session.getAddress());

            session.deliver(notificationIQ);

        }

        // 如果用户在线但不可用时,则保存推送消息到数据库中

        else {

            saveNotification(apiKey, username, title, message, uri);

        }

    }

    // 如果用户不在线但不可用时,则保存推送消息到数据库中

    else {

        User user;

        try {

            // 通过用户名发送推送消息,在保存消息时,验证该用户名是否存储,防止存储无用数据

            user = userService.getUserByUsername(username);

            if (user != null) {

                saveNotification(apiKey, username, title, message, uri);

            }

        } catch (UserNotFoundException e) {

            // TODO Auto-generated catch block

            e.printStackTrace();

        }

    }

}

    c. 用户重新上线后,向其推送之前的存储在数据库的离线消息 - 修改PresenceUpdateHandler.java中的process()方法

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

public void process(Packet packet) {

    ClientSession session = sessionManager.getSession(packet.getFrom());

 

    try {

        Presence presence = (Presence) packet;

        Presence.Type type = presence.getType();

 

        if (type == null) { // null == available

            if (session != null

                        && session.getStatus() == Session.STATUS_CLOSED) {

                log.warn("Rejected available presence: " + presence + " - "

                            + session);

                return;

            }

 

            // 用户重新上线

            if (session != null) {

                session.setPresence(presence);

                if (!session.isInitialized()) {

                    // initSession(session);

                    session.setInitialized(true);

                }

 

                // 遍历消息数据库

                List<Notification> list = notificationService

                            .findNotificationsByUsername(session.getUsername());

                if (list != null && list.size() > 0) {

                    for(Notification notification : list) {

                        String apiKey = notification.getApiKey();

                        String title = notification.getTitle();

                        String message = notification.getMessage();

                        String uri = notification.getUri();

                            notificationManager.sendNotifcationToUser(apiKey, session.getUsername(), title, message, uri);

                        // 发送后将该消息从数据库表中删除

                            notificationService.deleteNotification(notification);

                    }

                }

 

            }

 

        } else if (Presence.Type.unavailable == type) {

 

            if (session != null) {

                session.setPresence(presence);

            }

 

        } else {

            presence = presence.createCopy();

            if (session != null) {

                presence.setFrom(new JID(null, session.getServerName(),

                            null, true));

                presence.setTo(session.getAddress());

            } else {

                JID sender = presence.getFrom();

                presence.setFrom(presence.getTo());

                presence.setTo(sender);

            }

            presence.setError(PacketError.Condition.bad_request);

            PacketDeliverer.deliver(presence);

        }

 

    } catch (Exception e) {

        log.error("Internal server error. Triggered by packet: " + packet,

                    e);

    }

}

三. 版本二的实现 - 版本一的增强版

  1. 为Notification添加一个UUID字段(回执的消息体就是该消息的uuid)

    @Column(name = "uuid", length = 64, nullable = false, unique = true)

    private String uuid;

    // setter - getter

  2. 为Dao及其实现层和Server层及其实现层添加一个根据uuid删除消息的方法

    void deleteNotificationByUUID(String uuid); -> 实现

1

2

3

4

5

6

7

8

9

10

@SuppressWarnings("unchecked")

public void deleteNotificationByUUID(String uuid) {

    // TODO Auto-generated method stub

    List<Notification> list = getHibernateTemplate().find(

                "from Notification where uuid=?", uuid);

    if (list != null && list.size() > 0) {

        Notification notification = list.get(0);

        deleteNotification(notification);

    }

}

  3. 修改相关业务逻辑

    a. 修改NotificationManager相关方法

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

package org.androidpn.server.xmpp.push;

 

import java.util.List;

import java.util.Random;

 

import org.androidpn.server.model.Notification;

import org.androidpn.server.model.User;

import org.androidpn.server.service.NotificationService;

import org.androidpn.server.service.ServiceLocator;

import org.androidpn.server.service.UserNotFoundException;

import org.androidpn.server.service.UserService;

import org.androidpn.server.xmpp.session.ClientSession;

import org.androidpn.server.xmpp.session.SessionManager;

import org.apache.commons.logging.Log;

import org.apache.commons.logging.LogFactory;

import org.dom4j.DocumentHelper;

import org.dom4j.Element;

import org.dom4j.QName;

import org.xmpp.packet.IQ;

 

/**

 * This class is to manage sending the notifcations to the users.

 *

 * @author Sehwan Noh (devnoh@gmail.com)

 */

public class NotificationManager {

 

    private static final String NOTIFICATION_NAMESPACE = "androidpn:iq:notification";

 

    private final Log log = LogFactory.getLog(getClass());

 

    private SessionManager sessionManager;

 

    private NotificationService notificationService;

 

    private UserService userService;

 

    /**

     * Constructor.

     */

    public NotificationManager() {

        sessionManager = SessionManager.getInstance();

        notificationService = ServiceLocator.getNotificationService();

        userService = ServiceLocator.getUserService();

    }

 

    /**

     * Broadcasts a newly created notification message to all connected users.

     *

     * @param apiKey

     *            the API key

     * @param title

     *            the title

     * @param message

     *            the message details

     * @param uri

     *            the uri

     */

    public void sendBroadcast(String apiKey, String title, String message,

            String uri) {

        log.debug("sendBroadcast()...");

        // IQ notificationIQ = createNotificationIQ(id, apiKey, title, message,

        // uri);

        // 通过遍历数据库的用户,发送推送消息

        List<User> allUser = userService.getUsers();

        for (User user : allUser) {

            Random random = new Random();

            String id = Integer.toHexString(random.nextInt());

            IQ notificationIQ = createNotificationIQ(id, apiKey, title,

                    message, uri);

            ClientSession session = sessionManager.getSession(user

                    .getUsername());

            if (session != null && session.getPresence().isAvailable()) {

                notificationIQ.setTo(session.getAddress());

                session.deliver(notificationIQ);

            } /* else { */

            saveNotification(id, apiKey, user.getUsername(), title, message,

                    uri);

            // }

        }

 

        // 仅仅遍历在线用户

        // for (ClientSession session : sessionManager.getSessions()) {

        // if (session.getPresence().isAvailable()) {

        // notificationIQ.setTo(session.getAddress());

        // session.deliver(notificationIQ);

        // }

        // }

    }

 

    /**

     * Sends a newly created notification message to the specific user.

     *

     * @param apiKey

     *            the API key

     * @param title

     *            the title

     * @param message

     *            the message details

     * @param uri

     *            the uri

     */

    public void sendNotifcationToUser(String apiKey, String username,

            String title, String message, String uri) {

        log.debug("sendNotifcationToUser()...");

        Random random = new Random();

        String id = Integer.toHexString(random.nextInt());

        IQ notificationIQ = createNotificationIQ(id, apiKey, title, message,

                uri);

        ClientSession session = sessionManager.getSession(username);

        if (session != null) {

            if (session.getPresence().isAvailable()) {

                notificationIQ.setTo(session.getAddress());

                session.deliver(notificationIQ);

            }

            // // 如果用户在线但不可用时,则保存推送消息到数据库中

            // else {

            // saveNotification(id, apiKey, username, title, message, uri);

            // }

        }

        // // 如果用户不在线但不可用时,则保存推送消息到数据库中

        // else {

        User user;

        try {

            // 通过用户名发送推送消息,在保存消息时,验证该用户名是否存储,防止存储无用数据

            user = userService.getUserByUsername(username);

            if (user != null) {

                saveNotification(id, apiKey, username, title, message, uri);

            }

        } catch (UserNotFoundException e) {

            // TODO Auto-generated catch block

            e.printStackTrace();

        }

        // }

    }

 

    /**

     * 存储推送消息

     *

     * @param apiKey

     * @param username

     * @param title

     * @param message

     * @param uri

     */

    private void saveNotification(String uuid, String apiKey, String username,

            String title, String message, String uri) {

        Notification notification = new Notification();

        notification.setUuid(uuid);

        notification.setApiKey(apiKey);

        notification.setUri(uri);

        notification.setUsername(username);

        notification.setTitle(title);

        notification.setMessage(message);

        // ServiceLocator.getNotificationService().saveNotification(notification);

        notificationService.saveNotification(notification);

    }

 

    /**

     * Creates a new notification IQ and returns it.

     */

    private IQ createNotificationIQ(String id, String apiKey, String title,

            String message, String uri) {

        // String id = String.valueOf(System.currentTimeMillis());

        /**

         * uuid生成策略

         */

        // Random random = new Random();

        // String id = Integer.toHexString(random.nextInt());

        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;

    }

 

}

    b. 客户端向服务器发送消息回执

      1. 新建一个消息回执IQ -> DeliverConfirmIQ.java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

package org.androidpn.client;

 

import org.jivesoftware.smack.packet.IQ;

 

public class DeliverConfirmIQ extends IQ {

    private String uuid;

 

    @Override

    public String getChildElementXML() {

        // TODO Auto-generated method stub

        StringBuilder buf = new StringBuilder();

        buf.append("<").append("deliverconfirm").append(" xmlns=\"")

                .append("androidpn:iq:deliverconfirm").append("\">");

        if (uuid != null) {

            buf.append("<uuid>").append(uuid).append("</uuid>");

        }

        buf.append("</").append("deliverconfirm").append("> ");

        return buf.toString();

    }

 

    public String getUuid() {

        return uuid;

    }

 

    public void setUuid(String uuid) {

        this.uuid = uuid;

    }

 

}

      2. 在客户端收到推送消息对外发送广播后,向服务器发送回执消息,修改NotificationPacketListener的processPacket()方法

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

@Override

public void processPacket(Packet packet) {

    Log.d(LOGTAG, "NotificationPacketListener.processPacket()...");

    Log.d(LOGTAG, "packet.toXML()=" + packet.toXML());

 

    if (packet instanceof NotificationIQ) {

            // ...

            xmppManager.getContext().sendBroadcast(intent);

             

            // 向服务器发送消息回执

            DeliverConfirmIQ deliverConfirmIQ = new DeliverConfirmIQ();

            deliverConfirmIQ.setUuid(notificationId);

            deliverConfirmIQ.setType(IQ.Type.SET);

            xmppManager.getConnection().sendPacket(deliverConfirmIQ);

        }

    }

 

}

    c. 服务器处理来自客户端的消息回执

      1. 创建一个IQDeliverConfirmHandler处理从客户端发送过来的IQ

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

package org.androidpn.server.xmpp.handler;

 

import org.androidpn.server.service.NotificationService;

import org.androidpn.server.service.ServiceLocator;

import org.androidpn.server.xmpp.UnauthorizedException;

import org.androidpn.server.xmpp.session.ClientSession;

import org.androidpn.server.xmpp.session.Session;

import org.xmpp.packet.IQ;

import org.xmpp.packet.PacketError;

import org.dom4j.Element;

 

public class IQDeliverConfirmHandler extends IQHandler {

    private static final String NAMESPACE = "androidpn:iq:deliverconfirm";

 

    private NotificationService notificationService;

 

    public IQDeliverConfirmHandler() {

        // TODO Auto-generated constructor stub

        notificationService = ServiceLocator.getNotificationService();

    }

 

    @Override

    public IQ handleIQ(IQ packet) throws UnauthorizedException {

        // TODO Auto-generated method stub

        ClientSession session = sessionManager.getSession(packet.getFrom());

        IQ reply;

        if (session == null) {

            log.error("Session not found for key " + packet.getFrom());

            reply = IQ.createResultIQ(packet);

            reply.setChildElement(packet.getChildElement().createCopy());

            reply.setError(PacketError.Condition.internal_server_error);

            return reply;

        }

        if (session.getStatus() == Session.STATUS_AUTHENTICATED) {

            if (IQ.Type.set.equals(packet.getType())) {

                Element element = packet.getChildElement();

                String uuid = element.elementText("uuid");

                notificationService.deleteNotificationByUUID(uuid);

            }

        }

 

        return null;

    }

 

    @Override

    public String getNamespace() {

        // TODO Auto-generated method stub

        return NAMESPACE;

    }

 

}

      2. 修改IQRouter的构造方法,将IQDeliverConfirmHandler添加到IQHandler集合中

1

2

3

4

5

6

7

8

9

public IQRouter() {

        sessionManager = SessionManager.getInstance();

        iqHandlers.add(new IQAuthHandler());

        iqHandlers.add(new IQRegisterHandler());

        iqHandlers.add(new IQRosterHandler());

         

        // 将处理消息回执添加到IQHandler集合中

        iqHandlers.add(new IQDeliverConfirmHandler());

   }

      3. 修改NotificationManager中的sendNotificationToUser()方法 - 在PresenceUpdateHandler调用该方法时传入false,同时在其他类调该方法时传入true,

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

// 添加一个shouldSave标志位,防止在PresenceUpdateHandler中调用时删除Notification数据时重复保存该消息,导致消息没有得到删除

public void sendNotifcationToUser(String apiKey, String username,

            String title, String message, String uri, boolean shouldSave) {

    log.debug("sendNotifcationToUser()...");

    Random random = new Random();

    String id = Integer.toHexString(random.nextInt());

    IQ notificationIQ = createNotificationIQ(id, apiKey, title, message,

                uri);

    ClientSession session = sessionManager.getSession(username);

    if (session != null) {

        if (session.getPresence().isAvailable()) {

            notificationIQ.setTo(session.getAddress());

            session.deliver(notificationIQ);

        }

        // // 如果用户在线但不可用时,则保存推送消息到数据库中

        // else {

        // saveNotification(id, apiKey, username, title, message, uri);

        // }

    }

    // // 如果用户不在线但不可用时,则保存推送消息到数据库中

    // else {

    User user;

    try {

        // 通过用户名发送推送消息,在保存消息时,验证该用户名是否存储,防止存储无用数据

        user = userService.getUserByUsername(username);

        if (user != null && shouldSave) {

            saveNotification(id, apiKey, username, title, message, uri);

        }

    } catch (UserNotFoundException e) {

        // TODO Auto-generated catch block

        e.printStackTrace();

    }

    // }

}

  4. 在版本二的基础上修复离线发送逻辑

    1. 去掉NotificationManager中的sendNotificationToUser()方法中的shouldSave参数

    2. 在NotificationManager中添加一个发送离线消息给重新上线的用户

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

/**

* 从数据库中取出已有的离线消息推送给重新上线的用户

*

* @param uuid

* @param apiKey

* @param username

* @param title

* @param message

* @param uri

*/

public void sendOfflineNotifcationToUser(String uuid, String apiKey, String username, String title, String message,

            String uri) {

    log.debug("sendNotifcationToUser()...");

    IQ notificationIQ = createNotificationIQ(uuid, apiKey, title, message, uri);

    ClientSession session = sessionManager.getSession(username);

    if (session != null) {

        if (session.getPresence().isAvailable()) {

            notificationIQ.setTo(session.getAddress());

            session.deliver(notificationIQ);

        }

    }

}

    3. 在PresenceUpdateHandler中修改process(Packet)方法的相关代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

if (session != null) {

    session.setPresence(presence);

    if (!session.isInitialized()) {

        // initSession(session);

        session.setInitialized(true);

    }

 

    // 遍历消息数据库

    List<Notification> list = notificationService.findNotificationsByUsername(session.getUsername());

    if (list != null && list.size() > 0) {

        for (Notification notification : list) {

            String apiKey = notification.getApiKey();

            String title = notification.getTitle();

            String message = notification.getMessage();

            String uri = notification.getUri();

            String uuid = notification.getUuid();

            notificationManager.sendOfflineNotifcationToUser(uuid, apiKey, session.getUsername(), title,

                    message, uri);

        }

    }

 

}

-----------------------------------------华丽丽的分割线-----------------------------------------

 至此,基于Androidpn推送平台的离线功能就已经全部实现了

转载于:https://my.oschina.net/milu6852/blog/806364

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值