服务器主动向android手机端推送消息

前两篇简单介绍了下Web Service。下面就将此项技术与开源项目androidpn结合起来,实现服务器向android手机端推送消息

首先在eclipse中打开Androidpn服务器端,然后我们就准备将服务器端推送消息的方法暴露出来,在写代码前我们要将发布Web Service要用到的jar包拷贝到Androidpn工程中的WebRoot-->WEB-INF->lib目录下,要拷贝的几个jar包如下所示:


这些jar包都可以在D:\apache-cxf-2.4.0\lib目录下找到(这里是我的目录,也就是可以在解压得到的apache-cxf-2.4.0的lib目录下找到这些jar包)。

首先我们要将服务器端向android手机端发送消息的三个方法找到,通过之前我的跟踪调试发现androidpn是通过org.androidpn.server.xmpp.push.NotificationManager类里的sendBroadcast、sendAllBroadcast和sendNotifications三个方法来分别向所有在线用户、所有用户、指定用户发送信息的。找到方法后,我们现在要做的就是将其暴露出来。前面说过发布Web Service需要2个部分:接口和实现类。下面我们就在工程的Java Resources-->src目录下新建一个接口及其实现类,代码如下:

查看文本 打印 ?
  1. package org.YL.cxf.ws;  
  2.   
  3. import javax.jws.WebService;  
  4.   
  5.   
  6. @WebService  
  7. public interface AndroidPushNotification {  
  8.     public void sendBroadcast(String apiKey, String title, String message);  
  9.     public void sendAllBroadcast(String apiKey, String title, String message);  
  10.     void sendNotifications(String apiKey, String username,  
  11.             String title, String message);  
  12.   
  13. }  

注意上面的接口定义前有@WebService关键字。然后AndroidPushNotification接口的 实现 代码:

查看文本 打印 ?
  1. package org.YL.cxf.ws.impl;  
  2.   
  3. import javax.jws.WebService;  
  4.   
  5. import org.YL.cxf.ws.AndroidPushNotification;  
  6. import org.androidpn.server.console.controller.NotificationController;  
  7. import org.androidpn.server.xmpp.push.NotificationManager;  
  8. /*endpointInterface="org.YL.cxf.ws.AndroidPushNotification"为暴露的接口, 
  9.   serviceName="AndroidPushNotificationWs" 为服务名随便取*/  
  10. @WebService(endpointInterface="org.YL.cxf.ws.AndroidPushNotification",serviceName="AndroidPushNotificationWs")  
  11. public class AndroidPushNotificationWs implements AndroidPushNotification {  
  12.   
  13.     public void sendBroadcast(String apiKey, String title, String message) {  
  14.         // TODO Auto-generated method stub  
  15.         String uri = NotificationController.myuri;  
  16.         NotificationManager nm = new NotificationManager();  
  17.         nm.sendBroadcast(apiKey, title, message, uri);  
  18.   
  19.     }  
  20.   
  21.     public void sendAllBroadcast(String apiKey, String title, String message) {  
  22.         // TODO Auto-generated method stub  
  23.         String uri = NotificationController.myuri;  
  24.         NotificationManager nm = new NotificationManager();  
  25.         nm.sendAllBroadcast(apiKey, title, message, uri);  
  26.   
  27.     }  
  28.   
  29.     public void sendNotifications(String apiKey, String username, String title,  
  30.             String message) {  
  31.         // TODO Auto-generated method stub  
  32.         String uri = NotificationController.myuri;  
  33.         NotificationManager nm = new NotificationManager();  
  34.         nm.sendNotifications(apiKey, username, title, message, uri);  
  35.   
  36.     }  
  37.   
  38. }  

在发送信息的过程中需要uri,而uri在org.androidpn.server.console.controller.NotificationController中设置。为了获得此uri我在此类中定义了一个public static的String 成员 myuri,然后将此类中将uri赋值给myuri。下面是我修改后org.androidpn.server.console.controller.NotificationController类的代码:

