Androidpn大汇总

---------------------------------------------------(一)androidpn-server服务端启动过程的理解分析---------------------------------------------------

在Androidpn的底层主要采用的mina和openfire两大框架,其中mina主要为底层数据传输的Socket框架。下面简单说明mina的框架。

Apache Mina Server 是一个网络通信应用框架,也就是说,它主要是对基于TCP/IP、UDP/IP协议栈的通信框架(然,也可以提供JAVA 对象的序列化服务、虚拟机管道通信服务等),Mina 可以帮助我们快速开发高性能、高扩展性的网络通信应用,Mina 提供了事件驱动、异步(Mina 的异步IO 默认使用的是JAVA NIO 作为底层支持)操作的编程模型。Mina 主要有1.x 和2.x 两个分支,这里我们讲解最新版本2.0,如果你使用的是Mina 1.x,那么可能会有一些功能并不适用。学习本文档,需要你已掌握JAVA IO、JAVA NIO、JAVASocket、JAVA 线程及并发库(java.util.concurrent.*)的知识。Mina 同时提供了网络通信的Server 端、Client 端的封装,无论是哪端,Mina 在整个网通通信结构中都处于如下的位置:

 

 

 

 

 

     可见Mina 的API 将真正的网络通信与我们的应用程序隔离开来,你只需要关心你要发送、接收的数据以及你的业务逻辑即可。同样的,无论是哪端,Mina 的执行流程如下所示:

 

 

 

 

(1.) IoService:这个接口在一个线程上负责套接字的建立,拥有自己的Selector,监听是否有连接被建立。


(2.) IoProcessor:这个接口在另一个线程上负责检查是否有数据在通道上读写,也就是说它也拥有自己的Selector,这是与我们使用JAVA NIO 编码时的一个不同之处,通常在JAVA NIO 编码中,我们都是使用一个Selector,也就是不区分IoService与IoProcessor 两个功能接口。另外,IoProcessor 负责调用注册在IoService 上的过滤器,并在过滤器链之后调用IoHandler。


(3.) IoFilter:这个接口定义一组拦截器,这些拦截器可以包括日志输出、黑名单过滤、数据的编码(write 方向)与解码(read 方向)等功能,其中数据的encode 与decode是最为重要的、也是你在使用Mina 时最主要关注的地方。


(4.) IoHandler:这个接口负责编写业务逻辑,也就是接收、发送数据的地方。

 

 

 

Mina自带的过滤器(部分)

 

 

     由上面的Mina的执行的过程可以知道针对使用Mina的第三方来说主要的业务主要在IoHandler中。

androidpn-server的spring配置。

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd"> <!-- =============================================================== --> <!-- Resources --> <!-- =============================================================== --> <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>classpath:jdbc.properties</value> </list> </property> </bean> <!-- =============================================================== --> <!-- Data Source --> <!-- =============================================================== --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbcDriverClassName}" /> <property name="url" value="${jdbcUrl}" /> <property name="username" value="${jdbcUsername}" /> <property name="password" value="${jdbcPassword}" /> <property name="maxActive" value="${jdbcMaxActive}" /> <property name="maxIdle" value="${jdbcMaxIdle}" /> <property name="maxWait" value="${jdbcMaxWait}" /> <property name="defaultAutoCommit" value="true" /> </bean> <!-- =============================================================== --> <!-- Hibernate --> <!-- =============================================================== --> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="configLocation" value="classpath:hibernate.cfg.xml" /> </bean> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <!-- =============================================================== --> <!-- Data Access Objects --> <!-- =============================================================== --> <bean id="userDao" class="org.androidpn.server.dao.hibernate.UserDaoHibernate"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <!-- =============================================================== --> <!-- Services --> <!-- =============================================================== --> <bean id="userService" class="org.androidpn.server.service.impl.UserServiceImpl"> <property name="userDao" ref="userDao" /> </bean> <!-- =============================================================== --> <!-- SSL --> <!-- =============================================================== --> <!-- <bean id="tlsContextFactory" class="org.androidpn.server.ssl2.ResourceBasedTLSContextFactory"> <constructor-arg value="classpath:bogus_mina_tls.cert" /> <property name="password" value="boguspw" /> <property name="trustManagerFactory"> <bean class="org.androidpn.server.ssl2.BogusTrustManagerFactory" /> </property> </bean> --> <!-- =============================================================== --> <!-- MINA --> <!-- =============================================================== --> <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer"> <property name="customEditors"> <map> <entry key="java.net.SocketAddress"> <bean class="org.apache.mina.integration.beans.InetSocketAddressEditor" /> </entry> </map> </property> </bean> <bean id="xmppHandler" class="org.androidpn.server.xmpp.net.XmppIoHandler" /> <bean id="filterChainBuilder" class="org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder"> <property name="filters"> <map> <entry key="executor"> <bean class="org.apache.mina.filter.executor.ExecutorFilter" /> </entry> <entry key="codec"> <bean class="org.apache.mina.filter.codec.ProtocolCodecFilter"> <constructor-arg> <bean class="org.androidpn.server.xmpp.codec.XmppCodecFactory" /> </constructor-arg> </bean> </entry> <!-- <entry key="logging"> <bean class="org.apache.mina.filter.logging.LoggingFilter" /> </entry> --> </map> </property> </bean> <bean id="ioAcceptor" class="org.apache.mina.transport.socket.nio.NioSocketAcceptor" init-method="bind" destroy-method="unbind"> <property name="defaultLocalAddress" value=":5222" /> <property name="handler" ref="xmppHandler" /> <property name="filterChainBuilder" ref="filterChainBuilder" /> <property name="reuseAddress" value="true" /> </bean> </beans>

 
 

 

 

 

通过Mina的原理我们研究androidpn的运行流程不能看出,如下:

 

 

 

 

androidpn-server执行过程如下:

1.spring初始化并启动过程,调用NioSocketAcceptor。

2。NioSocketAcceptor开始执行调用IoProcessor.

3.IoProcessor开始调用FilterChain。FilterChain调用相关的IoFilter的。其中ProtocolCodecFilter的过滤器调用了org.androidpn.server.xmpp.codec.XmppCodecFactory进行编码。

4.XmppIoHandler实现自IoHanlder并调用通过openfire 的XMLLightweightParser解析相关的业务逻辑。

5.根据解析的信息调用xmpp并处理相关信息。


---------------------------------------------------(二)androidpn-server服务端几个类说明---------------------------------------------------

AndroidPN(Android Push Notification) 是一个基于XMPP协议的Java开源推送通知实现,它包含了完整的客户端和服务端。

AndroidPN基于Openfire下的一些开源项目构建。

 AndroidPN服务器包含两个部分,

      一个是侦听在5222端口上的XMPP服务,负责与客户端的XMPPConnection类进行通信,作用是用户注册和身份认证,并发送推送通知消息。

      另外一部分是Web服务器,采用一个轻量级的HTTP服务器,负责接收用户的Web请求。

最上层包含四个组成部分,分别是SessionManager,Auth Manager,PresenceManager以及Notification Manager。

        SessionManager负责管理客户端与服务器之间的会话。

        Auth Manager负责客户端用户认证管理。

        Presence Manager负责管理客户端用户的登录状态。

        NotificationManager负责实现服务器向客户端推送消息功能。

 

IQHandler消息处理器的类:

     IQHandler:消息处理器抽象类。

     IQAuthHandler:权限协议的消息处理类,消息的类型为:jabber:iq:auth

     IQRegisterHandler:用户注册的消息处理类,消息类型为: jabber:iq:register

     IQRosterHandler:用户消息交互类,消息类型为:jabber:iq:roster

     PresenceUpdateHandler:用户状态展现变化处理类。内部调用,不具有类型。

 NotificationManager源代码:

