The
Android emulator is capable of receiving push notifications through
GCM, however, it does require an additional step of actually logging
a valid Google account into the emulator when it is running. This
will need to be done for each emulator that you wish to use with push
notifications through GCM.
Google
Cloud Messaging
Google
provides a service called Google Cloud Messaging (GCM) to provide
push notifications for the Android platform. The
developer console provided by google is accessed with a google
account. Project management can be delegated to additional users, so
it is good form to create the project under the account which will
also be used in conjunction with releasing the application and
delegate functionality to developers.
1.
Create Project
Log
into the Google APIs console (https://code.google.com/apis/console)
to create a project. The “Overview” will display the project
number. This is how client applications will identify themselves to
the GCM service, in this case the Android application that will be
using the GCM library.
2.
Add GCM Service
Select the “Services”
menu item. A list of Google provided services is presented. Enable
the “Google Cloud Messaging for Android” by toggling the switch
to on. This enables the service for the project.
3.
Grand API Access
Select the “API Access”
menu item. There will already be an existing “Key for browser apps
(with referers)” which can be ignored. Click the “Create new
Server key...” button. Enter the IP or subnet which will be
allowed to send push notifications to clients. The “API Key”
will be used by the server to authenticate it is allowed to send push
notifications to the mobile devices.
Android
Application
Adding
push notification to an existing application is a straight forward
task of adding the GCM library and updating the manifest. Once push
notification is added to the application the developer is free to
implement management of the registration and registration ids and
handling the actual push notifications.
1.
Download GCM Package
Using
the Android SDK Manager, select the “Google Cloud Messaging for
Android Library”, and install. This will download the library into
$ANDROID_SDK/extras/google/gcm.
Copy $ANDROID_SDK/extras/google/gcm/gcm-client/dist/gcm.jar
to your project's $PROJECT/libs
directory. Refresh the libs
directory within Eclipse and add the gcm.jar
to the build path.
2.
Update Android Manifest
Several
additions need to be added to the AndroidManifest.xml
file to enable push notifications through GCM.
A
new permission PACKAGE.permission.C2D_MESSAGE,
where PACKAGE is the
project's package needs to be defined and added. Additionally the
RECEIVE and WAKE_LOCK
are dependencies from the GCM library.
<!-- gcm permissions -->
<permission
android:name="PACKAGE.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="PACKAGE.permission.C2D_MESSAGE" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
Under
the Application element
add a service, which is created further along in the process.
Replace PACKAGE with the
project's package. Note, there appears to be a limitation that this
service must reside in the root java package of the project.
Attempts to change this caused GCM to stop functioning, probably
based on some reflection aspects being used within the
GCMBaseIntentService
class.
<application...>
<service android:name="PACKAGE.GCMIntentService"/>
Also
under the Application
element add the boilerplate for the a broadcast receiver which is
used by the GCMIntentService
which was defined above. Replace PACKAGE
with the project's package.
<application...>
<receiver
android:name="com.google.android.gcm.GCMBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="PACKAGE" />
</intent-filter>
</receiver>
3.
Hook GCM Registration
At
some point the application will need to register itself with the
server which is generating push notifications. In the case of
applications which require login this is usually after the user has
been authenticated. The logout process should always include
mechanism to handle clearing the registration id from the server.
Otherwise, applications which do not require the user to log in can
register when convenient.
The
GCM library provides a couple functions to verify the environment for
the application is correctly setup. These methods will cause the
application to log the error and exit. They can be left in for
production, but are not necessary.
GCMRegistrar.checkDevice(context);
GCMRegistrar.checkManifest(context);
A
registration id uniquely identifies the phone to the GCM servers for
a project. Before requesting registration the application can check
if there is a current registration id available. If the registration
id exists the application can skip requesting a registration id.
Otherwise a registration request is sent with the application's
project id which will set into the motion a request to the GCM
service for a registration id and completing with a call to the
onRegistered method on the
GCMIntentService.
String registrationId = GCMRegistrar.getRegistrationId(context);
if (registrationId.equals("")) {
Log.d(getClass().getSimpleName(), "registering");
GCMRegistrar.register(context, context.getString(R.string.project_id));
} else {
Log.d(getClass().getSimpleName(), "registrationId: " + registrationId);
}
Unregistering
an application from the GCM service will set into motion the handling
of clearing the registration id from the GCM server and ending in the
call to the onUnregister
method on the GCMIntentService.
GCMRegistrar.unregister(context);
4.
Extend GCMBaseIntentService
The
interface to the GCM service is handled through implementing the
abstract methods of the GCMBaseIntentService.
There are registration methods, error handling methods, and receive
message methods.
The
registration id should be sent to the server which will be sending
push notifications. That value will be used by the server to direct
push notifications to the particular device. The only authoritative
source for the registration id should are calls to
GCMRegistrar.getRegistrationId
and this value should not be stored locally to determine if the
registration id is set. During the unregistration process the
registration id should be cleared on the server.
@Override
protected void onRegistered(Context context, String content)
{
Log.d(getClass().getSimpleName(), "onRegistered: " + content);
}
@Override
protected void onUnregistered(Context context, String content)
{
Log.d(getClass().getSimpleName(), "onRegistered: " + content);
}
5.
Push Notifications
The
GCMIntentService is also
the entry point for the push notification. The data will arrive
within an Intent as an
extra as a JSON encoded String.
This string can be parsed and the data extracted.
@Override
protected void onMessage(Context context, Intent intent)
{
String content = intent.getStringExtra(CONTENT);
Log.i(getClass().getSimpleName(), "content: " + content);
JSONObject json = new JSONObject(content);
// ...
}
Since
push notification data arrives asynchronously to the application UI
thread, it is necessary to implement a mechanism to handle the data
without a direct reference to an Activity. The application itself
may not have any active running activities.
Example
Application
The
example application (https://github.com/crazydays/scratchpad/tree/master/dealer) uses a queueing message service which is bound by
the GCMIntentService for
when receiving data. The lifecycle of the GCMIntentService
requires that the message queue service store messages when it stops
and load them again when started. The application's activity binds
and registers for notifications for when data arrives. Then the data
is published to the web view if it has been initialized.