查看文本 打印 ?
  1. /* 
  2.  * Copyright (C) 2010 Moduad Co., Ltd. 
  3.  *  
  4.  * This program is free software; you can redistribute it and/or modify 
  5.  * it under the terms of the GNU General Public License as published by 
  6.  * the Free Software Foundation; either version 2 of the License, or 
  7.  * (at your option) any later version. 
  8.  *  
  9.  * This program is distributed in the hope that it will be useful, 
  10.  * but WITHOUT ANY WARRANTY; without even the implied warranty of 
  11.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
  12.  * GNU General Public License for more details. 
  13.  *  
  14.  * You should have received a copy of the GNU General Public License along 
  15.  * with this program; if not, write to the Free Software Foundation, Inc., 
  16.  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 
  17.  */  
  18. package org.androidpn.server.console.controller;  
  19.   
  20. import javax.servlet.http.HttpServletRequest;  
  21. import javax.servlet.http.HttpServletResponse;  
  22.   
  23. import org.androidpn.server.util.Config;  
  24. import org.androidpn.server.xmpp.push.NotificationManager;  
  25. import org.springframework.web.bind.ServletRequestUtils;  
  26. import org.springframework.web.servlet.ModelAndView;  
  27. import org.springframework.web.servlet.mvc.multiaction.MultiActionController;  
  28.   
  29. /**  
  30.  * A controller class to process the notification related requests.   
  31.  * 
  32.  * @author Sehwan Noh ([email protected]) 
  33.  */  
  34. public class NotificationController extends MultiActionController {  
  35.     public static String myuri;  
  36.   
  37.     private NotificationManager notificationManager;  
  38.   
  39.     public NotificationController() {  
  40.         notificationManager = new NotificationManager();  
  41.     }  
  42.   
  43.     public ModelAndView list(HttpServletRequest request,  
  44.             HttpServletResponse response) throws Exception {  
  45.         ModelAndView mav = new ModelAndView();  
  46.         // mav.addObject("list", null);  
  47.         mav.setViewName("notification/form");  
  48.         return mav;  
  49.     }  
  50.   
  51.     public ModelAndView send(HttpServletRequest request,  
  52.             HttpServletResponse response) throws Exception {  
  53.         String broadcast = ServletRequestUtils.getStringParameter(request,  
  54.                 "broadcast""Y");  
  55.         String username = ServletRequestUtils.getStringParameter(request,  
  56.                 "username");  
  57.         String title = ServletRequestUtils.getStringParameter(request, "title");  
  58.         String message = ServletRequestUtils.getStringParameter(request,  
  59.                 "message");  
  60.         String uri = ServletRequestUtils.getStringParameter(request, "uri");  
  61.         myuri = uri;  
  62.   
  63.         String apiKey = Config.getString("apiKey""");  
  64.         logger.debug("apiKey=" + apiKey);  
  65.         //在线用户  
  66.         if (broadcast.equalsIgnoreCase("Y")) {  
  67.             notificationManager.sendBroadcast(apiKey, title, message, uri);  
  68.         //所有用户  
  69.         }else if (broadcast.equalsIgnoreCase("A")) {  
  70.             notificationManager.sendAllBroadcast(apiKey, title, message, uri);  
  71.         //指定用户  
  72.         }else {  
  73.             notificationManager.sendNotifications(apiKey, username, title,  
  74.                     message, uri);  
  75.         }  
  76.   
  77.         ModelAndView mav = new ModelAndView();  
  78.         mav.setViewName("redirect:notification.do");  
  79.         return mav;  
  80.     }  
  81.   
  82. }  

所做的处理就是在类的最前面定义一个public static String myuri,然后在public ModelAndView send(HttpServletRequest request,
            HttpServletResponse response) throws Exception中将uri赋值给myuri。

接口代码写完后,我们还需要更改下WebRoot-->WEB-INF下的web.xml配置文件,更改后的配置文件如下:

查看文本 打印 ?
  1. xml version="1.0" encoding="UTF-8"?>  
  2. <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.     xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">  
  5.       
  6.       
  7.   
  8.     <listener>  
  9.         <listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>  
  10.     listener>  
  11.       
  12.     <context-param>  
  13.           
  14.         <param-name>contextConfigLocationparam-name>  
  15.           
  16.         <param-value>/WEB-INF/applicationContext.xmlparam-value>  
  17.     context-param>   
  18.       
  19.       
  20.   
  21.     <display-name>androidpn-serverdisplay-name>  
  22.   
  23.     <filter>  
  24.         <filter-name>encodingFilterfilter-name>  
  25.         <filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>  
  26.         <init-param>  
  27.             <param-name>encodingparam-name>  
  28.             <param-value>UTF-8param-value>  
  29.         init-param>  
  30.         <init-param>  
  31.             <param-name>forceEncodingparam-name>  
  32.             <param-value>trueparam-value>  
  33.         init-param>  
  34.     filter>  
  35.     <filter>  
  36.         <filter-name>sitemeshfilter-name>  
  37.         <filter-class>com.opensymphony.module.sitemesh.filter.PageFilterfilter-class>  
  38.     filter>  
  39.   
  40.     <filter-mapping>  
  41.         <filter-name>encodingFilterfilter-name>  
  42.         <url-pattern>/*url-pattern>  
  43.     filter-mapping>  
  44.     <filter-mapping>  
  45.         <filter-name>sitemeshfilter-name>  
  46.         <url-pattern>/*url-pattern>  
  47.         <dispatcher>REQUESTdispatcher>  
  48.         <dispatcher>FORWARDdispatcher>  
  49.     filter-mapping>  
  50.       
  51.       
  52.     <servlet>  
  53.         <servlet-name>cxfservlet-name>  
  54.         <servlet-class>org.apache.cxf.transport.servlet.CXFServletservlet-class>  
  55.     servlet>  
  56.     <servlet-mapping>  
  57.         <servlet-name>cxfservlet-name>  
  58.         <url-pattern>/androidpnservice/*url-pattern>  
  59.     servlet-mapping>  
  60.       
  61.   
  62.     <servlet>  
  63.         <servlet-name>dispatcherservlet-name>  
  64.         <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>  
  65.         <load-on-startup>1load-on-startup>  
  66.     servlet>  
  67.   
  68.     <servlet-mapping>  
  69.         <servlet-name>dispatcherservlet-name>  
  70.         <url-pattern>*.dourl-pattern>  
  71.     servlet-mapping>  
  72.   
  73.     <session-config>  
  74.         <session-timeout>30session-timeout>  
  75.     session-config>  
  76.   
  77.     <welcome-file-list>  
  78.         <welcome-file>/index.htmlwelcome-file>  
  79.         <welcome-file>/index.jspwelcome-file>  
  80.         <welcome-file>/index.dowelcome-file>  
  81.     welcome-file-list>  
  82.   
  83.     <error-page>  
  84.         <error-code>400error-code>  
  85.         <location>/index.jsplocation>  
  86.     error-page>  
  87.     <error-page>  
  88.         <error-code>403error-code>  
  89.         <location>/403.jsplocation>  
  90.     error-page>  
  91.     <error-page>  
  92.         <error-code>404error-code>  
  93.         <location>/404.jsplocation>  
  94.     error-page>  
  95.     <error-page>  
  96.         <error-code>500error-code>  
  97.         <location>/error.jsplocation>  
  98.     error-page>  
  99.   
  100. web-app>  

其中

查看文本 打印 ?
  1.   
  2.     <servlet>  
  3.         <servlet-name>cxfservlet-name>  
  4.         <servlet-class>org.apache.cxf.transport.servlet.CXFServletservlet-class>  
  5.     servlet>  
  6.     <servlet-mapping>  
  7.         <servlet-name>cxfservlet-name>  
  8.         <url-pattern>/androidpnservice/*url-pattern>  
  9.     servlet-mapping>  

查看文本 打印 ?
  1. <listener>  
  2.         <listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>  
  3.     listener>  
  4.       
  5.     <context-param>  
  6.           
  7.         <param-name>contextConfigLocationparam-name>  
  8.           
  9.         <param-value>/WEB-INF/applicationContext.xmlparam-value>  
  10.     context-param>   

是新添加的。

上面还指定了一个/WEB-INF/applicationContext.xml的配置文件,这个文件是我新添加进去的,是为了发布Web Service用的,此文件内容为:

查看文本 打印 ?
  1. xml version="1.0" encoding="UTF-8"?>  
  2.   
  3. <beans xmlns="http://www.springframework.org/schema/beans"  
  4.     xmlns:p="http://www.springframework.org/schema/p"  
  5.     xmlns:jaxws="http://cxf.apache.org/jaxws"  
  6.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  7.     xsi:schemaLocation="http://www.springframework.org/schema/beans   
  8.     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  9.     http://cxf.apache.org/jaxws  
  10.     http://cxf.apache.org/schemas/jaxws.xsd">  
  11.       
  12.     <import resource="classpath:META-INF/cxf/cxf.xml"/>  
  13.     <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml"/>  
  14.     <import resource="classpath:META-INF/cxf/cxf-servlet.xml"/>  
  15.       
  16.     <jaxws:endpoint  
  17.         implementor = "org.YL.cxf.ws.impl.AndroidPushNotificationWs"  
  18.         address = "/androidpush">  
  19.     jaxws:endpoint>  
  20.       
  21. beans>  


其中


查看文本 打印 ?
  1. <jaxws:endpoint  
  2.         implementor = "org.YL.cxf.ws.impl.AndroidPushNotificationWs"  
  3.         address = "/androidpush">  
  4.     jaxws:endpoint>  

指定发布的Web Service的 实现 类和在获得WSDL描述文档时在“?”前面的服务名。

好了现在一切就绪,我们运行Androidpn服务器端程序,成功运行起来后,我们在浏览器地址栏中输入:http://192.168.1.117:8080/androidpnservice/androidpush?wsdl(192.168.1.117:8080是我本机的IP地址和tomcat的端口) 可得到WSDL描述文档如下(部分截图):

这时表明我们的服务端推送消息的方法发布成功,我们在来看客户端。

首先我们在eclipse中建立一个名为PushClient的空的Java Project,初始时什么也没有:


然后我们用cxf的wsdl2java来生成客户端代码:


在回到PushClient工程中刷新下(F5),可得:


为了方便调用,我又写了一个org.yl.SendNotification类,代码如下:

查看文本 打印 ?
  1. package org.yl;  
  2.   
  3. import org.yl.cxf.ws.AndroidPushNotification;  
  4. import org.yl.cxf.ws.impl.AndroidPushNotificationWs;  
  5.   
  6. public class SendNotification {  
  7.       
  8.     AndroidPushNotificationWs factory = new AndroidPushNotificationWs();  
  9.     AndroidPushNotification apn = factory.getAndroidPushNotificationWsPort();  
  10.       
  11.     public void sendBroadcast(String apiKey, String title, String message)  
  12.     {  
  13.         apn.sendAllBroadcast(apiKey, title, message);  
  14.     }  
  15.     public void sendAllBroadcast(String apiKey, String title, String message)  
  16.     {  
  17.         apn.sendBroadcast(apiKey, title, message);  
  18.     }  
  19.     public void sendNotifications(String apiKey, String username,  
  20.             String title, String message)  
  21.     {  
  22.         apn.sendNotifications(apiKey,username,title,message);  
  23.     }  
  24. }  

以后调用发送 消息 的方法时,只需要定义一个SendNotification对象,通过此对象就可以方便的调用方法了,避免每次调用时都

查看文本 打印 ?
  1. AndroidPushNotificationWs factory = new AndroidPushNotificationWs();  
  2.     AndroidPushNotification apn = factory.getAndroidPushNotificationWsPort();  

然后写一个主函数类来测试我们的整个系统,代码如下:

查看文本 打印 ?
  1. package org.main;  
  2.   
  3. import org.yl.SendNotification;  
  4.   
  5. public class CallMain {  
  6.   
  7.     /** 
  8.      * @param args 
  9.      */  
  10.     public static void main(String[] args) {  
  11.         // TODO Auto-generated method stub  
  12.         SendNotification sn = new SendNotification();  
  13.           
  14.         //给所有人发信息,这里第一个参数"1234567890"是androidpn中的apikey,在androidpn中是写死了的,第二个参数为消息标题(title),第三个参数为消息内容(message)  
  15.         sn.sendAllBroadcast("1234567890""hello""How are you?");  
  16.         //给在线人发信息,参数同上  
  17.         //sn.sendBroadcast("1234567890", "Hi", "OK");  
  18.         //给指定人发信息,  第一、三、四个消息的意义同上,第二个参数"4ed224a3fa8c4560a27e313ab24f597d"是用户名,可以在服务器端页面看到。  
  19.     //  sn.sendNotifications("1234567890","4ed224a3fa8c4560a27e313ab24f597d", "Hello Hi", "Hello World! How are you?");  
  20.   
  21.     }  
  22.   
  23. }  