Java代码   收藏代码
  1. public class NotificationManager {  
  2.   
  3.     private static final String NOTIFICATION_NAMESPACE = "androidpn:iq:notification";  
  4.   
  5.     private final Log log = LogFactory.getLog(getClass());  
  6.   
  7.     private SessionManager sessionManager;  
  8.   
  9.     /** 
  10.      * Constructor. 
  11.      */  
  12.     public NotificationManager() {  
  13.         sessionManager = SessionManager.getInstance();  
  14.     }  
  15.   
  16.     /** 
  17.      * Broadcasts a newly created notification message to all connected users. 
  18.      *  
  19.      * @param apiKey the API key 
  20.      * @param title the title 
  21.      * @param message the message details 
  22.      * @param uri the uri 
  23.      */  
  24.     public void sendBroadcast(String apiKey, String title, String message,  
  25.             String uri) {  
  26.         log.debug("sendBroadcast()...");  
  27.         IQ notificationIQ = createNotificationIQ(apiKey, title, message, uri);  
  28.         for (ClientSession session : sessionManager.getSessions()) {  
  29.             if (session.getPresence().isAvailable()) {  
  30.                 notificationIQ.setTo(session.getAddress());  
  31.                 session.deliver(notificationIQ);  
  32.             }  
  33.         }  
  34.     }  
  35.   
  36.     /** 
  37.      * Sends a newly created notification message to the specific user. 
  38.      *  
  39.      * @param apiKey the API key 
  40.      * @param title the title 
  41.      * @param message the message details 
  42.      * @param uri the uri 
  43.      */  
  44.     public void sendNotifcationToUser(String apiKey, String username,  
  45.             String title, String message, String uri) {  
  46.         log.debug("sendNotifcationToUser()...");  
  47.         IQ notificationIQ = createNotificationIQ(apiKey, title, message, uri);  
  48.         ClientSession session = sessionManager.getSession(username);  
  49.         if (session != null) {  
  50.             if (session.getPresence().isAvailable()) {  
  51.                 notificationIQ.setTo(session.getAddress());  
  52.                 session.deliver(notificationIQ);  
  53.             }  
  54.         }  
  55.     }  
  56.   
  57.     /** 
  58.      * Creates a new notification IQ and returns it. 
  59.      */  
  60.     private IQ createNotificationIQ(String apiKey, String title,  
  61.             String message, String uri) {  
  62.         Random random = new Random();  
  63.         String id = Integer.toHexString(random.nextInt());  
  64.         // String id = String.valueOf(System.currentTimeMillis());  
  65.   
  66.         Element notification = DocumentHelper.createElement(QName.get(  
  67.                 "notification", NOTIFICATION_NAMESPACE));  
  68.         notification.addElement("id").setText(id);  
  69.         notification.addElement("apiKey").setText(apiKey);  
  70.         notification.addElement("title").setText(title);  
  71.         notification.addElement("message").setText(message);  
  72.         notification.addElement("uri").setText(uri);  
  73.   
  74.         IQ iq = new IQ();  
  75.         iq.setType(IQ.Type.set);  
  76.         iq.setChildElement(notification);  
  77.   
  78.         return iq;  
  79.     }  
  80. }  

 

其中:

    发布订阅式的发送消息调用方法:

    /**
     * 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);

 

 

 

点对点的发送消息调用方法:

    /**
     * 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);

 

创建发送消息的方法:

    /**
     * Creates a new notification IQ and returns it.
     */
    private IQ createNotificationIQ(String apiKey, String title,
            String message, String uri);


---------------------------------------------------(三)androidpn-client客户端几个类说明---------------------------------------------------

 

在androidpn的客户端几个重要的类:

                ServiceManager:管理消息服务和加载相关的配置。

                ConnectivityReceiver:处理网络状态的广播。

                NotificationReceiver:处理服务端发送的推送消息。

                NotificationService:后台服务用户响应服务端的消息。需要在AndroidManifest.xml.注册。

                NotificationSettingsActivity:推送信息设置页面。

                PersistentConnectionListener:监控连接关闭和重连事件的监听。

                PhoneStateChangeListener:监听手机状态的事件监听类。

                ReconnectionThread:重连的线程类。

                Notifier:客户端发送通知的类。

                NotificationIQ:消息的数据包。

 

 

ServiceManager中获取属性信息的方法:

Java代码   收藏代码
  1. private Properties loadProperties() {  
  2.     //        InputStream in = null;  
  3.     //        Properties props = null;  
  4.     //        try {  
  5.     //            in = getClass().getResourceAsStream(  
  6.     //                    "/org/androidpn/client/client.properties");  
  7.     //            if (in != null) {  
  8.     //                props = new Properties();  
  9.     //                props.load(in);  
  10.     //            } else {  
  11.     //                Log.e(LOGTAG, "Could not find the properties file.");  
  12.     //            }  
  13.     //        } catch (IOException e) {  
  14.     //            Log.e(LOGTAG, "Could not find the properties file.", e);  
  15.     //        } finally {  
  16.     //            if (in != null)  
  17.     //                try {  
  18.     //                    in.close();  
  19.     //                } catch (Throwable ignore) {  
  20.     //                }  
  21.     //        }  
  22.     //        return props;  
  23.   
  24.     Properties props = new Properties();  
  25.     try {  
  26.         int id = context.getResources().getIdentifier("androidpn""raw",  
  27.                 context.getPackageName());  
  28.         props.load(context.getResources().openRawResource(id));  
  29.     } catch (Exception e) {  
  30.         Log.e(LOGTAG, "Could not find the properties file.", e);  
  31.         // e.printStackTrace();  
  32.     }  
  33.     return props;  
  34. }  

 

 

SharedPreferences的使用:

Java代码   收藏代码
  1. sharedPrefs = context.getSharedPreferences(  
  2.         Constants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE);  
  3. Editor editor = sharedPrefs.edit();  
  4. editor.putString(Constants.API_KEY, apiKey);  
  5. editor.putString(Constants.VERSION, version);  
  6. editor.putString(Constants.XMPP_HOST, xmppHost);  
  7. editor.putInt(Constants.XMPP_PORT, Integer.parseInt(xmppPort));  
  8. editor.putString(Constants.CALLBACK_ACTIVITY_PACKAGE_NAME,  
  9.         callbackActivityPackageName);  
  10. editor.putString(Constants.CALLBACK_ACTIVITY_CLASS_NAME,  
  11.         callbackActivityClassName);  
  12. editor.commit();  

 

获取手机的设备id:

Java代码   收藏代码
  1. TelephonyManager       telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);  
  2.         // wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);  
  3.         // connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);  
  4.   
  5.         // Get deviceId  
  6.         deviceId = telephonyManager.getDeviceId();  

 Notifier中发送通知的方法:

