GCM with custom broadcastreceiver

19,555

Solution 1

To change package/class name for application/GCMIntentService/GCMBroadcastReceiver for Android GCM (using Eclipse ADT)

Note: You are advised to verify that you can receive messages through GCM before making the following changes. To implement GCM in Android app using the application's default package name, see GCM: Getting Started.


Package name

To change package name of your app (e.g., new package name = com.example.newpackage),

  • In Package Explorer, right click the project → Android Tools → Rename Application Package.
    This updates the package name automatically and conveniently.
  • In AndroidManifest.xml, update the package name in permission and uses-permission for C2D_MESSAGE:

    <permission android:name="com.example.newpackage.permission.C2D_MESSAGE" android:protectionLevel="signature" />
    <uses-permission android:name="com.example.newpackage.permission.C2D_MESSAGE" />
    
  • In AndroidManifest.xml, update the package name in category of the receiver:

    <receiver
        android:name="com.example.oldpackage.GCMBroadcastReceiver"
        android:permission="com.google.android.c2dm.permission.SEND" >  <!-- Not this one!! -->
        <intent-filter>
            <action android:name="com.google.android.c2dm.intent.RECEIVE" />
            <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
    
            <category android:name="com.example.newpackage" />  <!-- This one!! -->
        </intent-filter>
    </receiver>
    

Name of GCMIntentService

If your app's package name is com.example.newpackage, your GCMIntentService must be called com.example.newpackage.GCMIntentService. If not,

  • Create a new class that extends GCMBroadcastReceiver and override getGCMIntentServiceClassName():

    public class MyBroadcastReceiver extends GCMBroadcastReceiver
    {
        @Override
        protected String getGCMIntentServiceClassName(Context context)
        {
            return MyIntentService.class.getName(); // Don't hard-code like "com.example.oldpackage.MyIntentService", see http://stackoverflow.com/a/936696/1402846
        }
    }
    

    this is based on Google's documentation on GCMBroadcastReceiver:

    By default, the GCMBaseIntentService class belongs to the application main package and is named DEFAULT_INTENT_SERVICE_CLASS_NAME. To use a new class, the getGCMIntentServiceClassName(Context) must be overridden.

  • In AndroidManifest.xml, update the name of the receiver:

    <receiver
        android:name="com.example.oldpackage.MyBroadcastReceiver"
        ... >
    </receiver>
    
  • In AndroidManifest.xml, update the name of the service:

    <service android:name="com.example.oldpackage.MyIntentService" />
    

Name of GCMBroadcastReceiver

If your changed the package/class name of GCMBroadcastReceiver:

  • In AndroidManifest.xml, update the name of the receiver:

    <receiver
        android:name="com.example.oldpackage.NewBroadcastReceiver"
        ... >
    </receiver>
    

Troubleshooting

  • Verify that in AndroidManifest.xml, the package name should appear at least 4 times:

    • In manifest:

      <manifest ...
          package="com.example.newpackage" ...
      
    • In permission:

      <permission android:name="com.example.newpackage.permission.C2D_MESSAGE" android:protectionLevel="signature" />
      
    • In uses-permission:

      <uses-permission android:name="com.example.newpackage.permission.C2D_MESSAGE" />
      
    • In category within intent-filter within receiver for the GCM broadcast receiver:

      <category android:name="com.example.newpackage" />
      
  • If you changed your package name, uninstall the old app on your device/emulator before testing.

  • If you changed your package name, notice that the registration ID (that you receive in onRegistered()) has changed.
  • If you received your registration ID in onRegistered(), you should see something like this in LogCat (tag GCMBroadcastReceiver):

    GCMBroadcastReceiver        onReceive: com.google.android.c2dm.intent.REGISTRATION
    GCMBroadcastReceiver        GCM IntentService class: com.example.oldpackage.MyIntentService
    

    Verify that the package/class name of the intent service is correct.

  • If you override getGCMIntentServiceClassName(Context) in your own GCMBroadcastReceiver, you should see something like this in LogCat (tag GCMRegistrar):

    GCMRegistrar        Setting the name of retry receiver class to com.example.oldpackage.MyBroadcastReceiver
    

    Verify that the package/class name of the broadcast receiver is correct.

  • When your server sends a message to GCM server, check the HTTP status code and HTTP response for errors.
  • And if you are desperate:
    • Check LogCat for errors.
    • Try restarting your device/emulator.
    • Try uninstalling your app on the device/emulator.
    • Try restarting Eclipse.
    • Try clean and re-build the project.
    • Try updating ADT (Pull down menu → Window → Android SDK Manager → Install packages).
    • Try updating Eclipse (Pull down menu → Help → Check for Updates).
    • Try restarting your computer.
    • Get a good sleep, or pray.

Solution 2

Because I use my code to generate lot of application with different package names I cannot use standard mypackage.GCMIntentService name.

