在客户端中,我们在AndroidManifest.xml文件中找到程序入口,即如下的DemoAppActivity类,
在此类中主要代码如下
ServiceManager serviceManager = new ServiceManager(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 void submitRegisterTask() {
Log.d(LOGTAG, "submitRegisterTask()...");
submitConnectTask();
addTask(new RegisterTask());
}
private void submitLoginTask() {
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(new IoBufferWriter(
buffer, (CharsetEncoder) encoder.get()),
new OutputFormat());
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()来获取连接。