Integrate Your App
After you Obtain Credentials to use Amazon Device Messaging (ADM) and set it up in your development environment, integrate ADM with your app. See Overview of ADM for a high-level overview of the process of adopting ADM in your app and server code.
- Testing notes
- Update your app manifest
- Store your API key as an asset
- Integrate ADM into your app's main activity
- Implement handling for registration and messages
- Gracefully degrade if ADM is unavailable
- Test ADM integration
Testing notes
When testing your app's use of ADM, note the following:
- First-generation Kindle Fire tablets do not support ADM.
- Second-generation Kindle Fire tablets must be running the following system versions to support ADM. To see the system version, go to Settings > Device > About.
- Kindle Fire HD 8.9" - System Version 8.3.0 or later
- Kindle Fire HD 7" - System Version 7.3.0 or later
- Kindle Fire (2nd Generation) - System Version 10.3.0 or later. On second-generation tablets, your corporate firewall may block network access for ADM. Your app must be able to communicate with servers outside your corporate network using UDP on one of the following ports: 49317, 33434, 40317
- Other system versions of Fire tablets and Fire TV devices all support ADM.
Update your app manifest
Updating your AndroidManifest.xml file is the first step in receiving messages sent via ADM. You must make the following changes to your existing file.
-
Open your AndroidManifest.xml file and add the Amazon namespace:
xmlns:amazon="http://schemas.amazon.com/apk/res/android"
-
To declare the permissions necessary to support ADM, after the
manifest
element, add thepermission
anduses-permission
elements.<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:amazon="http://schemas.amazon.com/apk/res/android" package="[YOUR PACKAGE NAME]" android:versionCode="1" android:versionName="1.0"> <!-- This permission ensures that no other application can intercept your ADM messages. --> <permission android:name="[YOUR PACKAGE NAME].permission.RECEIVE_ADM_MESSAGE" android:protectionLevel="signature" /> <uses-permission android:name="[YOUR PACKAGE NAME].permission.RECEIVE_ADM_MESSAGE" /> <!-- This permission allows your app access to receive push notifications from ADM. --> <uses-permission android:name="com.amazon.device.messaging.permission.RECEIVE" /> <!-- ADM uses WAKE_LOCK to keep the processor from sleeping when a message is received. --> <uses-permission android:name="android.permission.WAKE_LOCK" /> ... </manifest>
-
Explicitly enable ADM and declare whether your app requires ADM (
android:required="true"
) or can work without ADM being present (android:required="false"
). If you specifyandroid:required="false"
, your app must degrade gracefully if ADM is unavailable. In theapplication
node of the manifest, add theuses-library
element.... <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme"> <!-- You must explicitly enable ADM and declare whether your app cannot work without ADM (android:required="true") or can work without ADM (android:required="false"). If you specify android:required="false", your app must degrade gracefully if ADM is unavailable. --> <uses-library android:name="com.amazon.device.messaging" android:required="true"/> ...
-
Declare a broadcast receiver to handle the
REGISTRATION
andRECEIVE
intents that ADM sends. ADM requires that the receiver be defined in the AndroidManifest.xml file, rather than programmatically. Immediately afteruses-library
add the following elements.<!-- You must replace the names in the service and receiver tags with names that are appropriate to your package. --> <service android:name="[YOUR JOBSERVICE NAME]" android:permission="android.permission.BIND_JOB_SERVICE" android:exported="false" /> <!-- This is needed for devices with older ADM versions --> <service android:name="[YOUR SERVICE NAME]" android:exported="false" /> <receiver android:name="[YOUR RECEIVER NAME]" <!-- This permission ensures that only ADM can send your app registration broadcasts. --> android:permission="com.amazon.device.messaging.permission.SEND" > <!-- To interact with ADM, your app must listen for the following intents. --> <intent-filter> <action android:name="com.amazon.device.messaging.intent.REGISTRATION" /> <action android:name="com.amazon.device.messaging.intent.RECEIVE" /> <!-- Replace the name in the category tag with your app's package name. --> <category android:name="[YOUR PACKAGE NAME]" /> </intent-filter> </receiver>
-
After updating your AndroidManifest.xml file, you can confirm that the changes are correct for ADM by calling
ADMManifest.checkManifestAuthoredProperly()
.If you receive a
java.lang.NoClassDefFoundError: com.amazon.device.messaging.ADM
error, check your manifest to verify that you have added theuses-library
element in the location specified in step 3.
The following code is a complete example of an AndroidManifest.xml file enabled for ADM.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:amazon="http://schemas.amazon.com/apk/res/android"
package="[YOUR PACKAGE NAME]"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="15"
android:targetSdkVersion="15" />
<permission
android:name="[YOUR PACKAGE NAME].permission.RECEIVE_ADM_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="[YOUR PACKAGE NAME].permission.RECEIVE_ADM_MESSAGE" />
<uses-permission android:name="com.amazon.device.messaging.permission.RECEIVE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<uses-library
android:name="com.amazon.device.messaging"
android:required="true"/>
<service
android:name="[YOUR JOBSERVICE NAME]"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="false" />
<!-- This is needed for devices with older ADM versions -->
<service
android:name="[YOUR SERVICE NAME]"
android:exported="false" />
<receiver
android:name="[YOUR RECEIVER NAME]"
android:permission="com.amazon.device.messaging.permission.SEND" >
<intent-filter>
<action android:name="com.amazon.device.messaging.intent.REGISTRATION" />
<action android:name="com.amazon.device.messaging.intent.RECEIVE" />
<category android:name="[YOUR PACKAGE NAME]" />
</intent-filter>
</receiver>
</application>
</manifest>
Store your API key as an asset
To enable your app to receive messages, your app must contain a valid API key. Obtain Credentials describes the process of creating an API key, and Overview of ADM discusses the API key in more detail.
For a pre-release or "debug" version of your app, you must create an API key and store it in your project. To add the API key to your app:
- Create a file called
api_key.txt
located inside your project's assets folder. Placing the file in this specific directory is required. - Insert your API key as the only data in this
api_key.txt
file.
For a release or "production" version of your app, if your app uses the Appstore SDK, you must create an additional API key for the release version of your app. If using the older IAP SDK v2.0 and you sign your app using your own certificate, you must also create an API key for the release version of your app. In contrast, if using the IAP SDK v2.0, or no in-app purchasing SDK at all, and you allow Amazon to sign your app on your behalf, you do not need to create an additional API key. For a summary, see the following table.
App uses the Appstore SDK | App signed with your own certificate | API key required? |
---|---|---|
Yes | ||
Yes | ||
Yes | ||
No |
Integrate ADM into your app's main activity
The following code shows how to use the methods in the com.amazon.device.messaging.ADM
class to:
- Create an instance of an ADM context
- Initiate registration with ADM for this instance of your app
final ADM adm = new ADM(this);
if (adm.getRegistrationId() == null)
{
// startRegister() is asynchronous; your app is notified via the
// onRegistered() callback when the registration ID is available.
adm.startRegister();
}
You should run this code each time your app starts, for example, by placing it within your onCreate()
method. If your app is already registered, getRegistrationId()
returns the registration ID for this instance of the app, and startRegister()
isn't called. If this is the first startup of the app on a given device or if a previous registration failed, this code begins the registration process.
Implement handling for registration and messages
ADM has three classes that you must extend. Refer to the following table to understand the function of each. You can also review the ADM API reference.
Class | Description |
---|---|
com.amazon.device.messaging.ADMMessageHandlerJobBase |
For handling messages on the latest Fire devices |
com.amazon.device.messaging.ADMMessageHandlerBase |
For handling messages on older Fire devices |
com.amazon.device.messaging.ADMMessageReceiver |
For forwarding messages to the appropriate message handling class |
The broadcast receiver declared in your manifest both listens for intents and invokes your app when intents arrive. Your app communicates with the broadcast receiver via the following callback methods defined in the com.amazon.device.messaging.ADMMessageHandlerJobBase
and com.amazon.device.messaging.ADMMessageHandlerBase
classes:
Callback Method | Description |
---|---|
onRegistered |
Called when the registration ID for the app instance is ready. Your app must transmit this registration ID to your server. Doing so enables your server to send messages to the app instance. |
onUnregistered |
Called if your app instance becomes unregistered from ADM. |
onRegistrationError |
Called if your app's ADM registration request fails for any reason, such as no Amazon user being signed in to the device. |
onMessage |
Called when the ADM client delivers a message to your app instance. |
Your app must override these callbacks when you implement subclasses of com.amazon.device.messaging.ADMMessageHandlerJobBase
, com.amazon.device.messaging.ADMMessageHandlerBase
and com.amazon.device.messaging.ADMMessageReceiver
, as shown in the following code examples:
-
Check if the current device has been updated to support new ADM service.
ADMLatestAvailable = false ; try{ Class.forName( "com.amazon.device.messaging.ADMMessageHandlerJobBase" ); ADMLatestAvailable = true ; } catch (ClassNotFoundException e) { // Handle the exception. }
-
Create your receiver and service implementations.
public class Receiver extends ADMMessageReceiver { public Receiver() { // This is needed for backward compatibility super(MyADMLegacyMessageHandler.class); // Where available, prefer using the new job based if (ADMLatestAvailable) { registerJobServiceClass(MyADMMessageHandler.class, <JOB_ID>) } } // Nothing else is required here; your broadcast receiver automatically // forwards intents to your service for processing. }
public class MyADMMessageHandler extends ADMMessageHandlerJobBase { @Override protected void onRegistered(final Context context, final String newRegistrationId) { // You start the registration process by calling startRegister() in your Main // Activity. When the registration ID is ready, ADM calls onRegistered() on // your app. Transmit the passed-in registration ID to your server, so your // server can send messages to this app instance. onRegistered() is also // called if your registration ID is rotated or changed for any reason; your // app should pass the new registration ID to your server if this occurs. // Your server needs to be able to handle a registration ID up to 1536 characters // in length. // The following is an example of sending the registration ID to your // server via a header key/value pair over HTTP. URL url = new URL(YOUR_WEBSERVER_URL); HttpURLConnection con = (HttpURLConnection) url.openConnection(); con.setDoInput(true); con.setUseCaches(false); con.setRequestMethod("POST"); con.setRequestProperty("RegistrationId", newRegistrationId); con.getResponse(); } @Override protected void onUnregistered(final Context context, final String registrationId) { // If your app is unregistered on this device, inform your server that // this app instance is no longer a valid target for messages. } @Override protected void onRegistrationError(final Context context, final String errorId) { // You should consider a registration error fatal. In response, your app may // degrade gracefully, or you may wish to notify the user that this part of // your app's functionality is not available. } @Override protected void onMessage(final Context context, final Intent intent) { // Extract the message content from the set of extras attached to // the com.amazon.device.messaging.intent.RECEIVE intent. // Create strings to access the message and timeStamp fields from the JSON data. final String msgKey = getString(R.string.json_data_msg_key); final String timeKey = getString(R.string.json_data_time_key); // Obtain the intent action that will be triggered in onMessage() callback. final String intentAction = getString(R.string.intent_msg_action); // Obtain the extras that were included in the intent. final Bundle extras = intent.getExtras(); // Extract the message and time from the extras in the intent. // ADM makes no guarantees about delivery or the order of messages. // Due to varying network conditions, messages may be delivered more than once. // Your app must be able to handle instances of duplicate messages. final String msg = extras.getString(msgKey); final String time = extras.getString(timeKey); } }
public class MyADMLegacyMessageHandler extends ADMMessageHandlerBase
{
@Override
protected void onRegistered(final String newRegistrationId)
{
// You start the registration process by calling startRegister() in your Main
// Activity. When the registration ID is ready, ADM calls onRegistered() on
// your app. Transmit the passed-in registration ID to your server, so your
// server can send messages to this app instance. onRegistered() is also
// called if your registration ID is rotated or changed for any reason; your
// app should pass the new registration ID to your server if this occurs.
// Your server needs to be able to handle a registration ID up to 1536 characters
// in length.
// The following is an example of sending the registration ID to your
// server via a header key/value pair over HTTP.
URL url = new URL(YOUR_WEBSERVER_URL);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setDoInput(true);
con.setUseCaches(false);
con.setRequestMethod("POST");
con.setRequestProperty("RegistrationId", newRegistrationId);
con.getResponse();
}
@Override
protected void onUnregistered(final String registrationId)
{
// If your app is unregistered on this device, inform your server that
// this app instance is no longer a valid target for messages.
}
@Override
protected void onRegistrationError(final String errorId)
{
// You should consider a registration error fatal. In response, your app may
// degrade gracefully, or you may wish to notify the user that this part of
// your app's functionality is not available.
}
@Override
protected void onMessage(final Intent intent)
{
// Extract the message content from the set of extras attached to
// the com.amazon.device.messaging.intent.RECEIVE intent.
// Create strings to access the message and timeStamp fields from the JSON data.
final String msgKey = getString(R.string.json_data_msg_key);
final String timeKey = getString(R.string.json_data_time_key);
// Obtain the intent action that will be triggered in onMessage() callback.
final String intentAction = getString(R.string.intent_msg_action);
// Obtain the extras that were included in the intent.
final Bundle extras = intent.getExtras();
// Extract the message and time from the extras in the intent.
// ADM makes no guarantees about delivery or the order of messages.
// Due to varying network conditions, messages may be delivered more than once.
// Your app must be able to handle instances of duplicate messages.
final String msg = extras.getString(msgKey);
final String time = extras.getString(timeKey);
}
}
Gracefully degrade if ADM is unavailable
In your manifest file, you declare whether your app can (android:required="false"
) or cannot (android:required="true"
) work without ADM. If you specify android:required="false"
, your app must degrade gracefully if ADM is unavailable.
Designing your app to accommodate the absence of ADM allows you to build a single APK that can be installed and run on devices that may or may not include ADM.
To modify your app to gracefully degrade:
-
Use code similar to the following to check for ADM.
ADMAvailable = false ; try { Class.forName( "com.amazon.device.messaging.ADM" ); ADMAvailable = true ; } catch (ClassNotFoundException e) { // Handle the exception. }
-
Add the following code to any of your code that requires the ADM library runtime.
if (ADMAvailable) { // Your code that requires ADM goes here. }
-
In your AndroidManifest.xml file, verify that the application element specifies:
uses-library android:name="com.amazon.device.messaging" android:required="false"
Test ADM integration
You can confirm that your app is set up with ADM by sending a test notification to a device. See Send a Message for more information.
Last updated: Oct 30, 2024