XMPP(可扩展通讯和表示协议)是基于可扩展标记语言(XML)的协议,它用于即时消息(IM)以及在线探测。这个协议可能最终允许因特网用户向因特网上的其他任何人发送即时消息。用xmpp来实现android的push功能,感觉有点大材小用了,xmpp本身是一种即时通信协议。
xmpp是一种用于即时通信的协议,使用过程有点类似于我们使用QQ的过程,其使用过程分为三步:
1. 连接服务器,就好像打开QQ软件,看代码:
- if(!mXmppManager.isConnected()) {
- ConnectionConfiguration config = new ConnectionConfiguration(mHost, mPort);
- config.setSecurityMode(SecurityMode.required);
- config.setSASLAuthenticationEnabled(false);
- config.setCompressionEnabled(false);
- XMPPConnection connection = new XMPPConnection(config);
- mXmppManager.setConnection(connection);
- try {
- connection.connect();
- Log.i(LOGTAG, "XMPP connected successfully");
- /**
- * 这个就是对于通信的xml文本进行解析的解析器,再把信息转换成IQ,这个相当于QQ的聊天信息
- * 如果要用这个协议,其IQ的子类和IQProvider要进行重写
- */
- ProviderManager.getInstance().addIQProvider("notification",
- "androidpn:iq:notification",
- new NotificationIQProvider());
- } catch (XMPPException e) {
- Log.d(LOGTAG, "the connection is error ... ");
- }
- mXmppManager.runTask();
- } else {
- Log.i(LOGTAG, "XMPP connected already");
- mXmppManager.runTask();
- }
这一步主要是连接服务器,还有设置一些连接的参数,还有设置连接的解析器。
2. 如果没有用户,注册新的帐号和密码
- if(!mXmppManager.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));
- PacketListener packetListener = new PacketListener() {
- @Override
- public void processPacket(Packet packet) {
- // 服务器回复客户端
- if(packet instanceof IQ) {
- IQ response = (IQ) packet;
- if(response.getType() == IQ.Type.ERROR) { // 注册失败
- if (!response.getError().toString().contains(
- "409")) {
- Log.e(LOGTAG,"Unknown error while registering XMPP account! " + response.getError().getCondition());
- }
- } else if(response.getType() == IQ.Type.RESULT) { // 注册成功
- mXmppManager.setUsername(newUsername);
- mXmppManager.setPassword(newPassword);
- // 把用户名和密码都保存到磁盘
- Editor editor = mSharedPrefs.edit();
- editor.putString(Contants.XMPP_USERNAME, newUsername);
- editor.putString(Contants.XMPP_PASSWORD, newPassword);
- editor.commit();
- mXmppManager.runTask();
- }
- }
- }
- };
- // 给注册的Packet设置Listener,因为只有等到正真注册成功后,我们才可以交流
- mConnection.addPacketListener(packetListener, packetFilter);
- registration.setType(IQ.Type.SET);
- registration.addAttribute("username", newUsername);
- registration.addAttribute("password", newPassword);
- // 向服务器端,发送注册Packet包,注意其中Registration是Packet的子类
- mConnection.sendPacket(registration);
- } else { // 已经注册过了
- mXmppManager.runTask();
- }
只要连接了服务器了,客户端就可以向服务器端发送消息,发送是以Packet(数据包)来进行发送的,这个类有很多的子类,注册的子类为Registration。
还有要注意的是,上面的addPacketListener方法不是给所有发送的数据包设置listener,而只是针对这次的注册Packet。
3. 用注册的帐号和密码进行登陆(像用QQ号帐进行登陆一样)
- // 判断是否已经登陆过了,是否是在登陆状态
- if(!mXmppManager.isAuthenticated()) {
- try {
- mConnection.login(mUsername, mPassword, "AndroidpnClient");
- // 设置XmppConnection的监听器
- if(mXmppManager.getConnectionListener() != null) {
- mConnection.addConnectionListener(mXmppManager.getConnectionListener());
- }
- // 设置服务器端推送的监听器
- PacketFilter packetFilter = new PacketTypeFilter(NotificationIQ.class);
- PacketListener packetListener = mXmppManager.getNotificationPacketListener();
- mConnection.addPacketListener(packetListener, packetFilter);
- mXmppManager.runTask();
- } catch (XMPPException e) {
- // 登陆失败,应该重试
- String INVALID_CREDENTIALS_ERROR_CODE = "401";
- String errorMessage = e.getMessage();
- // 如果只是因为没有注册,则进行重新注册
- if (errorMessage != null && errorMessage.contains(INVALID_CREDENTIALS_ERROR_CODE)) {
- mXmppManager.reregisterAccount();
- return;
- }
- mXmppManager.startReconnectionThread();
- } catch (Exception e) { // 有可能mConnection都为空
- Log.e(LOGTAG, "LoginTask.run()... other error");
- Log.e(LOGTAG, "Failed to login to xmpp server. Caused by: " + e.getMessage());
- mXmppManager.startReconnectionThread(); // 启动重连线程
- }
- } else {
- mXmppManager.runTask();
- }
这里设置了连接的监听器mConnection.addConnectionListener(),连接过程中有可以连接突然中断,连接出错等等问题,要进行监听。
设置服务器推送信息的Listener,接收到信息后,显示给用户。
如果出错的原因是401(无效的用户名和密码,则应该进行重新注册,再连接)
对于服务器push过来的信息进行处理,是在PacketListener类里面,这个接口里,只要实现一个方法processPacket(Packet packet),从传过来的Packet(数据包)里获取自己需要的数据:
- public void processPacket(Packet packet) {
- if(packet instanceof NotificationIQ) {
- NotificationIQ notification = (NotificationIQ) packet;
- if(notification.getChildElementXML().contains("androidpn:iq:notification")) {
- String notificationId = notification.getId();
- String notificationApiKey = notification.getApiKey();
- String notificationTitle = notification.getTitle();
- String notificationMessage = notification.getMessage();
- String notificationUri = notification.getUri();
- Intent intent = new Intent(Contants.ACTION_SHOW_NOTIFICATION);
- intent.putExtra(Contants.NOTIFICATION_ID, notificationId);
- intent.putExtra(Contants.NOTIFICATION_API_KEY,notificationApiKey);
- intent.putExtra(Contants.NOTIFICATION_TITLE,notificationTitle);
- intent.putExtra(Contants.NOTIFICATION_MESSAGE, notificationMessage);
- intent.putExtra(Contants.NOTIFICATION_URI, notificationUri);
- mXmppManager.getContext().sendBroadcast(intent);
- }
- }
- }
对于Androidpn项目的整体代码分析,看下一遍文章。