在整个工程中,如下所示,红圈中是我手动添加的代码,其他的都cxf工具自动生成的。



好了,现在就来测试下我们的推送消息系统。

首先将Androidpn开源项目的客户端在模拟器重运行起来,然后回到PushClient工程,在主函数中将其他方法注释掉,只留下:

sn.sendAllBroadcast()方法,如下所示:

查看文本 打印 ?
  1. package org.main;  
  2.   
  3. import org.yl.SendNotification;  
  4.   
  5. public class CallMain {  
  6.   
  7.     /** 
  8.      * @param args 
  9.      */  
  10.     public static void main(String[] args) {  
  11.         // TODO Auto-generated method stub  
  12.         SendNotification sn = new SendNotification();  
  13.           
  14.         //给所有人发信息,这里第一个参数"1234567890"是androidpn中的apikey,在androidpn中是写死了的,第二个参数为消息标题(title),第三个参数为消息内容(message)  
  15.         sn.sendAllBroadcast("1234567890""hello""How are you?");  
  16.         //给在线人发信息,参数同上  
  17.         //sn.sendBroadcast("1234567890", "Hi", "OK");  
  18.         //给指定人发信息,  第一、三、四个消息的意义同上,第二个参数"4ed224a3fa8c4560a27e313ab24f597d"是用户名,可以在服务器端页面看到。  
  19.     //  sn.sendNotifications("1234567890","4ed224a3fa8c4560a27e313ab24f597d", "Hello Hi", "Hello World! How are you?");  
  20.   
  21.     }  
  22.   
  23. }  

