android + PHP 实现消息推送(采用MQTT协议)

PushService.java

package com.carcare.service;



import java.io.IOException;



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.carcare.updataversion.cjh.CarCareActivity;

import com.carcare.updataversion.cjh.R;

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 tag

	public static final String		TAG = "Carcare";



	// the IP address, where your MQTT broker is running.

	private static final String		MQTT_HOST = 

		"202.165.183.74";

//		"42.120.41.127";

		//"192.168.1.103";//

	// the port at which the broker is running. 

	private static int				MQTT_BROKER_PORT_NUM      = 1883;

	// Let's not use the MQTT persistence.

	private static MqttPersistence	MQTT_PERSISTENCE          = null;

	// We don't need to remember any state between the connections, so we use a clean start. 

	private static boolean			MQTT_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 short			MQTT_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 int				MQTT_QUALITY_OF_SERVICE   = 0;

	// The broker should not retain any messages.

	private static boolean			MQTT_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 String			MQTT_CLIENT_ID = "cjh";



	// These are the actions for the service (name are descriptive enough)

	private static String		ACTION_START = MQTT_CLIENT_ID + ".START";

	private static String		ACTION_STOP = MQTT_CLIENT_ID + ".STOP";

	private static String		ACTION_KEEPALIVE = MQTT_CLIENT_ID + ".KEEP_ALIVE";

	private static String		ACTION_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 connection

	private ConnectivityManager		mConnMan;

	// Notification manager to displaying arrived push notifications 

	private NotificationManager		mNotifMan;



	// 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 long		KEEP_ALIVE_INTERVAL = 1000 * 60 * 28;



	// Retry intervals, when the connection is lost.

	private static final long		INITIAL_RETRY_INTERVAL = 1000 * 10;

	private static final long		MAXIMUM_RETRY_INTERVAL = 1000 * 60 * 30;



	// Preferences instance 

	private SharedPreferences 		mPrefs;

	// We store in the preferences, whether or not the service has been started

	public static final String		PREF_STARTED = "isStarted";

	// We also store the deviceID (target)

	public static final String		PREF_DEVICE_ID = "deviceID";

	// We store the last retry interval

	public static final String		PREF_RETRY = "retryInterval";



	// Notification title

	public static String			NOTIF_TITLE ; 	

	// Notification id

	private static final int		NOTIF_CONNECTED = 0;	

		

	// This is the instance of an MQTT connection.

	private MQTTConnection			mConnection;

	private long					mStartTime;

	

	public static void setMqttClientId(String id){

		MQTT_CLIENT_ID = id;



		// These are the actions for the service (name are descriptive enough)

		ACTION_START = MQTT_CLIENT_ID + ".START";

		ACTION_STOP = MQTT_CLIENT_ID + ".STOP";

		ACTION_KEEPALIVE = MQTT_CLIENT_ID + ".KEEP_ALIVE";

		ACTION_RECONNECT = MQTT_CLIENT_ID + ".RECONNECT";

	}

	// Static method to start the service

	public static void actionStart(Context ctx) {

		NOTIF_TITLE = ctx.getResources().getString(R.string.app_name);

		Intent i = new Intent(ctx, PushService.class);

		i.setAction(ACTION_START);

		ctx.startService(i);

	}



	// Static method to stop the service

	public 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 message

	public static void actionPing(Context ctx) {

		Intent i = new Intent(ctx, PushService.class);

		i.setAction(ACTION_KEEPALIVE);

		ctx.startService(i);

	}



	@Override

	public 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 manager

		mPrefs = 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 restarted

	private void handleCrashedService() {

		if (wasStarted() == true) {

			log("Handling crashed service...");

			 // stop the keep alives

			stopKeepAlives(); 

				

			// Do a clean start

			start();

		}

	}

	

	@Override

	public void onDestroy() {

		log("Service destroyed (started=" + mStarted + ")");



		// Stop the services, if it has been started

		if (mStarted == true) {

			stop();

		}

		

		try {

			if (mLog != null)

				mLog.close();

		} catch (IOException e) {}		

	}

	

	@Override

	public 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==null){

			return ;

		}else{

			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();

				}

			}

		}

		

	}

	

	@Override

	public IBinder onBind(Intent intent) {

		return null;

	}



	// log helper function

	private 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 preferences

	private 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 connection

		connect();

		

		// Register a connectivity listener

		registerReceiver(mConnectivityChanged, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));		

	}



	private synchronized void stop() {

		// Do nothing, if the service is not running.

		log("stop service");

		if (mStarted == false) {

			Log.w(TAG, "Attempt to stop connection not active.");

			return;

		}



		// Save stopped state in the preferences

		setStarted(false);



		// Remove the connectivity receiver

		unregisterReceiver(mConnectivityChanged);

		// Any existing reconnect timers should be removed, since we explicitly stopping the service.

		cancelReconnect();



		// Destroy the MQTT connection if there is one

		if (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 NULL

		if (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 connect

				log("MqttException: " + (e.getMessage() != null ? e.getMessage() : "NULL"));

	        	if (isNetworkAvailable()) {

	        		scheduleReconnect(mStartTime);

	        	}

			}

			setStarted(true);

		}

	}



	private synchronized void keepAlive() {

		log("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 AlarmManager

	private void startKeepAlives() {

		//log("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 alives

	private void stopKeepAlives() {

		//log("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 service

	public void scheduleReconnect(long startTime) {

		// the last keep-alive interval

		long interval = mPrefs.getLong(PREF_RETRY, INITIAL_RETRY_INTERVAL);



		// Calculate the elapsed time since the start

		long 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 internval

		mPrefs.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 reconnect

	public 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

	// accordingly

	private BroadcastReceiver mConnectivityChanged = new BroadcastReceiver() {

		@Override

		public void onReceive(Context context, Intent intent) {

			// Get network info

			//log(mConnection));

			NetworkInfo 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 destroyed

				mConnection.disconnect();

				cancelReconnect();

				mConnection = null;

			}

		}

	};

	

	// Display the topbar notification

	private void showNotification(String text) {

		log(text);

		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_launcher;

		n.when = System.currentTimeMillis();



		// Simply open the parent activity

			

			PendingIntent pi = PendingIntent.getActivity(this, 0,

					  new Intent(this, CarCareActivity.class), 0);

			n.setLatestEventInfo(this, NOTIF_TITLE, text, pi);

		



		// Change the name of the notification here

		



		mNotifMan.notify(NOTIF_CONNECTED, n);

	}

	

	// Check if we are online

	private 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 topic

		public 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 messages

				mqttClient.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 time

				mStartTime = System.currentTimeMillis();

				// Star the keep-alives

				startKeepAlives();				        

		}

		

		// Disconnect

		public 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 connection

				log("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 connection				

				log("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 itself

			mConnection = 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 notification

			String s = new String(payload);

			showNotification(s);	

			log("Got message: " + s);

		}   

		

		public void sendKeepAlive() throws MqttException {

			log("Sending keep alive");

			// publish to a keep-alive topic

			publishToTopic(MQTT_CLIENT_ID + "/keepalive", mPrefs.getString(PREF_DEVICE_ID, ""));

		}		

	}

}

 BootReceiver.java

package com.carcare.service;





import android.content.BroadcastReceiver;

import android.content.Context;

import android.content.Intent;

import android.content.SharedPreferences.Editor;

import android.provider.Settings.Secure;



  public class BootReceiver extends BroadcastReceiver {  

     public void onReceive(Context context, Intent intent) {

    	        String mDeviceID = Secure.getString(context.getContentResolver(),

 				Secure.ANDROID_ID);

    			Editor editor = context.getSharedPreferences(PushService.TAG, 0)

    					.edit();

    			editor.putString(PushService.PREF_DEVICE_ID, mDeviceID);

    			editor.commit();

    			PushService.actionStart(context);

        }  



}

 ConnectionLog.java

/*

 * $Id$

 */



package com.carcare.service;



import java.io.BufferedWriter;

import java.io.File;

import java.io.FileWriter;

import java.io.IOException;

import java.io.Writer;

import java.text.SimpleDateFormat;

import java.util.Date;



import android.os.Environment;



public class ConnectionLog

{

	private String mPath;

	private Writer mWriter;



	private static final SimpleDateFormat TIMESTAMP_FMT = 

	  new SimpleDateFormat("[HH:mm:ss] ");



	public ConnectionLog()

	  throws IOException

	{

		File sdcard = Environment.getExternalStorageDirectory();

        File logDir = new File(sdcard, "appmkr/log/");

        if (!logDir.exists()) {

        	logDir.mkdirs();

        	// do not allow media scan

            new File(logDir, ".nomedia").createNewFile();

        }        

		

		open(logDir.getAbsolutePath() + "/push.log");

	}



	public ConnectionLog(String basePath)

	  throws IOException

	{

		open(basePath);

	}



	protected void open(String basePath)

	  throws IOException

	{

		File f = new File(basePath + "-" + getTodayString());

		mPath = f.getAbsolutePath();

		mWriter = new BufferedWriter(new FileWriter(mPath), 2048);



		println("Opened log.");

	}



	private static String getTodayString()

	{

		SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd-hhmmss");

		return df.format(new Date());

	}



	public String getPath()

	{

		return mPath;

	}



	public void println(String message)

	  throws IOException

	{

		mWriter.write(TIMESTAMP_FMT.format(new Date()));

		mWriter.write(message);

		mWriter.write('\n');

		mWriter.flush();

	}



	public void close()

	  throws IOException

	{

		mWriter.close();

	}

后台采用网上的PHP推送源码进行配置。

你可能感兴趣的:(android)