Simple Google Android C2DM Tutorial (Push Notifications for Android)

This tutorial is for getting started with Android Cloud to Device Messaging (C2DM) on Android. In the iOS world it is knows as “push notifications”.

There are many good resources out there for getting started, but all of them I felt were lacking in one way or another, which is why I decided to put together this tutorial of my own. A lot of my code and structure is borrowed from other posts that are referenced in the links at the end.

Things to know before getting started:

  • There are many different ways of accomplishing the same thing (polling, constant server connections, SMS messages). But as of right now, C2DM is the best option for keeping your Android application data up to date.
  • Do not send large amounts of data in the pushes. These should be “tickles” from the server telling your Android app to go get fresh data from the server.
  • This will only work with devices running Android 2.2 and later.
  • You need to have a device or emulator that is registered with a Google account. It won’t work if the emulator is “unregistered”.  Just assign a Google account to it and you’ll be fine.
  • You will need some server side scripting to actually push messages to the device.

Step 1: Sign-up for a C2DM account with Google

Follow the steps here. Normally takes a day or so before you are ready to roll.

Step 2: Setup your Manifest

Overview of the manifest changes:

  1. Permission to receive C2DM messages
  2. Access to the internet
  3. Restrict access to your C2DM messages so no other app can see them
  4. Declare a Receiver, that we’ll create later, that will let us receive the C2DM events
  5. Make sure that the minSdkVersion is set so that only 2.2 and higher can access your app
<manifest package="com.example.myapp" ...>

   <!-- Only this application can receive the messages and registration result -->
   <permission android:name="com.example.myapp.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
   <uses-permission android:name="com.example.myapp.permission.C2D_MESSAGE" />

   <!-- This app has permission to register and receive message -->
   <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />

   <!-- Send the registration id to the server -->
   <uses-permission android:name="android.permission.INTERNET" />

   <application...>
      <!-- Only C2DM servers can send messages for the app. If permission is not set -
any other app can generate it -->
      <receiver android:name=".MyC2dmReceiver"
android:permission="com.google.android.c2dm.permission.SEND">
          <!-- Receive the actual message -->
          <intent-filter>
              <action android:name="com.google.android.c2dm.intent.RECEIVE" />
              <category android:name="com.example.myapp" />
          </intent-filter>
          <!-- Receive the registration id -->
          <intent-filter>
              <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
              <category android:name="com.example.myapp" />
          </intent-filter>
      </receiver>
      ...
   </application>
   ...
</manifest>

Quick note on this step: make sure that your receiver is declared within your application tag. I’ve seen it twice where developers declared it outside that application tag. You won’t get any errors this way, but you also won’t get and receive events. It will never get to the onReceive method. It never gets called because the receiver is declared in the wrong place. :)

Step 3: Send the Registration call

Intent registrationIntent = new Intent("com.google.android.c2dm.intent.REGISTER");

registrationIntent.putExtra("app", PendingIntent.getBroadcast(context, 0, new Intent(), 0));

registrationIntent.putExtra("sender", aliasEmailUsedAtSignup);

context.startService(registrationIntent);

Where you put this code is up to you. If just getting started, you might want to just put in your main activity. After you get that working, you probably want to put it in a Utility class like is seen in this project.

Step 4: Handling Registration in your Receiver

The registration event will come back to the receiver that we declared in our AndroidManifest.xml. Create a Receiver class that looks something like this:

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences.Editor;
import android.util.Log;

import com.rain.skullcandy.R;
import com.rain.skullcandy.activities.FavoriteLocations;
import com.rain.skullcandy.model.SkullCandyModel;

public class MyC2dmReceiver extends BroadcastReceiver {
	private static String KEY = "c2dmPref";
	private static String REGISTRATION_KEY = "registrationKey";

	private Context context;
	@Override
	public void onReceive(Context context, Intent intent) {
	    this.context = context;
		if (intent.getAction().equals("com.google.android.c2dm.intent.REGISTRATION")) {
	        handleRegistration(context, intent);
	    } else if (intent.getAction().equals("com.google.android.c2dm.intent.RECEIVE")) {
	        handleMessage(context, intent);
	    }
	 }

