构建Android Push Notification Service服务端及客户端[含代码]

本文介绍了一种基于MQTT协议的Android推送服务实现方法,详细解析了服务的设计原理、关键技术及其实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

终于又开始上班了,只有在值班的时候,才是我比较清闲的时候,可以静下来做自己喜欢的事情,看自己喜欢的文章,写自己喜欢的博客。在Android架构部分,几个比较难啃的骨头里面,Android Push Notification Service算一个。我想今天来解释一下她的实现以及使用。

1 这个服务的必要性问题

在手机的使用过程中,我们知道,正睡觉呢,突然响起了短信声,打开一看,原来是移动/电信在提醒我们该上厕所了,或者天边冷了,多穿点衣服吧之类的话语。而在使用Android手机的时候,我们发现,如果有Gmail端,收到邮件的时候,会弹出一个提示,你有一条新邮件,并包含邮件的标题和相关信息。不知道你会不会好奇,这是如何实现的呢?我很好奇,所以便有了此文的写作动机。而对于QQ、安卓市场之类的软件,时不时的也弹出来这类信息,相信大家可以明白,这东西应该是有点用处的。比如我们开发一款应用,需要实时的提醒我们的安装用户一些事情,相信,你就会明白,这个服务是很有必要的,相信,在未来移动互联网、物联网占据大片江山的时候,也是很有必要的。

2 几个问题
好了,我们提出了这个东西的必要性,但是在做的时候,我们必须要考虑几个问题。
2.1 俺的电池不怎么抗用,可千万别太耗我的电量啊,这是哥最在意的啊。
2.2 除了花点流量,这玩意不要花我另外的钱,我可是月光族啊。
2.3 我着急要收到这个消息,别半小时后才把消息发给我,那样的话,会损失我的订单的。
2.4 必须要可靠哦,别用着用着,不好使了。

秉着以上的几个关键问题,我们开始了下一部分的探讨了。

3 几种可能的方案
我们来思考一下,要实现实时得到信息,有哪几种方法呢?
1 通过http/https或者其他协议,客户端以服务的方式,每隔10分钟或者10秒钟,向服务器请求一次,服务器判断这段时间是否有新消息,需要发给客户端,如果有就通过json或者xml方式发给客户端。
2 通过短信的方式,服务器端通过SMS的方式,将所需要的消息及时发送回来。
3 使用tcp长连接和心跳包的机制,实现数据定时推送。

4 采用的方案
从我的能力,我目前只能想到这么几种办法,下面我们来根据第二条里面的准则来分析上面提到的几种方案。
第一条通过http或者https的方式,向服务器每隔多长时间请求一次的方式,的确可以实现我们的功能,但是违反了我们的2.1和2.3原则。首先这种方式会耗电,当然你可以说时间设置长一点,但是这样又违背了2.3原则。所以这条一般是不会被采纳的。除非某些特殊应用。
第二条呢,2.1、2.3、2.4都符合,可是,违背了2.2,所以我们也不会考虑的。
第三条呢,好像全部符合,但是有一个小问题在里面,就是如果以Service的方式进行,由于Android系统的特殊性,在内存不够用的时候,会主动结束一些服务,这个服务包括了我们的定义服务,这么说,他违背了2.4。

但是,我们还是有办法的。

5 被采用方案的可实施方法
在Android 2.2以后,Google放出了C2DM【Android Cloud to Device Messaging Framework】服务,从服务的使用方法上,我们就可以明白他们采用了第三种方式。
随着他们推出这个服务后,很多公司开始基于这个服务做一些应用,如推送广告、推送定制信息等。如xtifyairpush等,国内也有一些企业加入了这种阵营,如单独提供服务的push-notification,当然QQ也有这样的服务存在。

在这种方案里面,有几个细节地方,需要来解释一下。
5.1 传输的时候使用什么协议?
5.2 传输的时候如何保证数据的安全性?
5.3 对于多平台,多用户的push如何保证惟一性?
5.4 服务器端的如何部署?

5.1的问题目前有几种方式,使用xmpp协议、IBM的MQTT、自定义协议。 目前有一些开源的项目中,大都采用第一种和第二种,当然,如果有特殊需求,可以采取自定义协议的。
5.2的问题可以对数据进行可逆加密。
5.3的问题,一般是将手机的ID传递到服务器端进行惟一性验证。
5.4的问题,服务器端可以自己使用任何语言开发,也可以使用Nginx + 脚本语言部署。

