Browse through more Android tutorials. If you'd like to see a tutorial on any particular topic, do leave a comment in the wishlist page. We frequently post new tutorials along with app releases. You may subscribe to our newsletter to get all updates in your inbox.
Now you can get the latest Java source bundled with each app update. Install the app from Google Play and go to Settings > Extras.
* Become a 10x programmer! →

«  Create a SMS app Create a notepad/to-do list app  »

Create an Instant Messaging app using Google Cloud Messaging (GCM)

DownloadDownload
By

Keywords: Google Cloud Messaging Google Play services ContentProvider SQLiteOpenHelper BroadcastReceiver NotificationManager SimpleCursorAdapter CursorLoader ActionBar DialogFragment ListFragment ViewBinder ContentResolver PreferenceFragment Google App Engine Google Plugin JPA Servlet

Contents

3 « Prev Page

10. GCM Utility class

GCM server needs registration ID to deliver message to a device. The registration ID identifies the device and application, as well as which servers are allowed to send messages. So to send or receive messages, you first need to get a registration ID.
The official docs explains in detail how to use GoogleCloudMessaging API to register the application for GCM and obtain the registration ID.
In short, we get an instance of GoogleCloudMessaging and invoke its register(senderID) method where senderID is the project number we obtained earlier. This should be done asynchronously (not on the UI thread).
	new AsyncTask<Void, Void, Boolean>() {
		@Override
		protected Boolean doInBackground(Void... params) {
			long backoff = BACKOFF_MILLI_SECONDS + random.nextInt(1000);
			for (int i = 1; i <= MAX_ATTEMPTS; i++) {
				Log.d(TAG, "Attempt #" + i + " to register");
				try {
					if (gcm == null) {
						gcm = GoogleCloudMessaging.getInstance(ctx);
					}
					String regid = gcm.register(Common.getSenderId());

					// You should send the registration ID to your server over HTTP,
					// so it can use GCM/HTTP or CCS to send messages to your app.
					ServerUtilities.register(Common.getPreferredEmail(), regid);

					// Save the regid - no need to register again.
					setRegistrationId(regid);
					return Boolean.TRUE;
					
				} catch (IOException ex) {
					Log.e(TAG, "Failed to register on attempt " + i + ":" + ex);
					if (i == MAX_ATTEMPTS) {
						break;
					}
					try {
						Log.d(TAG, "Sleeping for " + backoff + " ms before retry");
						Thread.sleep(backoff);
					} catch (InterruptedException e1) {
						// Activity finished before we complete - exit.
						Log.d(TAG, "Thread interrupted: abort remaining retries!");
						Thread.currentThread().interrupt();
					}
					// increase backoff exponentially
					backoff *= 2;		                
				}
			}
			return Boolean.FALSE;
		}

		@Override
		protected void onPostExecute(Boolean status) {
			//broadcastStatus(status);
		}
	}.execute(null, null, null);
					
There is sample code available on Google Code for working with GCM. We reused some of the code to create an utility class that you can get from here.

11. Server Utility class