Java代码   收藏代码
  1. // Notification  
  2. Notification notification = new Notification();  
  3. notification.icon = getNotificationIcon();  
  4. notification.defaults = Notification.DEFAULT_LIGHTS;  
  5. if (isNotificationSoundEnabled()) {  
  6.     notification.defaults |= Notification.DEFAULT_SOUND;  
  7. }  
  8. if (isNotificationVibrateEnabled()) {  
  9.     notification.defaults |= Notification.DEFAULT_VIBRATE;  
  10. }  
  11. notification.flags |= Notification.FLAG_AUTO_CANCEL;  
  12. notification.when = System.currentTimeMillis();  
  13. notification.tickerText = message;  
  14.   
  15. //            Intent intent;  
  16. //            if (uri != null  
  17. //                    && uri.length() > 0  
  18. //                    && (uri.startsWith("http:") || uri.startsWith("https:")  
  19. //                            || uri.startsWith("tel:") || uri.startsWith("geo:"))) {  
  20. //                intent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri));  
  21. //            } else {  
  22. //                String callbackActivityPackageName = sharedPrefs.getString(  
  23. //                        Constants.CALLBACK_ACTIVITY_PACKAGE_NAME, "");  
  24. //                String callbackActivityClassName = sharedPrefs.getString(  
  25. //                        Constants.CALLBACK_ACTIVITY_CLASS_NAME, "");  
  26. //                intent = new Intent().setClassName(callbackActivityPackageName,  
  27. //                        callbackActivityClassName);  
  28. //                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
  29. //                intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);  
  30. //            }  
  31.   
  32. Intent intent = new Intent(context,  
  33.         NotificationDetailsActivity.class);  
  34. intent.putExtra(Constants.NOTIFICATION_ID, notificationId);  
  35. intent.putExtra(Constants.NOTIFICATION_API_KEY, apiKey);  
  36. intent.putExtra(Constants.NOTIFICATION_TITLE, title);  
  37. intent.putExtra(Constants.NOTIFICATION_MESSAGE, message);  
  38. intent.putExtra(Constants.NOTIFICATION_URI, uri);  
  39. intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
  40. intent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);  
  41. intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);  
  42. intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);  
  43. intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);  
  44.   
  45. PendingIntent contentIntent = PendingIntent.getActivity(context, 0,  
  46.         intent, PendingIntent.FLAG_UPDATE_CURRENT);  
  47.   
  48. notification.setLatestEventInfo(context, title, message,  
  49.         contentIntent);  
  50. notificationManager.notify(random.nextInt(), notification);  

 

androidpn用户名和密码来源:

XmppManager的注册任务(RegisterTask)中run方法:

            if (!xmppManager.isRegistered()) {
                final String newUsername = newRandomUUID();
                final String newPassword = newRandomUUID();

                Registration registration = new Registration();

                PacketFilter packetFilter = new AndFilter(new PacketIDFilter(
                        registration.getPacketID()), new PacketTypeFilter(
                        IQ.class));

 


---------------------------------------------------(四)androidpn-client 常见BUG解决方法---------------------------------------------------

Androidpn用的比较广泛,但Androidpn还不成熟,存在一些BUG。
目前比较困扰大家的BUG主要有:
1.当服务端连续发送多条通知时,客户端都是显示同一条通知内容。
2.服务端重启,客户端也需要重启建立连接。

最后还有一个问题当我们服务器端重启的时候,客户端就无法在连接到服务器了,除非把android后台的服务

关掉,然后重启才行.在XmmpManager中加上如下红色代码就可:

    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");
    }



      由于没有经验,有BUG也只能继续GOOGLE,最终发现 http://jclick.iteye.com/blog/1289383  这个TOMCAT版本比较符合项目的需求。在此还是要感谢这位大神,真是造福广大人民群众。在这个版本中主要修复了第二个问题,相信很多朋友都已经看过了这篇BLOG ,但是大神在回复中写错了类名XMPPWriter,所有导致大家并不知道问题是怎么解决的, 其实是修改了org.androidpn.client.XmppManager,org.jivesoftware.smack.PacketWriter(smack源码)这2个类,这个将他2个版本的代码比较一下就可以看出来了,废话了那么多,在此说一下大神解决此问题的方法。代码在
http://phonepush.sinaapp.com/forum.php?mod=viewthread&tid=5&extra=page%3D1 篇帖子中已经贴出来了。在org.androidpn.client.XmppManager的LoginTask方法中加了一行代码getConnection().startKeepAliveThread(xmppManager);跟踪进去发现是开启了一个线程发送心跳,当发送失败时捕获异常,客户端每隔一段时间进行重连。
org.jivesoftware.smack.PacketWriter的run方法
Java代码 
  1. catch (SocketException e) {
  2. Log.e("PacketReader", e.toString());
  3. connection.disconnect();
  4. xmppManager.startReconnectionThread();
  5. catch (IOException e) {
  6. e.printStackTrace();
  7. }
这样就达到了当与服务端失去连接时,客户端能够进行重新连接的效果。后来群里有朋友说在LoginTask方法中加入
getConnection().startKeepAliveThread(xmppManager); 编译就报错,那是因为他用的是第一个版本 ,所有请先下载第二个版本,第二个版本带大神精简过smack源码。 其实心跳机制在官方的asmack中就已经存在,并且在建立XmppConnection的时候就已经启动,但是遗憾的是asmack的开发人员并没有进行异常之后的重连 

Java代码 
  1. catch (Exception e) {
  2. // Do nothing
  3. }
所有才出现这个困扰大家的问题。

       然后是第二个问题,我们刚才下载的这个版本并没有处理这个BUG,其实很简单,群里的高手经解决,就是将org.androidpn.client.Notifier中的notify方法的

  1. PendingIntent contentIntent = PendingIntent.getActivity(context, 0,intent, PendingIntent.FLAG_UPDATE_CURRENT);
复制代码
改成
  1. PendingIntent contentIntent = PendingIntent.getActivity(context, random.nextInt(),
  2.                     intent, PendingIntent.FLAG_UPDATE_CURRENT);
复制代码

       好了,这2个问题基本上就解决了,本人也只是在此将前辈们的经验写一下,方便大家集中修正BUG。其实在碰到框架BUG时,看看框架的源码还是有帮助,可能会有很多不解,但我觉得多多少少还是能看出一点东西来。以后大家碰到什么问题也可以提出来,大家一起研究讨论,集思广益,总比一个人瞎想的强,有什么好的想法也可以拿出来分享。再次谢谢各位前辈。


 在网上androidpn上的BUG基本都解决了,也多亏牛人们顶力相助,灰常感谢啊。在这里要说的问题是手机锁屏后,客户端心跳包不再发送了。由于android也接触不是很久,对一些系统的机制不太了解,经过多次测试与分析,才发现了是由于锁屏后CPU处于睡眠状态,线程都被挂起,所以在服务器端设定的闲置时间内收不到心跳包,强制移除用户下线。

      OK问题已经找到了就好办多了,既然是被挂起了我们就只有让心跳一直在跑了,不啰嗦了。既而在网上有找到两种方法,第一种是让系统不睡眠,第二种则是使用AlarmManager来做我们的操作,在这里我是用的第二种方案来解决我们的问题的。但这样可能有点费电,暂时只能这样解决了了,不知道大家有木有更好点的解决办法能说出来大家一起研究研究。

 

1).

 

 

[java]  view plain copy print ?
  1. //申请设备电源锁   
  2.     public void acquireWakeLock()  
  3.     {  
  4.         if (null == mWakeLock)  
  5.         {  
  6.             PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);  
  7.             mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "NotificationService");  
  8.             if (null != mWakeLock)  
  9.             {  
  10.                 mWakeLock.acquire();  
  11.             }  
  12.         }  
  13.     }  
  14.       
  15.     //释放设备电源锁   
  16.     public void releaseWakeLock()  
  17.     {  
  18.         if (null != mWakeLock)  
  19.         {  
  20.             mWakeLock.release();  
  21.             mWakeLock = null;  
  22.         }  
  23.     }  
Java代码   收藏代码
  1. //申请设备电源锁  
  2.     public void acquireWakeLock()  
  3.     {  
  4.         if (null == mWakeLock)  
  5.         {  
  6.             PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);  
  7.             mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "NotificationService");  
  8.             if (null != mWakeLock)  
  9.             {  
  10.                 mWakeLock.acquire();  
  11.             }  
  12.         }  
  13.     }  
  14.       
  15.     //释放设备电源锁  
  16.     public void releaseWakeLock()  
  17.     {  
  18.         if (null != mWakeLock)  
  19.         {  
  20.             mWakeLock.release();  
  21.             mWakeLock = null;  
  22.         }  
  23.     }  


2).

 

 

 