I'm not sure whether you ask for a runtime or a build time solution. The other answers are runtime solutions so I thought I add some information on possibilites during build time.


In AndroidManifest, you can use the variable $PACKAGE_NAME (available by default) which will refer to the package name you specified in the attribute package in the manifest root element.

The value of this package attribute needs not be a hard coded string but can also be set dynamically via resource reference like so:

<manifest package="@string/app_package" ...>
    ...
    <service
        android:name="$PACKAGE_NAME.service.GCMIntentService"
        android:enabled="true" />
    ...
</manifest>

Note .service is just a subpackage that I included to show how you specify these. Of course, this solution would work only if your applications follow general rules like putting Service classes in a .service subpackage.

Also note, that you can actually leave the root package off completely. It will be expanded by Android:

<manifest package="@string/app_package" ...>
    ...
    <service
        android:name=".service.GCMIntentService"
        android:enabled="true" />
    ...
</manifest>

In effect, same as above.

If you are looking for a more flexible way to replace values in an AndroidManifest (or different files), you might want to have a look at the Ant Replace task (http://ant.apache.org/manual/Tasks/replace.html) or Resource Filtering (http://maven.apache.org/plugins/maven-resources-plugin/examples/filter.html) with Maven.

Solution 3

You should put GCMIntentService class into your root application package. Here org.rferl

<service
    android:name=".GCMIntentService"
    android:enabled="true" />

and receiver

   <receiver
            android:name="com.google.android.gcm.GCMBroadcastReceiver"
            android:permission="com.google.android.c2dm.permission.SEND" >
            <intent-filter>

                <!-- Receives the actual messages. -->
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                <!-- Receives the registration id. -->
                <action android:name="com.google.android.c2dm.intent.REGISTRATION" />

                <category android:name="com.EgoSecure.ma" />
            </intent-filter>
        </receiver>
Share:
19,555
pcu
Author by

pcu

Updated on June 04, 2022

Comments

  • pcu
    pcu almost 2 years

    I am implementing gcm notifications in my application. Because I use my code to generate lot of application with different package names I cannot use standard mypackage.GCMIntentService name. When generating applications I do changes only in Manifest and change imports of my R class. So I impelented my own BroadcastReceiver

    public class GCMReceiver extends GCMBroadcastReceiver {
      @Override
      protected String getGCMIntentServiceClassName(Context context) {
        return GCMIntentService.class.getName();
      }
    }
    

    to return name of GCMIntentService regardless of package name.

    Here is my manifest:

        <uses-permission android:name="android.permission.INTERNET" />
        <uses-permission android:name="android.permission.GET_ACCOUNTS" />
        <uses-permission android:name="android.permission.WAKE_LOCK" />
    
        <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
        <permission
            android:name="org.rferl.permission.C2D_MESSAGE"
            android:protectionLevel="signature" />
    
        <uses-permission android:name="org.rferl.permission.C2D_MESSAGE" />
    
    
        <service
            android:name="org.rferl.service.GCMIntentService"
            android:enabled="true" />
    
    
       <receiver
            android:name="org.rferl.GCMReceiver"
            android:enabled="true"
            android:permission="com.google.android.c2dm.permission.SEND" >
            <intent-filter>
    
                <!-- Receives the actual messages. -->
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                <!-- Receives the registration id. -->
                <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
    
                <category android:name="org.rferl" />
            </intent-filter>
        </receiver>
    

    Everything works fine, I can register, unregister, receive messages. But when application is not runnig no GCMIntentService.onMessage is not called. Am I missing something in my manifest? Why system did not start service?

  • pcu
    pcu over 11 years
    It is not possible due to using source code as base to generate many applications - they have to have different package name which I change in manifest. So if I generate application for example org.rferl.en then I need to change package structure for many classes change imports and so... Also I wrote that everything works fine when application is running I can receive messages. Only system does not call receiver or start service when application is not running.
  • Yahor10
    Yahor10 over 11 years
    If you use Android 3.0 or > ,you must set flag for "stopped" applications
  • pcu
    pcu over 11 years
    you are right, stopped application is also killed by user. So I killed it during testing. I am not able to test it right know because server guys are from other company. Also sample from google does not work when I killed it manually. But when I started it and for example turn off phone everything works. But how to put this flag in manifest file? I usually kills aplications on my phone if something went wrong and untill I start them I am not able receive messages. Little strange behaviour.
  • Yahor10
    Yahor10 over 11 years
    Flag should add to your broadcast intent -"include stopped packages".But if you not receive gcm data from the server that does not help you.
  • pcu
    pcu over 11 years
    It is not my intent. This Intent which calls my receiver provides GCM service. So I have no control to update, create it programaticaly.
  • Yahor10
    Yahor10 over 11 years
    Change your broadcast receiver as i describe above