写在最前:Android实现推送是比较困难的,我这里虽然实现了推送,但是当服务器需要发送的对象一多,就会发生延迟或干脆Down机。很多软件看似实现了推送,其实不然,它们只不过是有一个Service一直在监听网络状态,当手机联网了就主动向服务器请求了数据,给人的感觉像“推送”了一样。这是障眼法,老板们哪管这些,还以为真的推送,非得实现它。殊不知,真正的推送跟服务器的数量有关!再细想一下,如果这么容易就能实现推送,那早就垃圾信息满天飞了,还要发垃圾短信干什么!至少,发垃圾短信还要收费,可是推送就不需要花钱。当别人都是傻子,用免费的不用还用收费的?所以,一味的追求“推送”不可取!
看了以上这些话,你是不是觉得不想再看下去了?这很正常!权当参考罢了.


请先参考:Android推送通知指南

这里使用了IBM提供的MQTT协议实现了推送。有一个wmqtt.jar包需要导入到工程,见附件。

然后编写PushService类实现一个服务,其中有个内部类:
MQTTConnection 实现了 MqttSimpleCallback接口,重写其中的publishArrived方法,我这里是当接受到推送的数据后显示一个Notification,点击该Notification后跳转到一个Activity上。
具体看PushService类,关键的地方我都用中文字说明了:
package com.ata.push;import org.json.JSONException;import org.json.JSONObject;import android.R;import android.app.AlarmManager;import android.app.Notification;import android.app.NotificationManager;import android.app.PendingIntent;import android.app.Service;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.content.IntentFilter;import android.content.SharedPreferences;import android.net.ConnectivityManager;import android.net.NetworkInfo;import android.os.IBinder;import android.util.Log;import com.ata.view.NewMesageInfoActivity;import com.ibm.mqtt.IMqttClient;import com.ibm.mqtt.MqttClient;import com.ibm.mqtt.MqttException;import com.ibm.mqtt.MqttPersistence;import com.ibm.mqtt.MqttPersistenceException;import com.ibm.mqtt.MqttSimpleCallback;/*  * PushService that does all of the work. * Most of the logic is borrowed from KeepAliveService. * http://code.google.com/p/android-random/source/browse/trunk/TestKeepAlive/src/org/devtcg/demo/keepalive/KeepAliveService.java?r=219 */public class PushService extends Service{// this is the log tagpublic static final StringTAG = "PushService";// the IP address, where your MQTT broker is running.private static final StringMQTT_HOST = "172.16.26.41";//需要改成服务器IP// the port at which the broker is running. private static intMQTT_BROKER_PORT_NUM      = 1883;//需要改成服务器port// Let's not use the MQTT persistence.private static MqttPersistenceMQTT_PERSISTENCE          = null;// We don't need to remember any state between the connections, so we use a clean start. private static booleanMQTT_CLEAN_START          = true;// Let's set the internal keep alive for MQTT to 15 mins. I haven't tested this value much. It could probably be increased.private static shortMQTT_KEEP_ALIVE           = 60 * 15;// Set quality of services to 0 (at most once delivery), since we don't want push notifications // arrive more than once. However, this means that some messages might get lost (delivery is not guaranteed)private static int[]MQTT_QUALITIES_OF_SERVICE = { 0 } ;private static intMQTT_QUALITY_OF_SERVICE   = 0;// The broker should not retain any messages.private static booleanMQTT_RETAINED_PUBLISH     = false;// MQTT client ID, which is given the broker. In this example, I also use this for the topic header. // You can use this to run push notifications for multiple apps with one MQTT broker. public static StringMQTT_CLIENT_ID = "ata";//需要改成自己需要的名称// These are the actions for the service (name are descriptive enough)private static final StringACTION_START = MQTT_CLIENT_ID + ".START";private static final StringACTION_STOP = MQTT_CLIENT_ID + ".STOP";private static final StringACTION_KEEPALIVE = MQTT_CLIENT_ID + ".KEEP_ALIVE";private static final StringACTION_RECONNECT = MQTT_CLIENT_ID + ".RECONNECT";// Connection log for the push service. Good for debugging.//private ConnectionLog mLog;// Connectivity manager to determining, when the phone loses connectionprivate ConnectivityManagermConnMan;// Notification manager to displaying arrived push notifications private NotificationManagermNotifMan;// Whether or not the service has been started.private boolean mStarted;// This the application level keep-alive interval, that is used by the AlarmManager// to keep the connection active, even when the device goes to sleep.private static final longKEEP_ALIVE_INTERVAL = 1000 * 60 * 28;// Retry intervals, when the connection is lost.private static final longINITIAL_RETRY_INTERVAL = 1000 * 10;private static final longMAXIMUM_RETRY_INTERVAL = 1000 * 60 * 30;// Preferences instance private SharedPreferences mPrefs;// We store in the preferences, whether or not the service has been started//判断Service是否已经启动,不要重复启动!public static final StringPREF_STARTED = "isStarted";// We also store the deviceID (target)//需要提供手机设备号,该设备号应该事先保存在SharedPreferences中public static final StringPREF_DEVICE_ID = "deviceID";// We store the last retry intervalpublic static final StringPREF_RETRY = "retryInterval";// Notification titlepublic static StringNOTIF_TITLE = "ata"; //需要改成自己需要的title  // Notification idprivate static final intNOTIF_CONNECTED = 0;// This is the instance of an MQTT connection.private MQTTConnectionmConnection;private longmStartTime;// Static method to start the service//在需要的地方直接调用该方法就启动监听了public static void actionStart(Context ctx) {Intent i = new Intent(ctx, PushService.class);i.setAction(ACTION_START);ctx.startService(i);}// Static method to stop the servicepublic static void actionStop(Context ctx) {Intent i = new Intent(ctx, PushService.class);i.setAction(ACTION_STOP);ctx.startService(i);}// Static method to send a keep alive messagepublic static void actionPing(Context ctx) {Intent i = new Intent(ctx, PushService.class);i.setAction(ACTION_KEEPALIVE);ctx.startService(i);}@Overridepublic void onCreate() {super.onCreate();log("Creating service");mStartTime = System.currentTimeMillis();/*try {//mLog = new ConnectionLog();//Log.i(TAG, "Opened log at " + mLog.getPath());} catch (IOException e) {Log.e(TAG, "Failed to open log", e);}*/// Get instances of preferences, connectivity manager and notification managermPrefs = getSharedPreferences(TAG, MODE_PRIVATE);mConnMan = (ConnectivityManager)getSystemService(CONNECTIVITY_SERVICE);mNotifMan = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);/* If our process was reaped by the system for any reason we need * to restore our state with merely a call to onCreate.  We record * the last "started" value and restore it here if necessary. */handleCrashedService();}// This method does any necessary clean-up need in case the server has been destroyed by the system// and then restartedprivate void handleCrashedService() {if (wasStarted() == true) {log("Handling crashed service..."); // stop the keep alivesstopKeepAlives(); // Do a clean startstart();}}@Overridepublic void onDestroy() {log("Service destroyed (started=" + mStarted + ")");// Stop the services, if it has been startedif (mStarted == true) {stop();}/*try {if (mLog != null)mLog.close();} catch (IOException e) {}*/}@Overridepublic void onStart(Intent intent, int startId) {super.onStart(intent, startId);log("Service started with intent=" + intent);// Do an appropriate action based on the intent.if (intent.getAction().equals(ACTION_STOP) == true) {stop();stopSelf();} else if (intent.getAction().equals(ACTION_START) == true) {start();} else if (intent.getAction().equals(ACTION_KEEPALIVE) == true) {keepAlive();} else if (intent.getAction().equals(ACTION_RECONNECT) == true) {if (isNetworkAvailable()) {reconnectIfNecessary();}}}@Overridepublic IBinder onBind(Intent intent) {return null;}// log helper functionprivate void log(String message) {log(message, null);}private void log(String message, Throwable e) {if (e != null) {Log.e(TAG, message, e);} else {Log.i(TAG, message);}/*if (mLog != null){try {mLog.println(message);} catch (IOException ex) {}}*/}// Reads whether or not the service has been started from the preferencesprivate boolean wasStarted() {return mPrefs.getBoolean(PREF_STARTED, false);}// Sets whether or not the services has been started in the preferences.private void setStarted(boolean started) {mPrefs.edit().putBoolean(PREF_STARTED, started).commit();mStarted = started;}private synchronized void start() {log("Starting service...");// Do nothing, if the service is already running.if (mStarted == true) {Log.w(TAG, "Attempt to start connection that is already active");return;}// Establish an MQTT connectionconnect();// Register a connectivity listenerregisterReceiver(mConnectivityChanged, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));}private synchronized void stop() {// Do nothing, if the service is not running.if (mStarted == false) {Log.w(TAG, "Attempt to stop connection not active.");return;}// Save stopped state in the preferencessetStarted(false);// Remove the connectivity receiverunregisterReceiver(mConnectivityChanged);// Any existing reconnect timers should be removed, since we explicitly stopping the service.cancelReconnect();// Destroy the MQTT connection if there is oneif (mConnection != null) {mConnection.disconnect();mConnection = null;}}// private synchronized void connect() {log("Connecting...");// fetch the device ID from the preferences.String deviceID = mPrefs.getString(PREF_DEVICE_ID, null);// Create a new connection only if the device id is not NULLif (deviceID == null) {log("Device ID not found.");} else {try {mConnection = new MQTTConnection(MQTT_HOST, deviceID);} catch (MqttException e) {// Schedule a reconnect, if we failed to connectlog("MqttException: " + (e.getMessage() != null ? e.getMessage() : "NULL"));        if (isNetworkAvailable()) {        scheduleReconnect(mStartTime);        }}setStarted(true);}}private synchronized void keepAlive() {try {// Send a keep alive, if there is a connection.if (mStarted == true && mConnection != null) {mConnection.sendKeepAlive();}} catch (MqttException e) {log("MqttException: " + (e.getMessage() != null? e.getMessage(): "NULL"), e);mConnection.disconnect();mConnection = null;cancelReconnect();}}// Schedule application level keep-alives using the AlarmManagerprivate void startKeepAlives() {Intent i = new Intent();i.setClass(this, PushService.class);i.setAction(ACTION_KEEPALIVE);PendingIntent pi = PendingIntent.getService(this, 0, i, 0);AlarmManager alarmMgr = (AlarmManager)getSystemService(ALARM_SERVICE);alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP,  System.currentTimeMillis() + KEEP_ALIVE_INTERVAL,  KEEP_ALIVE_INTERVAL, pi);}// Remove all scheduled keep alivesprivate void stopKeepAlives() {Intent i = new Intent();i.setClass(this, PushService.class);i.setAction(ACTION_KEEPALIVE);PendingIntent pi = PendingIntent.getService(this, 0, i, 0);AlarmManager alarmMgr = (AlarmManager)getSystemService(ALARM_SERVICE);alarmMgr.cancel(pi);}// We schedule a reconnect based on the starttime of the servicepublic void scheduleReconnect(long startTime) {// the last keep-alive intervallong interval = mPrefs.getLong(PREF_RETRY, INITIAL_RETRY_INTERVAL);// Calculate the elapsed time since the startlong now = System.currentTimeMillis();long elapsed = now - startTime;// Set an appropriate interval based on the elapsed time since start if (elapsed < interval) {interval = Math.min(interval * 4, MAXIMUM_RETRY_INTERVAL);} else {interval = INITIAL_RETRY_INTERVAL;}log("Rescheduling connection in " + interval + "ms.");// Save the new internvalmPrefs.edit().putLong(PREF_RETRY, interval).commit();// Schedule a reconnect using the alarm manager.Intent i = new Intent();i.setClass(this, PushService.class);i.setAction(ACTION_RECONNECT);PendingIntent pi = PendingIntent.getService(this, 0, i, 0);AlarmManager alarmMgr = (AlarmManager)getSystemService(ALARM_SERVICE);alarmMgr.set(AlarmManager.RTC_WAKEUP, now + interval, pi);}// Remove the scheduled reconnectpublic void cancelReconnect() {Intent i = new Intent();i.setClass(this, PushService.class);i.setAction(ACTION_RECONNECT);PendingIntent pi = PendingIntent.getService(this, 0, i, 0);AlarmManager alarmMgr = (AlarmManager)getSystemService(ALARM_SERVICE);alarmMgr.cancel(pi);}private synchronized void reconnectIfNecessary() {if (mStarted == true && mConnection == null) {log("Reconnecting...");connect();}}// This receiver listeners for network changes and updates the MQTT connection// accordinglyprivate BroadcastReceiver mConnectivityChanged = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {// Get network infoNetworkInfo info = (NetworkInfo)intent.getParcelableExtra (ConnectivityManager.EXTRA_NETWORK_INFO);// Is there connectivity?boolean hasConnectivity = (info != null && info.isConnected()) ? true : false;log("Connectivity changed: connected=" + hasConnectivity);if (hasConnectivity) {reconnectIfNecessary();} else if (mConnection != null) {// if there no connectivity, make sure MQTT connection is destroyedmConnection.disconnect();cancelReconnect();mConnection = null;}}};// Display the topbar notificationprivate void showNotification(String content) {Notification n = new Notification();n.flags |= Notification.FLAG_SHOW_LIGHTS;      n.flags |= Notification.FLAG_AUTO_CANCEL;        n.defaults = Notification.DEFAULT_ALL;      n.icon = R.drawable.ic_dialog_info;n.when = System.currentTimeMillis();Log.i("PushService", "json==="+content);String alert=null;String id=null;try {JSONObject json = new JSONObject(content);alert = json.optString("alert");id = json.optString("id");} catch (JSONException e) {// TODO Auto-generated catch blocke.printStackTrace();}Intent intent = new Intent(this,NewMesageInfoActivity.class);intent.putExtra("url", "http://testing.portal.ataudc.com/message/"+id);intent.putExtra("id", id);PendingIntent pi = PendingIntent.getActivity(this, 0,intent, PendingIntent.FLAG_ONE_SHOT);// Change the name of the notification heren.setLatestEventInfo(this, NOTIF_TITLE, alert, pi);mNotifMan.notify(NOTIF_CONNECTED, n);}// Check if we are onlineprivate boolean isNetworkAvailable() {NetworkInfo info = mConnMan.getActiveNetworkInfo();if (info == null) {return false;}return info.isConnected();}// This inner class is a wrapper on top of MQTT client.private class MQTTConnection implements MqttSimpleCallback {IMqttClient mqttClient = null;// Creates a new connection given the broker address and initial topicpublic MQTTConnection(String brokerHostName, String initTopic) throws MqttException {// Create connection spec    String mqttConnSpec = "tcp://" + brokerHostName + "@" + MQTT_BROKER_PORT_NUM;        // Create the client and connect        mqttClient = MqttClient.createMqttClient(mqttConnSpec, MQTT_PERSISTENCE);        String clientID = MQTT_CLIENT_ID + "/" + mPrefs.getString(PREF_DEVICE_ID, "");        mqttClient.connect(clientID, MQTT_CLEAN_START, MQTT_KEEP_ALIVE);        // register this client app has being able to receive messagesmqttClient.registerSimpleHandler(this);// Subscribe to an initial topic, which is combination of client ID and device ID.initTopic = MQTT_CLIENT_ID + "/" + initTopic;subscribeToTopic(initTopic);log("Connection established to " + brokerHostName + " on topic " + initTopic);// Save start timemStartTime = System.currentTimeMillis();// Star the keep-alivesstartKeepAlives();        }// Disconnectpublic void disconnect() {try {stopKeepAlives();mqttClient.disconnect();} catch (MqttPersistenceException e) {log("MqttException" + (e.getMessage() != null? e.getMessage():" NULL"), e);}}/* * Send a request to the message broker to be sent messages published with  *  the specified topic name. Wildcards are allowed. */private void subscribeToTopic(String topicName) throws MqttException {if ((mqttClient == null) || (mqttClient.isConnected() == false)) {// quick sanity check - don't try and subscribe if we don't have//  a connectionlog("Connection error" + "No connection");} else {String[] topics = { topicName };mqttClient.subscribe(topics, MQTT_QUALITIES_OF_SERVICE);}}/* * Sends a message to the message broker, requesting that it be published *  to the specified topic. */private void publishToTopic(String topicName, String message) throws MqttException {if ((mqttClient == null) || (mqttClient.isConnected() == false)) {// quick sanity check - don't try and publish if we don't have//  a connectionlog("No connection to public to");} else {mqttClient.publish(topicName,    message.getBytes(),   MQTT_QUALITY_OF_SERVICE,    MQTT_RETAINED_PUBLISH);}}/* * Called if the application loses it's connection to the message broker. */public void connectionLost() throws Exception {log("Loss of connection" + "connection downed");stopKeepAlives();// null itselfmConnection = null;if (isNetworkAvailable() == true) {reconnectIfNecessary();}}/* * Called when we receive a message from the message broker.  * 在这里处理服务器推送过来的数据 */public void publishArrived(String topicName, byte[] payload, int qos, boolean retained) {// Show a notificationString s = new String(payload);showNotification(s);}   public void sendKeepAlive() throws MqttException {log("Sending keep alive");// publish to a keep-alive topicpublishToTopic(MQTT_CLIENT_ID + "/keepalive", mPrefs.getString(PREF_DEVICE_ID, ""));}}}


PushService是根据你的设备号来准确定位你的手机的,它是从SharedPreferences取得该设备号的,所以你得事先将你的设备号保存在SharedPreferences中,如下:
class PushTask extends AsyncTask<Void, Integer, Boolean> {@Overrideprotected Boolean doInBackground(Void... params) {// 启动PUSH服务Editor editor = getSharedPreferences(PushService.TAG, MODE_PRIVATE).edit();String deviceID = Secure.getString(getContentResolver(),Secure.ANDROID_ID);editor.putString(PushService.PREF_DEVICE_ID, deviceID);editor.commit();try {PushService.actionStart(getApplicationContext());} catch (Exception e) {// TODO: handle exception//Log.i(tag, "启动PUSH服务失败.");return false;}return true;}@Overrideprotected void onCancelled() {super.onCancelled();}@Overrideprotected void onPostExecute(Boolean result) {Log.i(tag, result?"启动PUSH服务成功.":"启动PUSH服务失败.");}@Overrideprotected void onPreExecute() {// 预处理}@Overrideprotected void onProgressUpdate(Integer... values) {// 更新进度}}

PushTask task=new PushTask();task.execute();

如果要关闭服务,只需要将下面的代码添加到想要的地方:
PushService.actionStop(getApplicationContext());

至于服务器端代码,我们使用php写的,这里请参考Android推送通知指南

另一篇关于推送:
Android Push Notification实现信息推送使用
http://www.cnblogs.com/hanyonglu/archive/2012/03/16/2399655.html
http://www.apkbus.com/android-48367-1-1.html

更多相关文章

  1. Android(安卓)启动服务配合AsyncTask 使用OKHttp 实现断点下载大
  2. 关于APP接收开机广播延迟问题,解决开机启动慢问题
  3. 巧用apache httpcore-nio实现android推送服务
  4. 服务器端向Android客户端的推送
  5. 【Android(安卓)异常与性能优化】冷启动优化
  6. android MultiDex multidex原理原理下遇见的N个深坑(二)
  7. Android(安卓)C2DM学习——云端推送
  8. 如何在首次启动时跳过Google安装向导
  9. Android通过OkHttp框架上传照片到服务器

随机推荐

  1. Android(安卓)安全测试书单
  2. Android加密之文件级加密
  3. Android图像篇
  4. You are attempting to build with the i
  5. 各种类型Android(安卓)Market了解
  6. Android(安卓)Button应用法则
  7. Android(安卓)AIDE入门——Hello,world,
  8. Android(安卓)for opencv(1)android使用ope
  9. Android命令Monkey压力测试,详解
  10. Android(安卓)面试题总结之Android(安卓)