Java代码   收藏代码
  1. public void registerAlarmManager(){  
  2.     am = (AlarmManager)getSystemService(ALARM_SERVICE);   
  3.     pi = PendingIntent.getBroadcast(this0new Intent(this, HeartActionBroadCast.class), Intent.FLAG_ACTIVITY_NEW_TASK);    
  4.     long now = System.currentTimeMillis();    
  5.     am.setInexactRepeating(AlarmManager.RTC_WAKEUP, now, 20000, pi);  
  6. }  
 

---------------------------------------------------(五)Androidpn-server的Mina编码和解码解析过程---------------------------------------------------




在许多网络应用中可能针对传输的数据进行加密操作,接收到数据之后进行解码操作。

在mina中提供许多加密和解密的解析方式:

1.带一定前缀的字符串的解析方式。

2.序列化对象的字符串解析方式。

3.分隔符方式的字符串解析方式。

 

 

在mina中提供相关的filterchain支持相关的操作。

Mina的源代码如下:

Java代码   收藏代码
  1. package org.apache.mina.filter.codec;  
  2.   
  3. import org.apache.mina.core.session.IoSession;  
  4.   
  5. /** 
  6.  * Provides {@link ProtocolEncoder} and {@link ProtocolDecoder} which translates 
  7.  * binary or protocol specific data into message object and vice versa. 
  8.  * <p> 
  9.  * Please refer to 
  10.  * <a href="../../../../../xref-examples/org/apache/mina/examples/reverser/ReverseProtocolProvider.html"><code>ReverserProtocolProvider</code></a> 
  11.  * example. 
  12.  * 
  13.  * @author <a href="http://mina.apache.org">Apache MINA Project</a> 
  14.  */  
  15. public interface ProtocolCodecFactory {  
  16.     /** 
  17.      * Returns a new (or reusable) instance of {@link ProtocolEncoder} which 
  18.      * encodes message objects into binary or protocol-specific data. 
  19.      */  
  20.     ProtocolEncoder getEncoder(IoSession session) throws Exception;  
  21.   
  22.     /** 
  23.      * Returns a new (or reusable) instance of {@link ProtocolDecoder} which 
  24.      * decodes binary or protocol-specific data into message objects. 
  25.      */  
  26.     ProtocolDecoder getDecoder(IoSession session) throws Exception;  
  27. }  

 

 

加密处理类:

Java代码   收藏代码
  1. package org.apache.mina.filter.codec;  
  2.   
  3. import org.apache.mina.core.buffer.IoBuffer;  
  4. import org.apache.mina.core.session.IoSession;  
  5.   
  6. /** 
  7.  * Encodes higher-level message objects into binary or protocol-specific data. 
  8.  * MINA invokes {@link #encode(IoSession, Object, ProtocolEncoderOutput)} 
  9.  * method with message which is popped from the session write queue, and then 
  10.  * the encoder implementation puts encoded messages (typically {@link IoBuffer}s) 
  11.  * into {@link ProtocolEncoderOutput} by calling {@link ProtocolEncoderOutput#write(Object)}. 
  12.  * <p> 
  13.  * Please refer to 
  14.  * <a href="../../../../../xref-examples/org/apache/mina/examples/reverser/TextLineEncoder.html"><code>TextLineEncoder</code></a> 
  15.  * example. 
  16.  * 
  17.  * @author <a href="http://mina.apache.org">Apache MINA Project</a> 
  18.  *  
  19.  * @see ProtocolEncoderException 
  20.  */  
  21. public interface ProtocolEncoder {  
  22.   
  23.     /** 
  24.      * Encodes higher-level message objects into binary or protocol-specific data. 
  25.      * MINA invokes {@link #encode(IoSession, Object, ProtocolEncoderOutput)} 
  26.      * method with message which is popped from the session write queue, and then 
  27.      * the encoder implementation puts encoded messages (typically {@link IoBuffer}s) 
  28.      * into {@link ProtocolEncoderOutput}. 
  29.      * 
  30.      * @throws Exception if the message violated protocol specification 
  31.      */  
  32.     void encode(IoSession session, Object message, ProtocolEncoderOutput out)  
  33.             throws Exception;  
  34.   
  35.     /** 
  36.      * Releases all resources related with this encoder. 
  37.      * 
  38.      * @throws Exception if failed to dispose all resources 
  39.      */  
  40.     void dispose(IoSession session) throws Exception;  
  41. }  

 

解密类如下:

Java代码   收藏代码
  1. package org.apache.mina.filter.codec;  
  2.   
  3. import org.apache.mina.core.buffer.IoBuffer;  
  4. import org.apache.mina.core.session.IoSession;  
  5.   
  6. /** 
  7.  * Decodes binary or protocol-specific data into higher-level message objects. 
  8.  * MINA invokes {@link #decode(IoSession, IoBuffer, ProtocolDecoderOutput)} 
  9.  * method with read data, and then the decoder implementation puts decoded 
  10.  * messages into {@link ProtocolDecoderOutput} by calling 
  11.  * {@link ProtocolDecoderOutput#write(Object)}. 
  12.  * <p> 
  13.  * Please refer to 
  14.  * <a href="../../../../../xref-examples/org/apache/mina/examples/reverser/TextLineDecoder.html"><code>TextLineDecoder</code></a> 
  15.  * example. 
  16.  * 
  17.  * @author <a href="http://mina.apache.org">Apache MINA Project</a> 
  18.  *  
  19.  * @see ProtocolDecoderException 
  20.  */  
  21. public interface ProtocolDecoder {  
  22.     /** 
  23.      * Decodes binary or protocol-specific content into higher-level message objects. 
  24.      * MINA invokes {@link #decode(IoSession, IoBuffer, ProtocolDecoderOutput)} 
  25.      * method with read data, and then the decoder implementation puts decoded 
  26.      * messages into {@link ProtocolDecoderOutput}. 
  27.      * 
  28.      * @throws Exception if the read data violated protocol specification 
  29.      */  
  30.     void decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out)  
  31.             throws Exception;  
  32.   
  33.     /** 
  34.      * Invoked when the specified <tt>session</tt> is closed.  This method is useful 
  35.      * when you deal with the protocol which doesn't specify the length of a message 
  36.      * such as HTTP response without <tt>content-length</tt> header. Implement this 
  37.      * method to process the remaining data that {@link #decode(IoSession, IoBuffer, ProtocolDecoderOutput)} 
  38.      * method didn't process completely. 
  39.      * 
  40.      * @throws Exception if the read data violated protocol specification 
  41.      */  
  42.     void finishDecode(IoSession session, ProtocolDecoderOutput out)  
  43.             throws Exception;  
  44.   
  45.     /** 
  46.      * Releases all resources related with this decoder. 
  47.      * 
  48.      * @throws Exception if failed to dispose all resources 
  49.      */  
  50.     void dispose(IoSession session) throws Exception;  
  51. }  

 

在androidpn中的没有采取任何加密方式,但是提供相关的类org.androidpn.server.xmpp.codec,如果需要可以针对传输的数据进行加密和解密工作。

Java代码   收藏代码
  1. package org.androidpn.server.xmpp.codec;  
  2.   
  3. import org.apache.mina.core.session.IoSession;  
  4. import org.apache.mina.filter.codec.ProtocolCodecFactory;  
  5. import org.apache.mina.filter.codec.ProtocolDecoder;  
  6. import org.apache.mina.filter.codec.ProtocolEncoder;  
  7.   
  8. /**  
  9.  * Factory class that specifies the encode and decoder to use for parsing XMPP stanzas. 
  10.  * 
  11.  * @author Sehwan Noh (devnoh@gmail.com) 
  12.  */  
  13. public class XmppCodecFactory implements ProtocolCodecFactory {  
  14.   
  15.     private final XmppEncoder encoder;  
  16.   
  17.     private final XmppDecoder decoder;  
  18.   
  19.     /** 
  20.      * Constructor. 
  21.      */  
  22.     public XmppCodecFactory() {  
  23.         encoder = new XmppEncoder();  
  24.         decoder = new XmppDecoder();  
  25.     }  
  26.   
  27.     /** 
  28.      * Returns a new (or reusable) instance of ProtocolEncoder. 
  29.      */  
  30.     public ProtocolEncoder getEncoder(IoSession session) throws Exception {  
  31.         return encoder;  
  32.     }  
  33.   
  34.     /** 
  35.      * Returns a new (or reusable) instance of ProtocolDecoder. 
  36.      */  
  37.     public ProtocolDecoder getDecoder(IoSession session) throws Exception {  
  38.         return decoder;  
  39.     }  
  40.   
  41. }  

 