Once we obtain the registration ID from GCM we send it to our server so that it can be used while sending messages. Our server will persist the registration ID along with the chat email ID. The server is responsible for sending the message to GCM server. More about this later when we implement the server code.
We reuse the ServerUtilities class from Google Code with a little modification which you can get from here. It basically contains utility methods to send HTTP POST request to a server.
    /**
     * Issue a POST request to the server.
     *
     * @param endpoint POST address.
     * @param params request parameters.
     *
     * @throws IOException propagated from POST.
     */
    private static void post(String endpoint, Map<String, String> params) throws IOException {
        URL url;
        try {
            url = new URL(endpoint);
        } catch (MalformedURLException e) {
            throw new IllegalArgumentException("invalid url: " + endpoint);
        }
        StringBuilder bodyBuilder = new StringBuilder();
        Iterator<Entry<String, String>> iterator = params.entrySet().iterator();
        // constructs the POST body using the parameters
        while (iterator.hasNext()) {
            Entry<String, String> param = iterator.next();
            bodyBuilder.append(param.getKey()).append('=').append(param.getValue());
            if (iterator.hasNext()) {
                bodyBuilder.append('&');
            }
        }
        String body = bodyBuilder.toString();
        //Log.v(TAG, "Posting '" + body + "' to " + url);
        byte[] bytes = body.getBytes();
        HttpURLConnection conn = null;
        try {
            conn = (HttpURLConnection) url.openConnection();
            conn.setDoOutput(true);
            conn.setUseCaches(false);
            conn.setFixedLengthStreamingMode(bytes.length);
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
            // post the request
            OutputStream out = conn.getOutputStream();
            out.write(bytes);
            out.close();
            // handle the response
            int status = conn.getResponseCode();
            if (status != 200) {
              throw new IOException("Post failed with error code " + status);
            }
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
      }
					

12. The GCM BroadcastReceiver

GCM delivers messages as a broadcast. Recall that we already registered the reciever in the manifest with appropriate permission and intent filter. Let's implement the class now.
public class GcmBroadcastReceiver extends BroadcastReceiver {
	
	private static final String TAG = "GcmBroadcastReceiver";
	private Context ctx;	

	@Override
	public void onReceive(Context context, Intent intent) {
		ctx = context;
		
		PowerManager mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
		WakeLock mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
		mWakeLock.acquire();
		
		try {
			GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(context);
			
			String messageType = gcm.getMessageType(intent);
			if (GoogleCloudMessaging.MESSAGE_TYPE_SEND_ERROR.equals(messageType)) {
				sendNotification("Send error", false);
				
			} else if (GoogleCloudMessaging.MESSAGE_TYPE_DELETED.equals(messageType)) {
				sendNotification("Deleted messages on server", false);
				
			} else {
				String msg = intent.getStringExtra(DataProvider.COL_MSG);
				String email = intent.getStringExtra(DataProvider.COL_FROM);
				
				ContentValues values = new ContentValues(2);
				values.put(DataProvider.COL_MSG, msg);
				values.put(DataProvider.COL_FROM, email);
				context.getContentResolver().insert(DataProvider.CONTENT_URI_MESSAGES, values);
				
				if (Common.isNotify()) {
					sendNotification("New message", true);
				}
			}
			setResultCode(Activity.RESULT_OK);
			
		} finally {
			mWakeLock.release();
		}
	}
					
Since a broadcast might wake up the device so first get hold of the wake lock. Then insert the message into the database and create a notification to alert the user. Finally, release the wake lock.

You can optionally implement an IntentService to handle the intent and perform background tasks.

	private void sendNotification(String text, boolean launchApp) {
		NotificationManager mNotificationManager = (NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE);
		
		Notification.Builder mBuilder = new Notification.Builder(ctx)
			.setAutoCancel(true)
			.setSmallIcon(R.drawable.ic_launcher)
			.setContentTitle(ctx.getString(R.string.app_name))
			.setContentText(text);

		if (!TextUtils.isEmpty(Common.getRingtone())) {
			mBuilder.setSound(Uri.parse(Common.getRingtone()));
		}
		
		if (launchApp) {
			Intent intent = new Intent(ctx, MainActivity.class);
			intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
			PendingIntent pi = PendingIntent.getActivity(ctx, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
			mBuilder.setContentIntent(pi);
		}
		
		mNotificationManager.notify(1, mBuilder.getNotification());
	}
					
We set a intent to the notification so that user can launch the app directly by clicking the notification.
Next, we'll develop the user interface of the app.
Share the love:  

Next Page » 3

App Gen
App Name:
Project Name:
Package:
Screens:
Splash
Login
Help
Main
List  Grid  Pager
Detail
Settings
Options:
Action Bar
Navigation Drawer
Dummy Data
Generate
Free Apps