	private void handleRegistration(Context context, Intent intent) {
	    String registration = intent.getStringExtra("registration_id");
	    if (intent.getStringExtra("error") != null) {
	        // Registration failed, should try again later.
		    Log.d("c2dm", "registration failed");
		    String error = intent.getStringExtra("error");
		    if(error == "SERVICE_NOT_AVAILABLE"){
		    	Log.d("c2dm", "SERVICE_NOT_AVAILABLE");
		    }else if(error == "ACCOUNT_MISSING"){
		    	Log.d("c2dm", "ACCOUNT_MISSING");
		    }else if(error == "AUTHENTICATION_FAILED"){
		    	Log.d("c2dm", "AUTHENTICATION_FAILED");
		    }else if(error == "TOO_MANY_REGISTRATIONS"){
		    	Log.d("c2dm", "TOO_MANY_REGISTRATIONS");
		    }else if(error == "INVALID_SENDER"){
		    	Log.d("c2dm", "INVALID_SENDER");
		    }else if(error == "PHONE_REGISTRATION_ERROR"){
		    	Log.d("c2dm", "PHONE_REGISTRATION_ERROR");
		    }
	    } else if (intent.getStringExtra("unregistered") != null) {
	        // unregistration done, new messages from the authorized sender will be rejected
	    	Log.d("c2dm", "unregistered");

	    } else if (registration != null) {
	    	Log.d("c2dm", registration);
	    	Editor editor =
                context.getSharedPreferences(KEY, Context.MODE_PRIVATE).edit();
            editor.putString(REGISTRATION_KEY, registration);
    		editor.commit();
	       // Send the registration ID to the 3rd party site that is sending the messages.
	       // This should be done in a separate thread.
	       // When done, remember that all registration is done.
	    }
	}

	private void handleMessage(Context context, Intent intent)
	{
		//Do whatever you want with the message
	}
}

A couple things to note in the above code.

  • Once you get the registration ID, you’ll want to save it to the shared preferences like I’m doing so that the ID persists. More info on shared preferences here.
  • Like the comments say, you’ll want to save the registration ID to the server as soon as you get it.
  • A good way to tell the user that something important has happened is using the Notification class. This is a great feature for the Android platform that makes iOS developers jealous :)

Step 5: Sending Messages from your server side application

The server side scripting is outside the scope of this tutorial. Some references can be found here and here

That’s it!

More resources listed here:

By: Bryce Barrand Categories: Blogroll Tags: , , , , , , ,