androidpn的解密类:

Java代码   收藏代码
  1. package org.androidpn.server.xmpp.codec;  
  2.   
  3. import org.androidpn.server.xmpp.net.XmppIoHandler;  
  4. import org.apache.mina.core.buffer.IoBuffer;  
  5. import org.apache.mina.core.session.IoSession;  
  6. import org.apache.mina.filter.codec.CumulativeProtocolDecoder;  
  7. import org.apache.mina.filter.codec.ProtocolDecoderOutput;  
  8. import org.jivesoftware.openfire.nio.XMLLightweightParser;  
  9.   
  10. /**  
  11.  * Decoder class that parses ByteBuffers and generates XML stanzas. 
  12.  * 
  13.  * @author Sehwan Noh (devnoh@gmail.com) 
  14.  */  
  15. public class XmppDecoder extends CumulativeProtocolDecoder {  
  16.   
  17.     // private final Log log = LogFactory.getLog(XmppDecoder.class);  
  18.   
  19.     @Override  
  20.     public boolean doDecode(IoSession session, IoBuffer in,  
  21.             ProtocolDecoderOutput out) throws Exception {  
  22.         // log.debug("doDecode(...)...");  
  23.   
  24.         XMLLightweightParser parser = (XMLLightweightParser) session  
  25.                 .getAttribute(XmppIoHandler.XML_PARSER);  
  26.         parser.read(in);  
  27.   
  28.         if (parser.areThereMsgs()) {  
  29.             for (String stanza : parser.getMsgs()) {  
  30.                 out.write(stanza);  
  31.             }  
  32.         }  
  33.         return !in.hasRemaining();  
  34.     }  
  35.   
  36. }  

 

 

androidpn的加密类:

Java代码   收藏代码
  1. package org.androidpn.server.xmpp.codec;  
  2.   
  3. import org.apache.mina.core.session.IoSession;  
  4. import org.apache.mina.filter.codec.ProtocolEncoder;  
  5. import org.apache.mina.filter.codec.ProtocolEncoderOutput;  
  6.   
  7. /**  
  8.  *  Encoder class that does nothing (to the already encoded data).  
  9.  * 
  10.  * @author Sehwan Noh (devnoh@gmail.com) 
  11.  */  
  12. public class XmppEncoder implements ProtocolEncoder {  
  13.   
  14.     // private final Log log = LogFactory.getLog(XmppEncoder.class);  
  15.   
  16.     public void encode(IoSession session, Object message,  
  17.             ProtocolEncoderOutput out) throws Exception {  
  18.         // log.debug("encode()...");  
  19.     }  
  20.   
  21.     public void dispose(IoSession session) throws Exception {  
  22.         // log.debug("dispose()...");  
  23.     }  
  24.   
  25. }  

   最终将编码解析器转换为过滤器使用,具体配置如下:

Xml代码   收藏代码
  1. <bean class="org.apache.mina.filter.codec.ProtocolCodecFilter">  
  2.     <constructor-arg>  
  3.         <bean class="org.androidpn.server.xmpp.codec.XmppCodecFactory" />  
  4.     </constructor-arg>  
  5. </bean>  



---------------------(六)Androidpn-server的添加其他xmpp相关的协议(如查看好友列表等)----------------



 曾经有一个同学,在网上问我,如果想androidpn添加额外的xmpp协议的方法在怎么加呢?我当时很迷惑,后来经过一翻仔细研究androidpn发现,其实每一种处理xmpp协议方法,必须有一个Handler实现。具体可以参考org.androidpn.server.xmpp.handler中。

 

 

针对每一个Handler对应的xml拥有不同的命名空间,每一个命名空间在xmpp中都有定义,因为传输的xml的格式是一定的。

例如:

               IQAuthHandler:命名空间 String NAMESPACE = "jabber:iq:auth";

               IQRegisterHandler:命名空间 String NAMESPACE = "jabber:iq:register";

               IQRosterHandler:命名空间 String NAMESPACE = "jabber:iq:roster";

 

 

        同时我们从Handler的实现可以看到每一个handler最好实现对应IQHandler类,但是为辅助类型的Handler那么可以不用,例如androidpn的状态更新处理器类PresenceUpdateHandler,不必要发送相关的消息到客户端。

       所以如果要实现xmpp中如查看好友,用户分组等通信协议,那么你可能要实现相关的Handler并使用xmpp协议规定的相关的命名空间。

 

       在androidpn中主要的业务处理类XmppIoHandler可以看出最终消息解析之后分发到IQRouter中。IQRouter用于处理消息的响应的消息。

IQRouter的源代码如下:

 