6 实例说明
本文的实例采用了mqtt的架构,完全按照tokudu兄的文章而来,并成功实现了。里面采取的不是IBM的Really Small Message Broker,而是采用的开源Mosquitto实现,

准备工作:

6.1 Android真机,本文为三星I809
6.2 Apache + Php环境
6.3 tokudu兄的Android源代码
6.4 tukudu兄的php代码
6.5 mosquitto的可执行程序。

步骤1:
下载mosquitto的可执行程序,我选择的是cygwin版本的,安装后,进入目录双击mosquitto.exe执行即可。

步骤2:下载tokudu兄的php代码,官方地址为:https://github.com/tokudu/PhpMQTTClient
我这里也提供下载:androidpushservice

主要代码为如下:

[java]  view plain copy
  1. <?php  
  2.    
  3. require('SAM/php_sam.php');  
  4.    
  5. //create a new connection object  
  6. $conn = new SAMConnection();  
  7.    
  8. //start initialise the connection  
  9. $conn->connect(SAM_MQTT, array(SAM_HOST => '202.198.21.131',  
  10.                                SAM_PORT => 1883));  
  11. //create a new MQTT message with the output of the shell command as the body  
  12. $msgCpu = new SAMMessage($_REQUEST['message']);  
  13.    
  14. //send the message on the topic cpu  
  15. $conn->send('topic://'.$_REQUEST['target'], $msgCpu);  
  16.    
  17. $conn->disconnect();           
  18.    
  19. echo 'MQTT Message to ' . $_REQUEST['target'] . ' sent: ' . $_REQUEST['message'];   
  20.    
  21. ?>  

将代码部署到php环境目录里面。输入地址:http://localhost/androidpushservice/

步骤三:下载tokudu兄的android代码:
地址:https://github.com/tokudu/AndroidPushNotificationsDemo
本文提供下载:
tokudu-AndroidPushNotificationsDemo-ea18b09

导入项目,编译,在真机上面使用打开即可。

这里有一个Device Target号码需要在php的界面里面输入。才可以发送成功。

[java]  view plain copy
  1. /* 
  2. * $Id$ 
  3. */  
  4.   
  5. package com.tokudu.demo;  
  6.   
  7. import java.io.BufferedWriter;  
  8. import java.io.File;  
  9. import java.io.FileWriter;  
  10. import java.io.IOException;  
  11. import java.io.Writer;  
  12. import java.text.SimpleDateFormat;  
  13. import java.util.Date;  
  14.   
  15. import android.os.Environment;  
  16.   
  17. public class ConnectionLog  
  18. {  
  19. private String mPath;  
  20. private Writer mWriter;  
  21.   
  22. private static final SimpleDateFormat TIMESTAMP_FMT =  
  23. new SimpleDateFormat("[HH:mm:ss] ");  
  24.   
  25. public ConnectionLog()  
  26. throws IOException  
  27. {  
  28. File sdcard = Environment.getExternalStorageDirectory();  
  29. File logDir = new File(sdcard, "tokudu/log/");  
  30. if (!logDir.exists()) {  
  31. logDir.mkdirs();  
  32. // do not allow media scan  
  33. new File(logDir, ".nomedia").createNewFile();  
  34. }  
  35.   
  36. open(logDir.getAbsolutePath() + "/push.log");  
  37. }  
  38.   
  39. public ConnectionLog(String basePath)  
  40. throws IOException  
  41. {  
  42. open(basePath);  
  43. }  
  44.   
  45. protected void open(String basePath)  
  46. throws IOException  
  47. {  
  48. File f = new File(basePath + "-" + getTodayString());  
  49. mPath = f.getAbsolutePath();  
  50. mWriter = new BufferedWriter(new FileWriter(mPath), 2048);  
  51.   
  52. println("Opened log.");  
  53. }  
  54.   
  55. private static String getTodayString()  
  56. {  
  57. SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd-hhmmss");  
  58. return df.format(new Date());  
  59. }  
  60.   
  61. public String getPath()  
  62. {  
  63. return mPath;  
  64. }  
  65.   
  66. public void println(String message)  
  67. throws IOException  
  68. {  
  69. mWriter.write(TIMESTAMP_FMT.format(new Date()));  
  70. mWriter.write(message);  
  71. mWriter.write('\n');  
  72. mWriter.flush();  
  73. }  
  74.   
  75. public void close()  
  76. throws IOException  
  77. {  
  78. mWriter.close();  
  79. }  
  80. }  
[java]  view plain copy
  1. <span style="font-family:Arial, Helvetica, sans-serif;"><span style="white-space: normal;">  
  2. </span></span>  
