在客户端中,我们在AndroidManifest.xml文件中找到程序入口,即如下的DemoAppActivity类,

在此类中主要代码如下

ServiceManager serviceManager = newServiceManager(this);

serviceManager.setNotificationIcon(R.drawable.notification);

serviceManager.startService();

一路跟进,NotificationService中有个XmppManager类的调用(xmppManager.connect();),而XmppManager类就是客户端比较重要的类,有这几个方法

public void connect() {

Log.d(LOGTAG, "connect()...");

submitLoginTask();

}


private void submitConnectTask() {

Log.d(LOGTAG, "submitConnectTask()...");

addTask(new ConnectTask());

}


private voidsubmitRegisterTask() {

Log.d(LOGTAG, "submitRegisterTask()...");

submitConnectTask();

addTask(new RegisterTask());

}


private voidsubmitLoginTask() {

Log.d(LOGTAG, "submitLoginTask()...");

submitRegisterTask();

addTask(new LoginTask());

}


addTask方法中有一个线程队列,依次添加连接,注册和登陆任务。用runTask方法执行这些任务

在XmppManager类中有三个内部类,即连接(ConnectTask),注册(RegisterTask),登陆(LoginTask)三个线程类。

连接类负责与服务端的连接。


注册类中,即用UUID生成唯一的username和password,然后把它们保存到SharedPreferences中

final String newUsername = newRandomUUID();

final String newPassword = newRandomUUID();

.....

Editor editor = sharedPrefs.edit();

editor.putString(Constants.XMPP_USERNAME,newUsername);

editor.putString(Constants.XMPP_PASSWORD,newPassword);

editor.commit();

而在登陆类中,首先进行连接登陆,把客户端的信息更新到服务端,即

xmppManager.getConnection().login(

xmppManager.getUsername(),

xmppManager.getPassword(),XMPP_RESOURCE_NAME);

这个操作可能执行多次,直到真正更新到服务器。

执行连接登陆时,服务端就会收到信息。

在服务端,接受消息的类是router包(org.androidpn.server.xmpp.router)下的PacketRouter类,在这里分析消息的类型是什么类型,然后在调用具体的Router,如响应登陆的IQRouter。

如果是登陆(即通过唯一的客户端标识获取的session就是为null的),那么就要对这个包进行处理。处理消息包的操作是在org.androidpn.server.xmpp.handler包下进行的。把消息包处理完后就在维护连接的org.androidpn.server.xmpp.net包下的Connection类中传递消息包到客户端

XMLWriter xmlSerializer = new XMLWriter(newIoBufferWriter(

buffer,(CharsetEncoder) encoder.get()),

newOutputFormat());

xmlSerializer.write(packet.getElement());

xmlSerializer.flush();

buffer.flip();

ioSession.write(buffer);


如果登陆失败,就获取这个异常,重新进行登陆连接直到登陆成功。


注册登录的流程就如下所述,下面再讲一下,在服务端怎么发送消息到客户端,客户端怎么发消息到服务端,客户端怎么接收来自服务端的消息(服务端接收来自客户端的消息在如下登陆时已经讲了)


在服务端的推送消息的页面中(form.jsp),是通过notification.do?action=send来跳转后台的,那么这个后台是哪个类呢,在web.xml中,我们可以看到,拦截器都是spring的类,即这些都通过spring来托管了,那么我们再打开dispatcher-servlet.xml的spring的配置文件,可以看到

class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">

/index.do=filenameController

/user.do=userController

/session.do=sessionController

/notification.do=notificationController


notification.do跳转的页面就是notificationController即org.androidpn.server.console.controller.NotificationController这个类

class="org.androidpn.server.console.controller.NotificationController">


打开NotificationController类发现它是调用了NotificationManager

if (broadcast.equalsIgnoreCase("Y")) {

notificationManager.sendBroadcast(apiKey, title, message,uri);

} else {

notificationManager.sendNotifcationToUser(apiKey, username,title,

message, uri);

}


一路跟进,可以发现最终发送消息的类还是Connection的deliver方法。而中间的过程就是解析和生成消息的操作


那么客户端怎么接受消息呢,这个类应该是一个监听器,从类名和在客户端一路跟进的过程中,我们大概可以知道是NotificationPacketListener类,在这个类中获取各个节点的值,然后在客户端上发送一个通知


那么客户端发送消息的类又是在哪个类中呢,我们可以参考服务端的发送消息的类是在哪里,没错,是在Connection类中,那么我们在客户端的连接类中寻找一下是否有发送消息的类。果然,在org.jivesoftware.smack包下的XMPPConection这个类中有个发送消息的方法sendPacket。我们可以通过xmppManager.getConnection()来获取连接。