Java代码   收藏代码
  1. package org.androidpn.server.xmpp.router;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.List;  
  5. import java.util.Map;  
  6. import java.util.concurrent.ConcurrentHashMap;  
  7.   
  8. import org.androidpn.server.xmpp.handler.IQAuthHandler;  
  9. import org.androidpn.server.xmpp.handler.IQHandler;  
  10. import org.androidpn.server.xmpp.handler.IQRegisterHandler;  
  11. import org.androidpn.server.xmpp.handler.IQRosterHandler;  
  12. import org.androidpn.server.xmpp.session.ClientSession;  
  13. import org.androidpn.server.xmpp.session.Session;  
  14. import org.androidpn.server.xmpp.session.SessionManager;  
  15. import org.apache.commons.logging.Log;  
  16. import org.apache.commons.logging.LogFactory;  
  17. import org.dom4j.Element;  
  18. import org.xmpp.packet.IQ;  
  19. import org.xmpp.packet.JID;  
  20. import org.xmpp.packet.PacketError;  
  21.   
  22. /**  
  23.  * This class is to route IQ packets to their corresponding handler. 
  24.  * 
  25.  * @author Sehwan Noh (devnoh@gmail.com) 
  26.  */  
  27. public class IQRouter {  
  28.   
  29.     private final Log log = LogFactory.getLog(getClass());  
  30.   
  31.     private SessionManager sessionManager;  
  32.   
  33.     private List<IQHandler> iqHandlers = new ArrayList<IQHandler>();  
  34.   
  35.     private Map<String, IQHandler> namespace2Handlers = new ConcurrentHashMap<String, IQHandler>();  
  36.   
  37.     /** 
  38.      * Constucts a packet router registering new IQ handlers. 
  39.      */  
  40.     public IQRouter() {  
  41.         sessionManager = SessionManager.getInstance();  
  42.         iqHandlers.add(new IQAuthHandler());  
  43.         iqHandlers.add(new IQRegisterHandler());  
  44.         iqHandlers.add(new IQRosterHandler());  
  45.     }  
  46.   
  47.     /** 
  48.      * Routes the IQ packet based on its namespace. 
  49.      *  
  50.      * @param packet the packet to route 
  51.      */  
  52.     public void route(IQ packet) {  
  53.         if (packet == null) {  
  54.             throw new NullPointerException();  
  55.         }  
  56.         JID sender = packet.getFrom();  
  57.         ClientSession session = sessionManager.getSession(sender);  
  58.   
  59.         if (session == null  
  60.                 || session.getStatus() == Session.STATUS_AUTHENTICATED  
  61.                 || ("jabber:iq:auth".equals(packet.getChildElement()  
  62.                         .getNamespaceURI())  
  63.                         || "jabber:iq:register".equals(packet.getChildElement()  
  64.                                 .getNamespaceURI()) || "urn:ietf:params:xml:ns:xmpp-bind"  
  65.                         .equals(packet.getChildElement().getNamespaceURI()))) {  
  66.             handle(packet);  
  67.         } else {  
  68.             IQ reply = IQ.createResultIQ(packet);  
  69.             reply.setChildElement(packet.getChildElement().createCopy());  
  70.             reply.setError(PacketError.Condition.not_authorized);  
  71.             session.process(reply);  
  72.         }  
  73.     }  
  74.   
  75.     private void handle(IQ packet) {  
  76.         try {  
  77.             Element childElement = packet.getChildElement();  
  78.             String namespace = null;  
  79.             if (childElement != null) {  
  80.                 namespace = childElement.getNamespaceURI();  
  81.             }  
  82.             if (namespace == null) {  
  83.                 if (packet.getType() != IQ.Type.result  
  84.                         && packet.getType() != IQ.Type.error) {  
  85.                     log.warn("Unknown packet " + packet);  
  86.                 }  
  87.             } else {  
  88.                 IQHandler handler = getHandler(namespace);  
  89.                 if (handler == null) {  
  90.                     sendErrorPacket(packet,  
  91.                             PacketError.Condition.service_unavailable);  
  92.                 } else {  
  93.                     handler.process(packet);  
  94.                 }  
  95.             }  
  96.   
  97.         } catch (Exception e) {  
  98.             log.error("Could not route packet", e);  
  99.             Session session = sessionManager.getSession(packet.getFrom());  
  100.             if (session != null) {  
  101.                 IQ reply = IQ.createResultIQ(packet);  
  102.                 reply.setError(PacketError.Condition.internal_server_error);  
  103.                 session.process(reply);  
  104.             }  
  105.         }  
  106.     }  
  107.   
  108.     /** 
  109.      * Senda the error packet to the original sender 
  110.      */  
  111.     private void sendErrorPacket(IQ originalPacket,  
  112.             PacketError.Condition condition) {  
  113.         if (IQ.Type.error == originalPacket.getType()) {  
  114.             log.error("Cannot reply an IQ error to another IQ error: "  
  115.                     + originalPacket);  
  116.             return;  
  117.         }  
  118.         IQ reply = IQ.createResultIQ(originalPacket);  
  119.         reply.setChildElement(originalPacket.getChildElement().createCopy());  
  120.         reply.setError(condition);  
  121.         try {  
  122.             PacketDeliverer.deliver(reply);  
  123.         } catch (Exception e) {  
  124.             // Ignore  
  125.         }  
  126.     }  
  127.   
  128.     /** 
  129.      * Adds a new IQHandler to the list of registered handler. 
  130.      *  
  131.      * @param handler the IQHandler 
  132.      */  
  133.     public void addHandler(IQHandler handler) {  
  134.         if (iqHandlers.contains(handler)) {  
  135.             throw new IllegalArgumentException(  
  136.                     "IQHandler already provided by the server");  
  137.         }  
  138.         namespace2Handlers.put(handler.getNamespace(), handler);  
  139.     }  
  140.   
  141.     /** 
  142.      * Removes an IQHandler from the list of registered handler. 
  143.      *  
  144.      * @param handler the IQHandler 
  145.      */  
  146.     public void removeHandler(IQHandler handler) {  
  147.         if (iqHandlers.contains(handler)) {  
  148.             throw new IllegalArgumentException(  
  149.                     "Cannot remove an IQHandler provided by the server");  
  150.         }  
  151.         namespace2Handlers.remove(handler.getNamespace());  
  152.     }  
  153.   
  154.     /** 
  155.      * Returns an IQHandler with the given namespace. 
  156.      */  
  157.     private IQHandler getHandler(String namespace) {  
  158.         IQHandler handler = namespace2Handlers.get(namespace);  
  159.         if (handler == null) {  
  160.             for (IQHandler handlerCandidate : iqHandlers) {  
  161.                 if (namespace.equalsIgnoreCase(handlerCandidate.getNamespace())) {  
  162.                     handler = handlerCandidate;  
  163.                     namespace2Handlers.put(namespace, handler);  
  164.                     break;  
  165.                 }  
  166.             }  
  167.         }  
  168.         return handler;  
  169.     }  
  170.   
  171. }  

 

        由以上的源代码可以看出,IQRouter在加载时候将各种处理器添加到回话管理器中,当消息分发到IQRouter中时候,根据命名空间的不同使用不同的处理处置即可。



---------------------------------------------------(七)androidpn 中业务类XmppIoHandler实现分析---------------------------------------------------



在androidpn中主要采用Mina进行网络通讯,其中Mina中IoHandler用来处理主要的业务逻辑。

Mina 中源代码如下:

Java代码   收藏代码
  1. package org.apache.mina.core.service;  
  2.   
  3. import java.io.IOException;  
  4.   
  5. import org.apache.mina.core.session.IdleStatus;  
  6. import org.apache.mina.core.session.IoSession;  
  7.   
  8. /** 
  9.  * Handles all I/O events fired by MINA. 
  10.  * 
  11.  * @author <a href="http://mina.apache.org">Apache MINA Project</a> 
  12.  * 
  13.  * @see IoHandlerAdapter 
  14.  */  
  15. public interface IoHandler {  
  16.     /** 
  17.      * Invoked from an I/O processor thread when a new connection has been created. 
  18.      * Because this method is supposed to be called from the same thread that 
  19.      * handles I/O of multiple sessions, please implement this method to perform 
  20.      * tasks that consumes minimal amount of time such as socket parameter 
  21.      * and user-defined session attribute initialization. 
  22.      */  
  23.     void sessionCreated(IoSession session) throws Exception;  
  24.   
  25.     /** 
  26.      * Invoked when a connection has been opened.  This method is invoked after 
  27.      * {@link #sessionCreated(IoSession)}.  The biggest difference from 
  28.      * {@link #sessionCreated(IoSession)} is that it's invoked from other thread 
  29.      * than an I/O processor thread once thread model is configured properly. 
  30.      */  
  31.     void sessionOpened(IoSession session) throws Exception;  
  32.   
  33.     /** 
  34.      * Invoked when a connection is closed. 
  35.      */  
  36.     void sessionClosed(IoSession session) throws Exception;  
  37.   
  38.     /** 
  39.      * Invoked with the related {@link IdleStatus} when a connection becomes idle. 
  40.      * This method is not invoked if the transport type is UDP; it's a known bug, 
  41.      * and will be fixed in 2.0. 
  42.      */  
  43.     void sessionIdle(IoSession session, IdleStatus status) throws Exception;  
  44.   
  45.     /** 
  46.      * Invoked when any exception is thrown by user {@link IoHandler} 
  47.      * implementation or by MINA.  If <code>cause</code> is an instance of 
  48.      * {@link IOException}, MINA will close the connection automatically. 
  49.      */  
  50.     void exceptionCaught(IoSession session, Throwable cause) throws Exception;  
  51.   
  52.     /** 
  53.      * Invoked when a message is received. 
  54.      */  
  55.     void messageReceived(IoSession session, Object message) throws Exception;  
  56.   
  57.     /** 
  58.      * Invoked when a message written by {@link IoSession#write(Object)} is 
  59.      * sent out. 
  60.      */  
  61.     void messageSent(IoSession session, Object message) throws Exception;  
  62. }  

 

Mina中IoHandler可供处理的事件回调