[java]  view plain copy
  1. <span style="font-family:Arial, Helvetica, sans-serif;"><span style="white-space: normal;"></span></span><pre name="code" class="java">package com.tokudu.demo;  
  2.   
  3. import java.io.IOException;  
  4.   
  5. import com.ibm.mqtt.IMqttClient;  
  6. import com.ibm.mqtt.MqttClient;  
  7. import com.ibm.mqtt.MqttException;  
  8. import com.ibm.mqtt.MqttPersistence;  
  9. import com.ibm.mqtt.MqttPersistenceException;  
  10. import com.ibm.mqtt.MqttSimpleCallback;  
  11.   
  12. import android.app.AlarmManager;  
  13. import android.app.Notification;  
  14. import android.app.NotificationManager;  
  15. import android.app.PendingIntent;  
  16. import android.app.Service;  
  17. import android.content.BroadcastReceiver;  
  18. import android.content.Context;  
  19. import android.content.Intent;  
  20. import android.content.IntentFilter;  
  21. import android.content.SharedPreferences;  
  22. import android.net.ConnectivityManager;  
  23. import android.net.NetworkInfo;  
  24. import android.os.IBinder;  
  25. import android.util.Log;  
  26.   
  27. /* 
  28. * PushService that does all of the work. 
  29. * Most of the logic is borrowed from KeepAliveService. 
  30. * http://code.google.com/p/android-random/source/browse/trunk/TestKeepAlive/src/org/devtcg/demo/keepalive/KeepAliveService.java?r=219 
  31. */  
  32. public class PushService extends Service  
  33. {  
  34. // this is the log tag  
  35. public static final String TAG = "DemoPushService";  
  36.   
  37. // the IP address, where your MQTT broker is running.  
  38. private static final String MQTT_HOST = "88.88.88.111";  
  39. // the port at which the broker is running.  
  40. private static int MQTT_BROKER_PORT_NUM = 1883;  
  41. // Let's not use the MQTT persistence.  
  42. private static MqttPersistence MQTT_PERSISTENCE = null;  
  43. // We don't need to remember any state between the connections, so we use a clean start.  
  44. private static boolean MQTT_CLEAN_START = true;  
  45. // Let's set the internal keep alive for MQTT to 15 mins. I haven't tested this value much. It could probably be increased.  
  46. private static short MQTT_KEEP_ALIVE = 60 * 15;  
  47. // Set quality of services to 0 (at most once delivery), since we don't want push notifications  
  48. // arrive more than once. However, this means that some messages might get lost (delivery is not guaranteed)  
  49. private static int[] MQTT_QUALITIES_OF_SERVICE = { 0 } ;  
  50. private static int MQTT_QUALITY_OF_SERVICE = 0;  
  51. // The broker should not retain any messages.  
  52. private static boolean MQTT_RETAINED_PUBLISH = false;  
  53.   
  54. // MQTT client ID, which is given the broker. In this example, I also use this for the topic header.  
  55. // You can use this to run push notifications for multiple apps with one MQTT broker.  
  56. public static String MQTT_CLIENT_ID = "tokudu";  
  57.   
  58. // These are the actions for the service (name are descriptive enough)  
  59. private static final String ACTION_START = MQTT_CLIENT_ID + ".START";  
  60. private static final String ACTION_STOP = MQTT_CLIENT_ID + ".STOP";  
  61. private static final String ACTION_KEEPALIVE = MQTT_CLIENT_ID + ".KEEP_ALIVE";  
  62. private static final String ACTION_RECONNECT = MQTT_CLIENT_ID + ".RECONNECT";  
  63.   
  64. // Connection log for the push service. Good for debugging.  
  65. private ConnectionLog mLog;  
  66.   
  67. // Connectivity manager to determining, when the phone loses connection  
  68. private ConnectivityManager mConnMan;  
  69. // Notification manager to displaying arrived push notifications  
  70. private NotificationManager mNotifMan;  
  71.   
  72. // Whether or not the service has been started.  
  73. private boolean mStarted;  
  74.   
  75. // This the application level keep-alive interval, that is used by the AlarmManager  
  76. // to keep the connection active, even when the device goes to sleep.  
  77. private static final long KEEP_ALIVE_INTERVAL = 1000 * 60 * 28;  
  78.   
  79. // Retry intervals, when the connection is lost.  
  80. private static final long INITIAL_RETRY_INTERVAL = 1000 * 10;  
  81. private static final long MAXIMUM_RETRY_INTERVAL = 1000 * 60 * 30;  
  82.   
  83. // Preferences instance  
  84. private SharedPreferences mPrefs;  
  85. // We store in the preferences, whether or not the service has been started  
  86. public static final String PREF_STARTED = "isStarted";  
  87. // We also store the deviceID (target)  
  88. public static final String PREF_DEVICE_ID = "deviceID";  
  89. // We store the last retry interval  
  90. public static final String PREF_RETRY = "retryInterval";  
  91.   
  92. // Notification title  
  93. public static String NOTIF_TITLE = "Tokudu";  
  94. // Notification id  
  95. private static final int NOTIF_CONNECTED = 0;  
  96.   
  97. // This is the instance of an MQTT connection.  
  98. private MQTTConnection mConnection;  
  99. private long mStartTime;  
  100.   
  101. // Static method to start the service  
  102. public static void actionStart(Context ctx) {  
  103. Intent i = new Intent(ctx, PushService.class);  
  104. i.setAction(ACTION_START);  
  105. ctx.startService(i);  
  106. }  
  107.   
  108. // Static method to stop the service  
  109. public static void actionStop(Context ctx) {  
  110. Intent i = new Intent(ctx, PushService.class);  
  111. i.setAction(ACTION_STOP);  
  112. ctx.startService(i);  
  113. }  
  114.   
  115. // Static method to send a keep alive message  
  116. public static void actionPing(Context ctx) {  
  117. Intent i = new Intent(ctx, PushService.class);  
  118. i.setAction(ACTION_KEEPALIVE);  
  119. ctx.startService(i);  
  120. }  
  121.   
  122. @Override  
  123. public void onCreate() {  
  124. super.onCreate();  
  125.   
  126. log("Creating service");  
  127. mStartTime = System.currentTimeMillis();  
  128.   
  129. try {  
  130. mLog = new ConnectionLog();  
  131. Log.i(TAG, "Opened log at " + mLog.getPath());  
  132. catch (IOException e) {  
  133. Log.e(TAG, "Failed to open log", e);  
  134. }  
  135.   
  136. // Get instances of preferences, connectivity manager and notification manager  
  137. mPrefs = getSharedPreferences(TAG, MODE_PRIVATE);  
  138. mConnMan = (ConnectivityManager)getSystemService(CONNECTIVITY_SERVICE);  
  139. mNotifMan = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);  
  140.   
  141. /* If our process was reaped by the system for any reason we need 
  142. * to restore our state with merely a call to onCreate. We record 
  143. * the last "started" value and restore it here if necessary. */  
  144. handleCrashedService();  
  145. }  
  146.   
  147. // This method does any necessary clean-up need in case the server has been destroyed by the system  
  148. // and then restarted  
  149. private void handleCrashedService() {  
  150. if (wasStarted() == true) {  
  151. log("Handling crashed service...");  
  152. // stop the keep alives  
  153. stopKeepAlives();  
  154.   
  155. // Do a clean start  
  156. start();  
  157. }  
  158. }  
  159.   
  160. @Override  
  161. public void onDestroy() {  
  162. log("Service destroyed (started=" + mStarted + ")");  
  163.   
  164. // Stop the services, if it has been started  
  165. if (mStarted == true) {  
  166. stop();  
  167. }  
  168.   
  169. try {  
  170. if (mLog != null)  
  171. mLog.close();  
  172. catch (IOException e) {}  
  173. }  
  174.   
  175. @Override  
  176. public void onStart(Intent intent, int startId) {  
  177. super.onStart(intent, startId);  
  178. log("Service started with intent=" + intent);  
  179.   
  180. // Do an appropriate action based on the intent.  
  181. if (intent.getAction().equals(ACTION_STOP) == true) {  
  182. stop();  
  183. stopSelf();  
  184. else if (intent.getAction().equals(ACTION_START) == true) {  
  185. start();  
  186. else if (intent.getAction().equals(ACTION_KEEPALIVE) == true) {  
  187. keepAlive();  
  188. else if (intent.getAction().equals(ACTION_RECONNECT) == true) {  
  189. if (isNetworkAvailable()) {  
  190. reconnectIfNecessary();  
  191. }  
  192. }  
  193. }  
  194.   
  195. @Override  
  196. public IBinder onBind(Intent intent) {  
  197. return null;  
  198. }  
  199.   
  200. // log helper function  
  201. private void log(String message) {  
  202. log(message, null);  
  203. }  
  204. private void log(String message, Throwable e) {  
  205. if (e != null) {  
  206. Log.e(TAG, message, e);  
  207.   
  208. else {  
  209. Log.i(TAG, message);  
  210. }  
  211.   
  212. if (mLog != null)  
  213. {  
  214. try {  
  215. mLog.println(message);  
  216. catch (IOException ex) {}  
  217. }  
  218. }  
  219.   
  220. // Reads whether or not the service has been started from the preferences  
  221. private boolean wasStarted() {  
  222. return mPrefs.getBoolean(PREF_STARTED, false);  
  223. }  
  224.   
  225. // Sets whether or not the services has been started in the preferences.  
  226. private void setStarted(boolean started) {  
  227. mPrefs.edit().putBoolean(PREF_STARTED, started).commit();  
  228. mStarted = started;  
  229. }  
  230.   
  231. private synchronized void start() {  
  232. log("Starting service...");  
  233.   
  234. // Do nothing, if the service is already running.  
  235. if (mStarted == true) {  
  236. Log.w(TAG, "Attempt to start connection that is already active");  
  237. return;  
  238. }  
  239.   
  240. // Establish an MQTT connection  
  241. connect();  
  242.   
  243. // Register a connectivity listener  
  244. registerReceiver(mConnectivityChanged, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));  
  245. }  
  246.   
  247. private synchronized void stop() {  
  248. // Do nothing, if the service is not running.  
  249. if (mStarted == false) {  
  250. Log.w(TAG, "Attempt to stop connection not active.");  
  251. return;  
  252. }  
  253.   
  254. // Save stopped state in the preferences  
  255. setStarted(false);  
  256.   
  257. // Remove the connectivity receiver  
  258. unregisterReceiver(mConnectivityChanged);  
  259. // Any existing reconnect timers should be removed, since we explicitly stopping the service.  
  260. cancelReconnect();  
  261.   
  262. // Destroy the MQTT connection if there is one  
  263. if (mConnection != null) {  
  264. mConnection.disconnect();  
  265. mConnection = null;  
  266. }  
  267. }  
  268.   
  269. //  
  270. private synchronized void connect() {  
  271. log("Connecting...");  
  272. // fetch the device ID from the preferences.  
  273. String deviceID = mPrefs.getString(PREF_DEVICE_ID, null);  
  274. // Create a new connection only if the device id is not NULL  
  275. if (deviceID == null) {  
  276. log("Device ID not found.");  
  277. else {  
  278. try {  
  279. mConnection = new MQTTConnection(MQTT_HOST, deviceID);  
  280. catch (MqttException e) {  
  281. // Schedule a reconnect, if we failed to connect  
  282. log("MqttException: " + (e.getMessage() != null ? e.getMessage() : "NULL"));  
  283. if (isNetworkAvailable()) {  
  284. scheduleReconnect(mStartTime);  
  285. }  
  286. }  
  287. setStarted(true);  
  288. }  
  289. }  
  290.   
  291. private synchronized void keepAlive() {  
  292. try {  
  293. // Send a keep alive, if there is a connection.  
  294. if (mStarted == true && mConnection != null) {  
  295. mConnection.sendKeepAlive();  
  296. }  
  297. catch (MqttException e) {  
  298. log("MqttException: " + (e.getMessage() != null? e.getMessage(): "NULL"), e);  
  299.   
  300. mConnection.disconnect();  
  301. mConnection = null;  
  302. cancelReconnect();  
  303. }  
  304. }  
  305.   
  306. // Schedule application level keep-alives using the AlarmManager  
  307. private void startKeepAlives() {  
  308. Intent i = new Intent();  
  309. i.setClass(this, PushService.class);  
  310. i.setAction(ACTION_KEEPALIVE);  
  311. PendingIntent pi = PendingIntent.getService(this0, i, 0);  
  312. AlarmManager alarmMgr = (AlarmManager)getSystemService(ALARM_SERVICE);  
  313. alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP,  
  314. System.currentTimeMillis() + KEEP_ALIVE_INTERVAL,  
  315. KEEP_ALIVE_INTERVAL, pi);  
  316. }  
  317.   
  318. // Remove all scheduled keep alives  
  319. private void stopKeepAlives() {  
  320. Intent i = new Intent();  
  321. i.setClass(this, PushService.class);  
  322. i.setAction(ACTION_KEEPALIVE);  
  323. PendingIntent pi = PendingIntent.getService(this0, i, 0);  
  324. AlarmManager alarmMgr = (AlarmManager)getSystemService(ALARM_SERVICE);  
  325. alarmMgr.cancel(pi);  
  326. }  
  327.   
  328. // We schedule a reconnect based on the starttime of the service  
  329. public void scheduleReconnect(long startTime) {  
  330. // the last keep-alive interval  
  331. long interval = mPrefs.getLong(PREF_RETRY, INITIAL_RETRY_INTERVAL);  
  332.   
  333. // Calculate the elapsed time since the start  
  334. long now = System.currentTimeMillis();  
  335. long elapsed = now - startTime;  
  336.   
  337. // Set an appropriate interval based on the elapsed time since start  
  338. if (elapsed < interval) {  
  339. interval = Math.min(interval * 4, MAXIMUM_RETRY_INTERVAL);  
  340. else {  
  341. interval = INITIAL_RETRY_INTERVAL;  
  342. }  
  343.   
  344. log("Rescheduling connection in " + interval + "ms.");  
  345.   
  346. // Save the new internval  
  347. mPrefs.edit().putLong(PREF_RETRY, interval).commit();  
  348.   
  349. // Schedule a reconnect using the alarm manager.  
  350. Intent i = new Intent();  
  351. i.setClass(this, PushService.class);  
  352. i.setAction(ACTION_RECONNECT);  
  353. PendingIntent pi = PendingIntent.getService(this0, i, 0);  
  354. AlarmManager alarmMgr = (AlarmManager)getSystemService(ALARM_SERVICE);  
  355. alarmMgr.set(AlarmManager.RTC_WAKEUP, now + interval, pi);  
  356. }  
  357.   
  358. // Remove the scheduled reconnect  
  359. public void cancelReconnect() {  
  360. Intent i = new Intent();  
  361. i.setClass(this, PushService.class);  
  362. i.setAction(ACTION_RECONNECT);  
  363. PendingIntent pi = PendingIntent.getService(this0, i, 0);  
  364. AlarmManager alarmMgr = (AlarmManager)getSystemService(ALARM_SERVICE);  
  365. alarmMgr.cancel(pi);  
  366. }  
  367.   
  368. private synchronized void reconnectIfNecessary() {  
  369. if (mStarted == true && mConnection == null) {  
  370. log("Reconnecting...");  
  371. connect();  
  372. }  
  373. }  
  374.   
  375. // This receiver listeners for network changes and updates the MQTT connection  
  376. // accordingly  
  377. private BroadcastReceiver mConnectivityChanged = new BroadcastReceiver() {  
  378. @Override  
  379. public void onReceive(Context context, Intent intent) {  
  380. // Get network info  
  381. NetworkInfo info = (NetworkInfo)intent.getParcelableExtra (ConnectivityManager.EXTRA_NETWORK_INFO);  
  382.   
  383. // Is there connectivity?  
  384. boolean hasConnectivity = (info != null && info.isConnected()) ? true : false;  
  385.   
  386. log("Connectivity changed: connected=" + hasConnectivity);  
  387.   
  388. if (hasConnectivity) {  
  389. reconnectIfNecessary();  
  390. else if (mConnection != null) {  
  391. // if there no connectivity, make sure MQTT connection is destroyed  
  392. mConnection.disconnect();  
  393. cancelReconnect();  
  394. mConnection = null;  
  395. }  
  396. }  
  397. };  
  398.   
  399. // Display the topbar notification  
  400. private void showNotification(String text) {  
  401. Notification n = new Notification();  
  402.   
  403. n.flags |= Notification.FLAG_SHOW_LIGHTS;  
  404. n.flags |= Notification.FLAG_AUTO_CANCEL;  
  405.   
  406. n.defaults = Notification.DEFAULT_ALL;  
  407.   
  408. n.icon = com.tokudu.demo.R.drawable.icon;  
  409. n.when = System.currentTimeMillis();  
  410.   
  411. // Simply open the parent activity  
  412. PendingIntent pi = PendingIntent.getActivity(this0,  
  413. new Intent(this, PushActivity.class), 0);  
  414.   
  415. // Change the name of the notification here  
  416. n.setLatestEventInfo(this, NOTIF_TITLE, text, pi);  
  417.   
  418. mNotifMan.notify(NOTIF_CONNECTED, n);  
  419. }  
  420.   
  421. // Check if we are online  
  422. private boolean isNetworkAvailable() {  
  423. NetworkInfo info = mConnMan.getActiveNetworkInfo();  
  424. if (info == null) {  
  425. return false;  
  426. }  
  427. return info.isConnected();  
  428. }  
  429.   
  430. // This inner class is a wrapper on top of MQTT client.  
  431. private class MQTTConnection implements MqttSimpleCallback {  
  432. IMqttClient mqttClient = null;  
  433.   
  434. // Creates a new connection given the broker address and initial topic  
  435. public MQTTConnection(String brokerHostName, String initTopic) throws MqttException {  
  436. // Create connection spec  
  437. String mqttConnSpec = "tcp://" + brokerHostName + "@" + MQTT_BROKER_PORT_NUM;  
  438. // Create the client and connect  
  439. mqttClient = MqttClient.createMqttClient(mqttConnSpec, MQTT_PERSISTENCE);  
  440. String clientID = MQTT_CLIENT_ID + "/" + mPrefs.getString(PREF_DEVICE_ID, "");  
  441. mqttClient.connect(clientID, MQTT_CLEAN_START, MQTT_KEEP_ALIVE);  
  442.   
  443. // register this client app has being able to receive messages  
  444. mqttClient.registerSimpleHandler(this);  
  445.   
  446. // Subscribe to an initial topic, which is combination of client ID and device ID.  
  447. initTopic = MQTT_CLIENT_ID + "/" + initTopic;  
  448. subscribeToTopic(initTopic);  
  449.   
  450. log("Connection established to " + brokerHostName + " on topic " + initTopic);  
  451.   
  452. // Save start time  
  453. mStartTime = System.currentTimeMillis();  
  454. // Star the keep-alives  
  455. startKeepAlives();  
  456. }  
  457.   
  458. // Disconnect  
  459. public void disconnect() {  
  460. try {  
  461. stopKeepAlives();  
  462. mqttClient.disconnect();  
  463. catch (MqttPersistenceException e) {  
  464. log("MqttException" + (e.getMessage() != null? e.getMessage():" NULL"), e);  
  465. }  
  466. }  
  467. /* 
  468. * Send a request to the message broker to be sent messages published with 
  469. * the specified topic name. Wildcards are allowed. 
  470. */  
  471. private void subscribeToTopic(String topicName) throws MqttException {  
  472.   
  473. if ((mqttClient == null) || (mqttClient.isConnected() == false)) {  
  474. // quick sanity check - don't try and subscribe if we don't have  
  475. // a connection  
  476. log("Connection error" + "No connection");  
  477. else {  
  478. String[] topics = { topicName };  
  479. mqttClient.subscribe(topics, MQTT_QUALITIES_OF_SERVICE);  
  480. }  
  481. }  
  482. /* 
  483. * Sends a message to the message broker, requesting that it be published 
  484. * to the specified topic. 
  485. */  
  486. private void publishToTopic(String topicName, String message) throws MqttException {  
  487. if ((mqttClient == null) || (mqttClient.isConnected() == false)) {  
  488. // quick sanity check - don't try and publish if we don't have  
  489. // a connection  
  490. log("No connection to public to");  
  491. else {  
  492. mqttClient.publish(topicName,  
  493. message.getBytes(),  
  494. MQTT_QUALITY_OF_SERVICE,  
  495. MQTT_RETAINED_PUBLISH);  
  496. }  
  497. }  
  498.   
  499. /* 
  500. * Called if the application loses it's connection to the message broker. 
  501. */  
  502. public void connectionLost() throws Exception {  
  503. log("Loss of connection" + "connection downed");  
  504. stopKeepAlives();  
  505. // null itself  
  506. mConnection = null;  
  507. if (isNetworkAvailable() == true) {  
  508. reconnectIfNecessary();  
  509. }  
  510. }  
  511.   
  512. /* 
  513. * Called when we receive a message from the message broker. 
  514. */  
  515. public void publishArrived(String topicName, byte[] payload, int qos, boolean retained) {  
  516. // Show a notification  
  517. String s = new String(payload);  
  518. showNotification(s);  
  519. log("Got message: " + s);  
  520. }  
  521.   
  522. public void sendKeepAlive() throws MqttException {  
  523. log("Sending keep alive");  
  524. // publish to a keep-alive topic  
  525. publishToTopic(MQTT_CLIENT_ID + "/keepalive", mPrefs.getString(PREF_DEVICE_ID, ""));  
  526. }  
  527. }  
  528. }</pre><br>  
  529. <br>  
  530. <span style="color:#ff0000"><strong>7 代码下载</strong></span>  
  531. <pre></pre>  
  532. <pre name="code" class="java"><span style="color:#ff0000"><strong><a href="http://doandroid.info/wp-content/uploads/2012/03/tokudu-AndroidPushNotificationsDemo-ea18b09.zip">tokudu-AndroidPushNotifications</a></strong></span></pre><pre name="code" class="java"><span style="color:#ff0000"><strong><a href="http://doandroid.info/wp-content/uploads/2012/03/tokudu-AndroidPushNotificationsDemo-ea18b09.zip">Demo-ea18b09</a><a href="http://doandroid.info/wp-content/uploads/2012/03/androidpushservice.rar">androidpushservice</a></strong></span></pre><pre name="code" class="java"><span style="color:#ff0000"><strong>MQTT实现:http://mosquitto.org/download/</strong></span></pre><pre name="code" class="java"><span style="color:#ff0000"><strong>8 尾声我们目前已经完成了整个功能,如果要商业使用,还有一些问题摆在我们面前,如连接数的问题,大并发的问题,Apikey的问题等等。我们也会在tokudu兄的基础上,尝试完善一下,在这里对tokudu兄致以深深的敬意。</strong></span></pre><pre name="code" class="java"><span style="color:#ff0000"><strong>参考文章:</strong></span></pre><pre name="code" class="java"><span style="color:#ff0000"><strong>1 http://tokudu.com/2010/how-to-implement-push-notifications-for-android/【主参考】</strong></span></pre><pre name="code" class="java"><span style="color:#ff0000"><strong>2 http://sourceforge.net/projects/androidpn/</strong></span></pre><pre name="code" class="java"><span style="color:#ff0000"><strong>3 http://blog.mediarain.com/2011/03/simple-google-android-c2dm-tutorial-push-notifications-for-android/</strong></span></pre><pre name="code" class="java"><span style="color:#ff0000"><strong>4 http://mosquitto.org/download/</strong></span></pre><pre name="code" class="java"><span style="color:#ff0000"><strong>5 http://hi.baidu.com/zhu410289616/blog/item/b697f5328b0c405fad4b5f83.html</strong></span></pre><pre name="code" class="java"><span style="color:#ff0000"><strong>6 http://blog.youkuaiyun.com/joshua_yu/article/details/6563587</strong></span></pre><pre name="code" class="java"><span style="color:#ff0000"><strong>7 http://blog.mediarain.com/2011/03/simple-google-android-c2dm-tutorial-push-notifications-for-android/</strong></span></pre><pre name="code" class="java"><span style="color:#ff0000"><strong>8 http://www.codeproject.com/Articles/339162/Android-push-notification-implementation-using-ASP</strong></span></pre><pre name="code" class="java"><span style="color:#ff0000"><strong>9 http://www.eoeandroid.com/thread-97603-1-1.html</strong></span></pre><pre name="code" class="java"><span style="color:#ff0000"><strong>10 https://www14.software.ibm.com/webapp/iwm/web/reg/acceptLogin.do?source=AW-0U9&lang=en_US</strong></span></pre><pre name="code" class="java"><span style="color:#ff0000"><strong>11 http://mosquitto.org/</strong></span></pre><pre name="code" class="java"><span style="color:#ff0000"><strong>12 https://github.com/tokudu/AndroidPushNotificationsDemo</strong></span></pre><pre name="code" class="java"><span style="color:#ff0000"><strong>13 http://code.google.com/intl/zh-CN/android/c2dm/</strong></span></pre><pre name="code" class="java"><span style="color:#ff0000"><strong>14 http://stackoverflow.com/questions/1378671/push-notifications-in-android-platform</strong></span></pre><pre name="code" class="java"><span style="color:#ff0000"><strong>15 http://dalelane.co.uk/blog/?p=938</strong></span></pre><pre name="code" class="java"><span style="color:#ff0000"><strong>16 http://mqtt.org/</strong></span></pre><pre name="code" class="java"><span style="color:#ff0000"><strong>17 http://stackoverflow.com/questions/1243066/does-android-support-near-real-time-push-notification</strong></span></pre><pre name="code" class="java"><span style="color:#ff0000"><strong>18 http://www.chengyunfeng.com/2010/09/android-push-notification</strong></span></pre><pre name="code" class="java"><span style="color:#ff0000"><strong>19 http://www.airpush.com/</strong></span></pre><pre name="code" class="java"><span style="color:#ff0000"><strong>-END-</strong></span></pre><pre name="code" class="java"><span style="color:#ff0000"><strong>  
  533. </strong></span></pre><pre name="code" class="java"><span style="color:#ff0000"><strong>本文同发地址:http://doandroid.info/2012/03/03/android-push-service/</strong></span><p></p><p></p><pre></pre>  
  534. <pre></pre>  
  535.   
  536. </pre>  

原文:http://blog.youkuaiyun.com/huzhangyou/article/details/7314988
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值