34 Responses to Simple Google Android C2DM Tutorial (Push Notifications for Android)

  1. abc xyz says:

    waste tutorial not even completed fully.

    but says that it is the simple tutorial, i say worst tutorial

  2. Runny says:

    @abc xyz
    waste comment not even completed fully.

    but says that it is the simple comment, i say worst comment

  3. I Give You Props says:

    This post was very helpful in getting our C2DM implementation completed. The Android documentation on C2DM is hideous and doesn’t explain anywhere that the author creates a new class that extends BroadcastReceiver. They just assume the person knows what to do. I’m working in a team environment and a C2DM implementation was my first adventure into Android. What really frustrated me was trying to figure out how to resolve all the functions I needed to use that depend on Context because BroadcastReceiver doesn’t derive from Context.

    This tutorial isn’t the greatest C2DM implementation ever but it was good enough to point me in the right direction. Thanks.

  4. I Give You Props says:

    One final thing we had to do: If the user wipes their user data, neither the third-party server nor Google’s C2DM service get notification that the device no longer knows about a specific ‘registration_id’. However, the C2DM Service delivers messages to the app (which is still installed) if the third-party sends the message using the old ‘registration_id’. The solution I came up with was to include an extra field that contains the ‘registration_id’ and then ignore any notifications that come in for anything but the ‘registration_id’ the app is looking for. The app therefore ignores all other notifications. Was really confusing until I figured out what was going on.

  5. Bryce says:

    @IGiveYouProps that is a great suggestion. Thanks for pointing that out.

    @lytsing Thanks for linking to that.

  6. Bruce says:

    String comparisons aren’t done with the equality operator “==” in Java. You have to use the equals method for that. Otherwise you are trying to check whether the “error” String object is the same as the String objects you are creating on the fly in these if statements. And that will never be the case.

  7. Pingback: Simple Groovy 3rd party server for android push(C2DM) development | The Thought Circus

  8. Pingback: Using Google’s Cloud2Device Messaging with Unity3D | Twelve21

  9. Infostar says:

    Does anyone know how realtime is C2DM? Will the notification delivered within one second or withing 10 second? Will the server get a receive ticket?

  10. Bryce Barrand says:

    @Infostar, It is very fast. From my testing the notifications felt like a second or less.

  11. Frode Nilsen says:

    Thank you for this excellent post, and for somehow magically knowing that I had placed my receiver registration outside the application tag :-) Saved my day!

  12. Jason Voll says:

    I’m getting a Service Not Available error back upon registration. I’ve pretty much copied what you have above. I have registered the email I am using and it still doesn’t seem to be working.

  13. @Frode, I did the same thing at first. About drove me nuts. I’m glad you got it.

    @Jason I’m not sure why you are getting that. I’m sorry I couldn’t be of more assistance.

  14. rajaramesh says:

    It shows a best way for android beginners…………..

  15. firzan says:

    hello….
    how i make a chat application using C2DM….
    if 1 device sends the message then it will be push to the another device and vice-versa.

    plz help me…!!!

  16. firzan says:

    how i make a chat application using C2DM….
    if 1 device sends the message then it will be push to the another device and vice-versa.

  17. Gani says:

    Hi,

    Can anyone let me know, is it possible to send Device Specific Notifications using C2DM. That means Different users of My App want to be notified with different text notifications.

  18. Andrea says:

    Nice tutorial!
    Please could you add information explaining
    how to display the text-message received in a push notification
    on the main Activity with a Toast ?
    It would be very useful for me!
    Thanks

  19. @Gani, yes you can. On the server, you can define which users have which settings, and then only push to the desired devices

  20. @Andrea, Are you looking for text messages or push notifications? They are two very different things.

  21. Shashank says:

    Brilliant article ! I am doing a project on android connected app engine..and I am stuck at the database connectivity part..i am trying to use datastore to store my database..i know how to store and retrieve information on datastore but i dont know where exactly the code for it is to be put ! pls help ! if anyone can provide me with a basic idea of the file structure in eclipse it would be of great help..

  22. MAhe says:

    @Jason Voll Have you tried that in device?
    Regards,
    Mahe

  23. MAhe says:

    Forgot to say that Google will not deliver registrationId to emulator as I know. . :)

  24. One part is still missing here and in the documentation:

    When the app registers with multiple servers (=multiple senderIDs),
    how to know what registration a handleRegistration() call is for.

  25. arul says:

    nice post.It helped me to create a messaging app.

  26. HAO RAN says:

    Hi,i am a android beginner and i just want to create a simple push notification function for my simple hello world function. But for the step 3, sending of registration call, i just copy and paste the code that you provided but i get some syntax errors,
    like for the second sentence,
    //registrationIntent.putExtra(“app”, PendingIntent.getBroadcast(context, 0, new Intent(), 0));//,
    the entire line is being red lined and errors of:
    Multiple markers at this line
    - Syntax error on tokens, delete these tokens
    - Syntax error on token “(“, delete this token
    - Syntax error on token “”app”", invalid
    FormalParameterList
    - Syntax error on tokens, delete these tokens
    - Syntax error on token(s), misplaced construct(s)
    for the third sentence,
    //registrationIntent.putExtra(“sender”, aliasEmailUsedAtSignup);//,
    the dot between registrationIntent and putExtra, “sender”, and aliasEmailUsedAtSignup is underlined red, errors being:
    Multiple markers at this line
    - Syntax error on token “”sender”", invalid FormalParameterList
    - Syntax error on token “aliasEmailUsedAtSignup”, VariableDeclaratorId expected after
    this token
    - Syntax error on token(s), misplaced construct(s)
    and for for the last sentence,
    //context.startService(registrationIntent);//,
    the dot between context and startService and registrationIntent is underlined red
    and the errors being:
    Multiple markers at this line
    - Syntax error on token “registrationIntent”, VariableDeclaratorId expected after
    this token
    - Syntax error on token(s), misplaced construct(s)

    and lastly, for the step 4, if my codes only got the main.java and the receiver java, what do i put in place of
    //import com.rain.skullcandy.R;
    import com.rain.skullcandy.activities.FavoriteLocations;
    import com.rain.skullcandy.model.SkullCandyModel;// that is in the receiver java?? or do i just delete this 3 imports and leave it?

    Much help would really be appreciated because my java knowledge is barely a few weeks and i hava only a short period left to rectify my simple hello world just with an additional push notification function

  27. xees says:

    This article requires that you have knowledge of BroadCast , read retro meirs Pro Android programming…

  28. Pingback: Android Cloud to Device Messaging (C2DM) « TMK Mobile

  29. Pingback: Apps benachrichtigung durch App - Android-Hilfe.de

  30. Pingback: Pushing data to android application from server using WCF REST | PHP Developer Resource

  31. Pingback: Android c2dm Service to allow notification and reading of a Gmail account | appsgoogleplus.com

  32. Pingback: Best Android push notification (C2DM) tutorials « Looks OK!

  33. Pingback: SIMPLE GOOGLE ANDROID C2DM TUTORIAL (PUSH NOTIFICATIONS FOR ANDROID) | jolam

Leave a Reply