and possibly create Notifications. Without the power management code we added,this wouldn’t happen when the device is asleep.
there will always be some window of time where an event has happened that you’d like to give your user a Notification.Android’s Cloud to Device Messaging service
provides an elegant alternative to this.The less time,the more valuable.we can use C2DM to allow a server to tell our background Service to refresh its cache.
Update manifest with C2DM permissions
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="example.stockportfolio" android:versionCode="1" android:versionName="1.0" > <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".ViewStocks" android:label="@string/title_activity_main" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name="PortfolioManagerService" android:process=":stocks_background" android:icon="@drawable/icon" android:label="@string/service_name" /> <service android:name=".SendC2dmRegistrationService" android:process=":stocks_background" /> <receiver android:name="PortfolioStartupReceiver" android:process=":stocks_background" > <intent-filter > <action android:name="android.intent.action.BOOT_COMPLETED"/> </intent-filter> </receiver> <receiver android:name=".AlarmReceiver" android:process=":stocks_background" /> <receiver android:name=".PushReceiver" android:permission="com.google.android.c2dm.permission.SEND"> <intent-filter> <action android:name="com.google.android.c2dm.intent.RECEIVE" /> <category android:name="example.stockportfolio" /> </intent-filter> <intent-filter> <action android:name="com.google.android.c2dm.intent.REGISTRATION" /> <category android:name="example.stockportfolio" /> </intent-filter> </receiver> </application> <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="15" /> <uses-permission android:name="android.permission.INTERNET"/> <permission android:name="example.stockportfolio.permission.C2D_MESSAGE" android:protectionLevel="signature" /> <uses-permission android:name="cexample.stockportfolio.permission.C2D_MESSAGE"/> <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/> <uses-permission android:name="android.permission.WAKE_LOCK"/> <uses-permission android:name="android.permission.VIBRATE"/> </manifest>initiating the C2DM registration process.
public class PortfolioStartupReceiver extends BroadcastReceiver { private static final String DEVELOPER_EMAIL_ADDRESS = "..."; @Override public void onReceive(Context context, Intent intent) { Intent registrationIntent = new Intent("com.google.android.c2dm.intent.REGISTER"); registrationIntent.putExtra("app", PendingIntent.getBroadcast(context, 0, new Intent(), 0)); registrationIntent.putExtra("sender", DEVELOPER_EMAIL_ADDRESS); context.startService(registrationIntent); } }Now instead of using the AlarmManager here to schedule the execution of our Service, we’re going to rely on C2DM.
Once the device boots up, the receiver will send out this registration request. We need another BroadcastReceiver to handle the response from the C2DM servers.
Registration and messaging receiver
/** * <code>BroadcastReceiver</code> used to receive events from the * Cloud to Device Messaging (C2DM) service. */ public class PushReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { AlarmReceiver.acquireLock(context); if(intent.getAction().equals("com.google.android.c2dm.intent.REGISTRATION")){ onRegistration(context,intent); }else if(intent.getAction().equals("com.google.android.c2dm.intent.RECEIVE")){ onMessage(context,intent); } } /** * This method is used to handle the registration event being sent from C2DM. */ private void onRegistration(Context context, Intent intent){ String regId=intent.getStringExtra("registration_id"); if(regId!=null){ Intent i=new Intent(context,SendC2dmRegistrationService.class); i.putExtra("regId", regId); context.startService(i); } } /** * This method is used to handle events sent from your servers, via the C2DM service. */ private void onMessage(Context context, Intent intent){ Intent stockService=new Intent(context,PortfolioManagerService.class); // copy any data sent from your server stockService.putExtras(intent); context.startService(stockService); } }Note that when we receive a message, we acquire the static WakeLock in a manner similar to the previous technique. There are two types of messages that it’ll
We need this ID in order for our app servers to be able to send events to the C2DM servers. The C2DM servers will use the registration ID provided by our servers to route the message to the correct device, and then to the correct BroadcastReceiver on that device. We could send the registration ID to our servers from this BroadcastReceiver, but a BroadcastReceiver is designed to execute quickly, so we’ll offload this to an IntentService.
IntentService for sending registration info to servers
public class SendC2dmRegistrationService extends IntentService { private static final String WORKER_NAME = "SendC2DMReg"; public SendC2dmRegistrationService() { super(WORKER_NAME); // TODO Auto-generated constructor stub } @Override protected void onHandleIntent(Intent intent) { // TODO Auto-generated method stub try{ String regId = intent.getStringExtra("regId"); // TODO: Send the regId to the server } finally { AlarmReceiver.releaseLock(); } } }
In addition to the registration ID from the device, it’ll also need a ClientLogin auth token. This is a generic Google authentication and authorization mechanism.
onMesage()
This is the code to start the PortfolioManagerService. In this case, we’ve still acquired the static WakeLock. But as we saw in the previous technique, the PortfolioManager-
Service will release this WakeLock once it finishes its work.
In this example, we use a message pushed from the server to tell our background Service to update its cache and generate Notifications as needed. But the data that we
push from the server can be much richer. When your application sends data to the C2DM servers, it can send arbitrary name-value pairs. Those name-value pairs can then be accessed from your receiver using the Intent.getXXXExtra methods. For our application, we could have our server track the high/low price events, and it could pass this information as part of the Intent. That could save our background Service from having to wait for data from the network, so that it can issue Notifications quicker.