sessionCreate(IoSession)

  IoSession对象被创建时的回调,一般用于进行会话初始化操作。注意:与sessionOpened(IoSession)不同,IoSession对象的创建并不意味着对应底层TCP连接的建立,而仅仅代表字面意思:一个IoSession对象被创建出来了。

sessionOpened(IoSession)

  IoSession对象被打开时回调。在TCP中,该事件是在TCP连接建立时触发,一般可用于发起连接建立的握手、认证等操作。

sessionIdle(IoSession,IdleStatus)

  IoSession对象超时时回调。当一个IoSession对象在指定的超时时常内没有读写事件发生,就会触发该事件,一般可用于通知服务器断开长时间闲置的连接等处理。具体的超时设置可由 IoService.setWriteIdleTime(int) ,IoService.setReadIdleTime(int) ,IoService.setBothIdleTime(int)设置。

messageReceived(IoSession,Object)

  当接收到IoSession对Client发送的数据时回调。

messageSent(IoSession,Object)

  当发送给IoSession对Client的数据发送成功时回调。

exceptionCaught(IoSession,Throwable)

  当会话过程中出现异常时回调,通常用于错误处理。

 

 

 

session.write(Object)方法是一个异步方法,对该方法的调用并不会阻塞,而是向Mina投递一个异步的写操作,并返回一个可用于对已投递异步写操作进行控制的WriteFuture对象。例如:调用WriteFuture的await()或awaitUninterruptibly(),可由同步等待该异步操作的完成。

 

在I/O处理器中实现业务逻辑的时候,对于简单的情况,一般只需要在messageReceived中对传入的消息进行处理。如果需要写回数据到对等体,用IoSession.write()即可。

另外的情况,client和server的通信协议比较复杂,client是有状态变迁的,这时可用Mina提供的状态机实现,可使用IO处理器的实现更加简单。

 

androidpn中XmppIoHandler源代码:

Java代码   收藏代码
  1. package org.androidpn.server.xmpp.net;  
  2.   
  3. import java.util.Map;  
  4. import java.util.concurrent.ConcurrentHashMap;  
  5.   
  6. import org.androidpn.server.xmpp.XmppServer;  
  7. import org.apache.commons.logging.Log;  
  8. import org.apache.commons.logging.LogFactory;  
  9. import org.apache.mina.core.service.IoHandler;  
  10. import org.apache.mina.core.session.IdleStatus;  
  11. import org.apache.mina.core.session.IoSession;  
  12. import org.dom4j.io.XMPPPacketReader;  
  13. import org.jivesoftware.openfire.net.MXParser;  
  14. import org.jivesoftware.openfire.nio.XMLLightweightParser;  
  15. import org.xmlpull.v1.XmlPullParserException;  
  16. import org.xmlpull.v1.XmlPullParserFactory;  
  17.   
  18. /**  
  19.  * This class is to create new sessions, destroy sessions and deliver 
  20.  * received XML stanzas to the StanzaHandler. 
  21.  * 
  22.  * @author Sehwan Noh (devnoh@gmail.com) 
  23.  */  
  24. public class XmppIoHandler implements IoHandler {  
  25.   
  26.     private static final Log log = LogFactory.getLog(XmppIoHandler.class);  
  27.   
  28.     public static final String XML_PARSER = "XML_PARSER";  
  29.   
  30.     private static final String CONNECTION = "CONNECTION";  
  31.   
  32.     private static final String STANZA_HANDLER = "STANZA_HANDLER";  
  33.   
  34.     private String serverName;  
  35.   
  36.     private static Map<Integer, XMPPPacketReader> parsers = new ConcurrentHashMap<Integer, XMPPPacketReader>();  
  37.   
  38.     private static XmlPullParserFactory factory = null;  
  39.   
  40.     static {  
  41.         try {  
  42.             factory = XmlPullParserFactory.newInstance(  
  43.                     MXParser.class.getName(), null);  
  44.             factory.setNamespaceAware(true);  
  45.         } catch (XmlPullParserException e) {  
  46.             log.error("Error creating a parser factory", e);  
  47.         }  
  48.     }  
  49.   
  50.     /** 
  51.      * Constructor. Set the server name from server instance.  
  52.      */  
  53.     protected XmppIoHandler() {  
  54.         serverName = XmppServer.getInstance().getServerName();  
  55.     }  
  56.   
  57.     /** 
  58.      * Invoked from an I/O processor thread when a new connection has been created. 
  59.      */  
  60.     public void sessionCreated(IoSession session) throws Exception {  
  61.         log.debug("sessionCreated()...");  
  62.     }  
  63.   
  64.     /** 
  65.      * Invoked when a connection has been opened. 
  66.      */  
  67.     public void sessionOpened(IoSession session) throws Exception {  
  68.         log.debug("sessionOpened()...");  
  69.         log.debug("remoteAddress=" + session.getRemoteAddress());  
  70.         // Create a new XML parser  
  71.         XMLLightweightParser parser = new XMLLightweightParser("UTF-8");  
  72.         session.setAttribute(XML_PARSER, parser);  
  73.         // Create a new connection  
  74.         Connection connection = new Connection(session);  
  75.         session.setAttribute(CONNECTION, connection);  
  76.         session.setAttribute(STANZA_HANDLER, new StanzaHandler(serverName,  
  77.                 connection));  
  78.     }  
  79.   
  80.     /** 
  81.      * Invoked when a connection is closed. 
  82.      */  
  83.     public void sessionClosed(IoSession session) throws Exception {  
  84.         log.debug("sessionClosed()...");  
  85.         Connection connection = (Connection) session.getAttribute(CONNECTION);  
  86.         connection.close();  
  87.     }  
  88.   
  89.     /** 
  90.      * Invoked with the related IdleStatus when a connection becomes idle. 
  91.      */  
  92.     public void sessionIdle(IoSession session, IdleStatus status)  
  93.             throws Exception {  
  94.         log.debug("sessionIdle()...");  
  95.         Connection connection = (Connection) session.getAttribute(CONNECTION);  
  96.         if (log.isDebugEnabled()) {  
  97.             log.debug("Closing connection that has been idle: " + connection);  
  98.         }  
  99.         connection.close();  
  100.     }  
  101.   
  102.     /** 
  103.      * Invoked when any exception is thrown. 
  104.      */  
  105.     public void exceptionCaught(IoSession session, Throwable cause)  
  106.             throws Exception {  
  107.         log.debug("exceptionCaught()...");  
  108.         log.error(cause);  
  109.     }  
  110.   
  111.     /** 
  112.      * Invoked when a message is received. 
  113.      */  
  114.     public void messageReceived(IoSession session, Object message)  
  115.             throws Exception {  
  116.         log.debug("messageReceived()...");  
  117.         log.debug("RCVD: " + message);  
  118.   
  119.         // Get the stanza handler  
  120.         StanzaHandler handler = (StanzaHandler) session  
  121.                 .getAttribute(STANZA_HANDLER);  
  122.   
  123.         // Get the XMPP packet parser  
  124.         int hashCode = Thread.currentThread().hashCode();  
  125.         XMPPPacketReader parser = parsers.get(hashCode);  
  126.         if (parser == null) {  
  127.             parser = new XMPPPacketReader();  
  128.             parser.setXPPFactory(factory);  
  129.             parsers.put(hashCode, parser);  
  130.         }  
  131.   
  132.         // The stanza handler processes the message  
  133.         try {  
  134.             handler.process((String) message, parser);  
  135.         } catch (Exception e) {  
  136.             log.error(  
  137.                     "Closing connection due to error while processing message: "  
  138.                             + message, e);  
  139.             Connection connection = (Connection) session  
  140.                     .getAttribute(CONNECTION);  
  141.             connection.close();  
  142.         }  
  143.     }  
  144.   
  145.     /** 
  146.      * Invoked when a message written by IoSession.write(Object) is sent out. 
  147.      */  
  148.     public void messageSent(IoSession session, Object message) throws Exception {  
  149.         log.debug("messageSent()...");  
  150.     }  
  151.   
  152. }  

 XmppIoHandler在加载的时候创建相关的xml解析工厂。

        sessionOpened:在连接打开时候创建相关的xml的解析器和Handler处理器。

        sessionClosed:关闭相关的连接。

        sessionIdle:关闭相关的连接。

        messageReceived:获取相关的xml解析器和handler处理器处理相关的消息。