然后运行程序,可看到模拟器中接收到了推送主题为:hello,内容为:How  are you?的 消息


同理注释掉其他方法直留下:sn.sendBroadcast()方法:

查看文本 打印 ?
  1. package org.main;  
  2.   
  3. import org.yl.SendNotification;  
  4.   
  5. public class CallMain {  
  6.   
  7.     /** 
  8.      * @param args 
  9.      */  
  10.     public static void main(String[] args) {  
  11.         // TODO Auto-generated method stub  
  12.         SendNotification sn = new SendNotification();  
  13.           
  14.         //给所有人发信息,这里第一个参数"1234567890"是androidpn中的apikey,在androidpn中是写死了的,第二个参数为消息标题(title),第三个参数为消息内容(message)  
  15.         //sn.sendAllBroadcast("1234567890", "hello", "How are you?");  
  16.         //给在线人发信息,参数同上  
  17.         sn.sendBroadcast("1234567890""Hi""OK");  
  18.         //给指定人发信息,  第一、三、四个消息的意义同上,第二个参数"4ed224a3fa8c4560a27e313ab24f597d"是用户名,可以在服务器端页面看到。  
  19.     //  sn.sendNotifications("1234567890","4ed224a3fa8c4560a27e313ab24f597d", "Hello Hi", "Hello World! How are you?");  
  20.   
  21.     }  
  22.   
  23. }  

