就最近的形势来看,似乎是不差的。变革即意味着机遇,确实如此。我觉得我似乎在朝着自己的计划慢慢靠近。我想很快就会有确切的答案吧。我静静地等着。
最近两天一直在研究Android 信息推送的事情。学习了AndroidPN这个框架,虽然最后不会用这个,项目还是要自己搭,但终究有值得借鉴的地方,特此把学习的内容总结在这里。
AndroidPN(Android Push Notification)是韩国人思密达共享的一个简单的框架,基于XMPP协议,实现了信息推送服务。
一:安装测试运行
1,下载客户端和服务器端程序。http://sourceforge.net/projects/androidpn/files/ 程序10年11月上传后就没有再更改过,可能已经被原分享者放弃了思密达。
2,打开客户端程序下的raw/androidpn.properties文件,配置客户端信息,将xmppHost配置成10.0.2.2,xmppPort=5222,5222 是服务器的xmpp服务监听端口。
3,运行androidpn-server-0.5.0\bin\run.bat启动服务器,从浏览器访问http://127.0.0.1:7070/
在模拟器中运行客户端。
4,从Web端向客户端发消息。
效果如下:
友情提示:
1,如果出现运行run.bat一闪而过,无法访问http://127.0.0.1:7070/的情况,请配置好Java环境变量。
2,客户端运行后后提示“Application unfortunately Stopped”,有可能是引用的asmack.jar包的问题,重新引用,然后Clean,重新Build Project。祝大家好运。
二:源代码学习
在项目中我是写客户端的,所以我主要学习了客户端的源码。现总结如下:
1,程序入口DemoAppActivity开启服务:
// Start the service
ServiceManager serviceManager = new ServiceManager(this);
serviceManager.setNotificationIcon(R.drawable.notification);//设置消息的图标
serviceManager.startService();
在实例化ServiceManage的过程中,做了以下工作,加载 res/raw/androidpn.properties 配置文件中的参数信息,并将其保存在SharedPreferences中。然后调用startService()开启服务。
public void startService() {
Thread serviceThread = new Thread(new Runnable() {
@Override
public void run() {
Intent intent = NotificationService.getIntent();
context.startService(intent);
}
});
serviceThread.start();
}
startService()方法开启了一个子线程去启动真正的信息推送服务NotificationService。对于Service,作者思密达在OnCreate()的时候做了很多工作,OnCreate()方法在服务被创建时调用,且只会被调用一次。因此多次的start()并不会产生多个实例。在OnCreate()方法中,作者获取了一个很重要的参数deviceId设备ID。可是后面并没有使用它。
telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
// Get deviceId
deviceId = telephonyManager.getDeviceId();
获得设备ID后,
xmppManager = new XmppManager(this);
taskSubmitter.submit(new Runnable() {
public void run() {
NotificationService.this.start();
}
});
实例化NotificationService类时会做以下工作:
public NotificationService() {
notificationReceiver = new NotificationReceiver();
connectivityReceiver = new ConnectivityReceiver(this);
phoneStateListener = new PhoneStateChangeListener(this);
executorService = Executors.newSingleThreadExecutor();
taskSubmitter = new TaskSubmitter(this);
taskTracker = new TaskTracker(this);
}
BroadcastReceiver类。PhoneStateChangeListener继承了PhoneStateListener,用于监听手机状态变化。Executors.newSingleThreadExecutor()返回一个线程池(这个线程池只有一个线程),这个线程池可以在线程死后(或发生异常时)重新启动一个线程来替代原来的线程继续执行下去。
NotificationService.start()方法
private void start() {
Log.d(LOGTAG, "start()...");
registerNotificationReceiver();
registerConnectivityReceiver();
// Intent intent = getIntent();
// startService(intent);
xmppManager.connect();
}
notificationReceiver来接收广播,registerConnectivityReceiver()方法注册了connectivityReceiver来监听网络连接状况。然后调用xmappManager的connect方法。
继续跟踪xmappManager的connect方法又执行了哪些工作
public void connect() {
Log.d(LOGTAG, "connect()...");
submitLoginTask();
}
调用了submitLoginTask()方法,顾名思义,提交登录任务。
private void submitLoginTask() {
Log.d(LOGTAG, "submitLoginTask()...");
submitRegisterTask();
addTask(new LoginTask());
}
提交登录任务中,又提交了一个注册任务,同时将新建的登录任务添加到任务集合中并交由 TaskTracker 来对添加的任务进行监视。继续根据在登录任务中执行的工作:
(1)如果连接没有经过身份验证,根据username和password执行登录操作,然后为连接添加各种监听机制。执行刚刚添加的任务runTask()(2)如果连接通过身份验证,直接执行任务runTask();
private LoginTask() {
this.xmppManager = XmppManager.this;
}
public void run() {
Log.i(LOGTAG, "LoginTask.run()...");
if (!xmppManager.isAuthenticated()) {
Log.d(LOGTAG, "username=" + username);
Log.d(LOGTAG, "password=" + password);
try {
xmppManager.getConnection().login(
xmppManager.getUsername(),
xmppManager.getPassword(), XMPP_RESOURCE_NAME);
Log.d(LOGTAG, "Loggedn in successfully");
// connection listener
if (xmppManager.getConnectionListener() != null) {
xmppManager.getConnection().addConnectionListener(
xmppManager.getConnectionListener());
}
// packet filter
PacketFilter packetFilter = new PacketTypeFilter(
NotificationIQ.class);
// packet listener
PacketListener packetListener = xmppManager
.getNotificationPacketListener();
connection.addPacketListener(packetListener, packetFilter);
xmppManager.runTask();
} catch (XMPPException e) {
Log.e(LOGTAG, "LoginTask.run()... xmpp error");
Log.e(LOGTAG, "Failed to login to xmpp server. Caused by: "
+ e.getMessage());
String INVALID_CREDENTIALS_ERROR_CODE = "401";
String errorMessage = e.getMessage();
if (errorMessage != null
&& errorMessage
.contains(INVALID_CREDENTIALS_ERROR_CODE)) {
xmppManager.reregisterAccount();
return;
}
xmppManager.startReconnectionThread();
} catch (Exception e) {
Log.e(LOGTAG, "LoginTask.run()... other error");
Log.e(LOGTAG, "Failed to login to xmpp server. Caused by: "
+ e.getMessage());
xmppManager.startReconnectionThread();
}
} else {
Log.i(LOGTAG, "Logged in already");
xmppManager.runTask();
}
}
}
具体的监听和接下来的操作会在下篇进行分析。