最近正在做一个项目,要用到Android的Push技术。目前对于Android的推送技术,用的比较多的还是AndroidPn。由于要对Push的服务器端,进行压力测试。当然,不可能真找几千台手机来测试。所以只能通过PC端模拟AndroidPN的用户端,每个线程代表一个AndroidPN的客户端。

闲话少说,要想在PC端模拟AndroidPN的客户端,不了解 源码是不行的。
Google一下,大致可以找到相关源码的解析。本文是在相关基础上,添加些自己的见解。
Androidpn包含有server和client两个包,server部分可以作为服务器单独运行,也可以嵌入到web项目的servlet中,在tomcat环境中与web项目的其他部分交互。
Server部分的主要包结构如下:
其中org.androidpn.server.dao,org.androidpn.server.model和org.androidpn.server.service为使用hibernate链接 数据库并实现简单的用户登录认证,开发中可以用我们自己的认证模块替换。剩下的包就是推送的主体实现。
接下来逐个包来看:
1.util包中的类用来加载resources中的配置文件,在配置文件中可指定监听端口和ssl证书目录等属性。
2.org.androidpn.server.xmpp包里面定义了一些异常类型,主要是包含有入口类XmppServer,这个类用来启动和停止server程序。
3.org.androidpn.server.xmpp.auth包里面是认证的一些类,我们自己的认证模块可以在这里与androidpn进行结合。
4.org.androidpn.server.xmpp.codec是XMPP协议的XML文件解析包,server收到和发送的消息都要通过这个包来进行xmpp协议编码和解码。
5.org.androidpn.server.xmpp.handler包主要是对消息的处理,我们可以针对不同的消息类型定义自己的handler,
6.org.androidpn.server.xmpp.net包负责维护与client之间的持久连接,并实现了一些传输方式供发送xmpp消息时使用。
7.org.androidpn.server.xmpp.presence里面只包含PresenceManager类,用来维护client的在线状态。
8.org.androidpn.server.xmpp.push包里面的NotificationManager类包含有向client发送消息的接口。
9.org.androidpn.server.xmpp.router包负责将收到的信息包发送到相应的handler进行处理,是一个路由包。
10.org.androidpn.server.xmpp.session包定义了用来表示持久链接的session,每个session包含一条连接的状态信息。
11.org.androidpn.server.xmpp.ssl是对连接进行ssl认证的工具包。(目前服务器使用的是NonSASLAuthentication认证)
跟XMPP协议有关的类:
IQ Presence Message 分别表示XMPP中的<iq>节,<presence>节,<message>节
Packet表示XMPP节的抽象类,IQ Presence Message均是Packet的子类
Element表示XML节中的元素
如果对XMPP协议不太了解,可以先大致了解下XMPP协议中各种xml节的含义。
server发送消息的整个流程主要是:
1. NotificationManager的push接口被调用。
2.使用SessionManager在当前session集合中查找相应的client链接。
3.定义自己的XMPP消息格式并组装。
4.通过相应session,向client发送消息。
在这个流程中我们需要修改的是步骤3,也就是需要定义和组装自己的xmpp消息,以便于将适当的信息传到客户端并便于客户端解析。一个简单的消息组装例子如下:
   1: private IQ createCustomizeIQ(String apiKey, String title,
   2:            String message, String uri) {
   3:        Random random = new Random();
   4:        String id = Integer.toHexString(random.nextInt());
   5:        // String id = String.valueOf(System.currentTimeMillis());
   6:  
   7:        Element notification = DocumentHelper.createElement(QName.get(
   8:                "notification", NOTIFICATION_NAMESPACE));
   9:        notification.addElement("id").setText(id);
  10:        notification.addElement("title").setText(title);
  11:        notification.addElement("message").setText(message);
  12:        notification.addElement("uri").setText(uri);
  13:        //自定义IQ的属性
  14:        notification.addElement("属性名").setText(属性);
  15:        IQ iq = new IQ();
  16:        iq.setType(IQ.Type.set);
  17:        iq.setChildElement(notification);
  18:  
  19:        return iq;
  20:    }
要注意的是在创建element的时候,传入的namespace要和client解析使用的namespace相匹配。
server端接收和处理消息的流程是:
1.connection收到packet,使用tsc.push.server.xmpp.codec解码。
2.router根据packet的namespace等信息,将packet路由到相应的handler。
3.handler进行处理。
相应的router和handler类在androidpn中都有例子可以参考,这里就不贴代码了。开发中只要根据client发送消息的格式,定义自己的router和handler类,然后在PacketRouter中注册router,在IQHandler中注册handler即可。
补充:
PacketRouter注册router的具体步骤
      1.即在PacketRouter中添加成员变量,类型为自己定义的XXXRouter(自定义消息 路由器)
Image
     2.在PackRouter的构造函数里初始化XXXRouter
Image(1)
     3.根据不同的消息类型调用相应的路由器
Image(2)
     4.在PacketRouter中添加XXXRouter的路由方法route(XXX xxx)
Image(3)
注:XXX(自定义XMPP节)是Packet的子类
IQHandler注册handler的具体步骤
IQHandler处理信息<iq>节的抽象类,注册IQHandler就是继承IQHandler,重写其中的handleIQ(IQ)方法返回
     应答的<iq>节.
IQHandler的process(IQ)即是处理各种IQ,在实际过程中是IQHandler handler = new IQXXXHandler()。在调用
     handler.process()就会调用子类的handleIQ(IQ)方法
Client部分的主要包结构如下:
Client这边包含有消息的收发,解析以及持久连接的发起,重连等功能呢,十分强大,我们开发时完全不用管底层的连接,也不用担心断线,可以专注于业务部分的开发。
同时,代码结构也很简单。去除android的Service和BroadCast类以及一些工具类和常量类不谈:
1.NotificationIQ,NotificationIQProvider,NotificationPacketListener三个类负责对收到的Notification格式的消息进行解析和处理,
2.XmppManager是主控制器,NotificationService通过这个类,在后台维护androidpn连接。
3.PersistentConnectionListener,PhoneStateChangeListener,ReconnectionThread.java三个类则负责监听手机的状态并进行断线重连。
我们自定义消息时需要定义3个类:在***IQ中定义消息的实体,在***IQProvider中将消息转化为***IQ实体,在***PacketListener中对实体进行处理,具体的实现可参考NotificationIQ,NotificationIQProvider,NotificationPacketListener三个类。在定义这些类之后,还需要在XmppManager中将这3个类中注册到connection中,代码如下:
  //ConnectTask类在XmppManager类里
   1: Log.i(LOGTAG, "XMPP connected successfully");
   2: // packet provider
   3: ProviderManager.getInstance().addIQProvider("notification",
   4:     "androidpn:iq:notification",new NotificationIQProvider());
  //LoginTask类在XmppManager类里
   1: // packet filter
   2: PacketFilter packetFilter = new PacketTypeFilter(NotificationIQ.class);
   3: // packet listener
   4: PacketListener packetListener = xmppManager.getNotificationPacketListener();
   5: connection.addPacketListener(packetListener, packetFilter);
需要注意的是,注册***IQProvider时,传入的namespace需要和服务端组装消息时使用的namespace一致,才能正确的收到。 
以上红色部分,是自己在看这篇文章时,所添加上去的,算是对这篇文章的一点补充…如果不对的地方,请大家指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值