然后运行程序,可看到模拟器中接收到了推送主题为:Hi,内容为:OK的 消息



测试最后一个给指定用户发送消息的方法:

查看文本 打印 ?
  1. package org.main;  
  2.   
  3. import org.yl.SendNotification;  
  4.   
  5. public class CallMain {  
  6.   
  7.     /** 
  8.      * @param args 
  9.      */  
  10.     public static void main(String[] args) {  
  11.         // TODO Auto-generated method stub  
  12.         SendNotification sn = new SendNotification();  
  13.           
  14.         //给所有人发信息,这里第一个参数"1234567890"是androidpn中的apikey,在androidpn中是写死了的,第二个参数为消息标题(title),第三个参数为消息内容(message)  
  15.         //sn.sendAllBroadcast("1234567890", "hello", "How are you?");  
  16.         //给在线人发信息,参数同上  
  17.         //sn.sendBroadcast("1234567890", "Hi", "OK");  
  18.         //给指定人发信息,  第一、三、四个消息的意义同上,第二个参数"f6ac2608faa94a65b5c2d94b72e968b0"是用户名,可以在服务器端页面看到。  
  19.         sn.sendNotifications("1234567890","f6ac2608faa94a65b5c2d94b72e968b0""Hello Hi""Hello World! How are you?");  
  20.   
  21.     }  
  22.   
  23. }  

其中第二个参数是从 服务器 端页面得来的:



运行程序得:



到此为止我们完成了根据某一事件触发,服务器可以主动向android手机端推送消息的这一需求。


另外为了使用上的方便,可以将上面PushClient工程中除了包含main函数的类之外的代码都统统打包成androidpn.jar文件。这样当其他的应用程序需要调用相应的方法发送信息时,只需要将此androidpn.jar文件加入到相应的工程中就可以调用其中的方法了。操作如下:

首先新建一个工程PushClient2,将PushClient工程中除了包含main函数的类外的其他文件文件拷贝到PushClient2工程中,


然后file-->Export-->Java-->JAR file-->选定输出路径和文件名-->finish即可。这里我取得文件名为androidp:


然后我们新建一个空的Java Project:PushClient3,并将androidp.jar引入此工程,主函数与PushClient中的主函数相同,


然后运行此工程可得到与工程PushClient相同的效果。


总结服务器端向android手机端推送消息实现逻辑是:当我们需要向用户推送消息时,就将消息发给AndroidPn的服务器端,由AndroidPn服务器端来替我们将消息发送给用户。那么怎么将我们要推送的信息发给AndroidPn服务器端呢?我们可以采用WebService技术,将AndroidPn服务器端发送消息的方法暴露出来,然后我们就可以在本地或远程来调用AndroidPn暴露出来的方法了。所以我们的系统要想正常运行,Androidpn服务器端必须事先开启。因为Androidpn服务器端是一个Web应用程序,可以将其经过Web Service技术处理后部署到tomcat上,以后我们只需要启动tomcat,Androidpn服务器端就会自动启动起来。

做法是:在eclipse中选中经过WebService处理后的AndroidPN服务器端,然后file-->Export-->Web-->WAR file-->将其导入到tomcat的webapps目录中。

以后只需要执行apache-tomcat-7.0.32\bin\startup.bat,启动tomcat,Androidpn服务器端也就随即启动了,不需要在eclipse中启动了。


转自:http://www.verydemo.com/demo_c131_i166053.html

你可能感兴趣的:(服务器主动向android手机端推送消息)