C2DMBroadcastReceiver's onReceive is not executing (For Registration)

≡放荡痞女 提交于 2019-12-05 09:23:20
olamotte

For an obscure reason, your receiver class, C2DMReceiver, has to be in the root of your package, which is declared as your package name in your manifest. This is the only way i managed to get this working... so instead of :

service android:name="com.sample.gt.c2dm.C2DMReceiver" 

try

service android:name=".C2DMReceiver"

here is my manifest excerpt (except the permissions)

 <!--C2DM -->
    <service android:name=".C2DMReceiver" />
    <receiver android:name="com.google.android.c2dm.C2DMBroadcastReceiver"
        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="my.package" />
        </intent-filter>
        <!-- Receive the registration id -->
        <intent-filter>
            <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
            <category android:name="my.package" />
        </intent-filter>
    </receiver>

I feel your pain, I struggled getting C2DM to work as well looking at vogille.de amongst other sites. What ended up working for me was to use the C2DM.jar file created by the eclipse "App Engine Connected Android Project" (under File > New > Project > Google).

Note: at the time of this writing you must install the beta version of the plugin to have this option! http://code.google.com/eclipse/beta/docs/download.html

Relevant portions of my manifest file:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      ...
>
    <permission
        android:name="my_package_name.permission.C2D_MESSAGE"
        android:protectionLevel="signature"
    />
    <uses-permission android:name="my_package_name.permission.C2D_MESSAGE" />
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />

    <application
       ...
    >
        <!-- Only C2DM servers can send messages for the app. If permission is not set - any other app can generate it --> 
        <receiver
            android:name="com.google.android.c2dm.C2DMBroadcastReceiver"
            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="my_package_name" />
            </intent-filter>
            <!-- Receive the registration id -->
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
                <category android:name="my_package_name" />
            </intent-filter>
        </receiver>
    </application>
</manifest>

Here's the code I use to interact with the C2DM service:

package my_package_name

import com.google.android.c2dm.C2DMBaseReceiver;
import com.google.android.c2dm.C2DMessaging;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;

/**
 * Receive C2DM state changes.
 * 
 * Be careful: the various onX receivers may be called from a mysterious
 * context -- in particular they cannot safely create new AsyncTask objects.
 */
public class C2DMReceiver extends C2DMBaseReceiver {

    // GMail account associated with the C2DM application.  Must agree with
    // what the 3rd party server uses to authenticate with C2DM.
    private static final String C2DM_SENDER = "my_email@gmail.com";

    // -----------------------------------------------------------------

    /**
     * Ask this device to register for C2DM messaging.  Will trigger
     * onRegistered (or onError) when finished.
     */
    public static void register(Context context) {
        C2DMessaging.register(context, C2DM_SENDER);
    }

    /**
     * Unregister this device from further C2DM messaging.
     */
    public static void unregister(Context context) {
        C2DMessaging.unregister(context);
    }

    // -----------------------------------------------------------------

    public C2DMReceiver() {
        super(C2DM_SENDER);
    }

    @Override
    protected void onMessage(Context context, Intent intent) {
        // Extras contains whatever your server put into the C2DM message.
        final Bundle extras = intent.getExtras();
    }

    @Override
    public void onError(Context context, String error_id) {
    }

    @Override
    public void onRegistered(Context context, String registration_id) {
    }

    @Override
    public void onUnregistered(Context context) {
    }
}

The sample code generated includes a Java based AppEngine app. I am using python, here's the relevant code to round out this post:

class C2dmAuthToken(db.Model):
    """Maintain an auth token used to talk to the C2DM service.  There is at
    most one of these records."""
    role_email = db.StringProperty(indexed=False, default='my_email@gmail.com')
    passwd = db.StringProperty(indexed=False, default='my_password')
    token = db.TextProperty(indexed=False, default='')

class C2dmRegistration(db.Model):
    """Map from user to the C2DM registration id needed for the C2DM
    service to send messages to the registered device."""
    user_id = db.IntegerProperty(required=True)
    registration_id = db.StringProperty(indexed=False)

class RegisterHandler(MyRequestHandler.MyRequestHandler):
    def post(self):
        # Parse arguments.
        user_id = self.parseId('user_id')
        registration_id = self.parseStr('registration_id')

        # Create or update the device record.
        record = C2dmRegistration.gql('WHERE user_id = :1', user_id).get()
        if record == None:
            record = C2dmRegistration(user_id=user_id)
        record.registration_id = registration_id
        record.put()

class UnregisterHandler(MyRequestHandler.MyRequestHandler):
    def post(self):
        # Parse arguments.
        user_id = self.parseId('user_id')

        # Unregister this device.
        record = C2dmRegistration.gql('WHERE user_id = :1', user_id).get()
        if record != None:
            record.delete()

def getAuthToken():
    """Return an auth token associated with the role account.  Login to
    Google and store the auth token if needed."""
    token_record = C2dmAuthToken.all().get()
    if token_record == None:
        token_record = C2dmAuthToken()

    if len(token_record.token) > 0:
        return token_record.token

    form_fields = {
        'accountType' : 'GOOGLE',
        'Email' : token_record.role_email,
        'Passwd' : token_record.passwd,
        'service' : 'ac2dm',
        'source' : 'my_source_name',
    }
    headers = {
        'Content-Type' : 'application/x-www-form-urlencoded',
    }
    result = urlfetch.fetch(url='https://www.google.com/accounts/ClientLogin',
                            payload=urllib.urlencode(form_fields),
                            method=urlfetch.POST,
                            headers=headers)
    if result.status_code != 200:
        logging.warning('getAuthToken: client login http error %d' % result.status_code)
        return None

    for line in result.content.split('\n'):
        if line.startswith('Auth='):
            token_record.token = line[5:]

    if len(token_record.token) == 0:
        logging.warning('getAuthToken: no token')
        return None

    logging.info('getAuthToken allocated new token %s' % token_record.token)
    token_record.put()
    return token_record.token

def setAuthToken(token):
    """Update the auth token."""
    token_record = C2dmAuthToken.all().get()
    if token_record == None:
        token_record = C2dmAuthToken()
    token_record.token = token
    token_record.put()

def sendMessage(dst_user_id, message):
    """Send a message to the dst user's device using C2DM."""

    registration_record = C2dmRegistration.gql('WHERE user_id = :1', dst_user_id).get()
    if registration_record == None:
        logging.warning('SendMessage: no such dst_user_id %ld' % dst_user_id)
        return False

    # Use http and not https to avoid an invalid certificate error.
    # Since the request originates inside google hopefully it is
    # never snoop-able to the outside world, and does not contain
    # critically secure info (such as the role password).
    form_fields = {
        'registration_id' : registration_record.registration_id,
        'collapse_key' : '%d' % int(time.time() * 1000),
        'data.message' : message,
    }
    headers = {
        'Content-Type' : 'application/x-www-form-urlencoded',
        'Authorization': 'GoogleLogin auth=%s' % getAuthToken(),
    }
    result = urlfetch.fetch(url='http://android.apis.google.com/c2dm/send',
                            payload=urllib.urlencode(form_fields),
                            method=urlfetch.POST,
                            headers=headers)
    if result.status_code != 200:
        logging.warning('sendMessage: http error %d' % result.status_code)
        return None
    if 'Update-Client-Auth' in result.headers:
        logging.info('updating auth token')
        setAuthToken(result.headers['Update-Client-Auth'])
    return True

def main():    
    application = webapp.WSGIApplication([
        ('/c2dm/register', RegisterHandler),
        ('/c2dm/unregister', UnregisterHandler),
       ], debug=True)
    wsgiref.handlers.CGIHandler().run(application)

if __name__ == '__main__':
    main()

Your Android app should invoke the /c2dm/register and /c2dm/unregister methods to set and clear the devices c2dm token with the backend. Other backend code should call sendMessage to ask Google to relay the message to a device.

This code includes your gmail password. I use a throwaway gmail address for my c2dm needs and actually set the password via direct datastore manipulation instead of having it in plaintext in code. Even still if anyone knows a better way to manage authentication I'd love to hear about it.

I hope this helps!

Shereef

Let me start by saying your IDE setup is not the problem here, and this process doesn't even need a server(for now) with app engine which is what adds the Google option under file > New > Project

The android device contacts google C2DM servers to get the registration id if the process succeeds Google replies with the registration id, which later you can send to your server, for now we will be trying to get the process to work and have the registration id on the device, then later you could handle that.

As i understood, you created an Android Project, took the Google classes which they used in their chrome to phone example, which vogella.de provided in their tutorial

after doing so in your startup activity you called the method C2DMMessaging.register(this, "example@gmail.com);

there are a number of things that can be wrong here: p.s. these are ordered according to their likeliness to occur by my standards, also i have excluded some cases that appear not to apply to the snippets you mentioned.

  1. you have not registered you role email account with google.
    Please head over to http://code.google.com/android/c2dm/signup.html and do the following accept the license after reading it carefully
    fill all the required information as accurately as possible, and pay attention to the following fields:
    "Package name of your Android app" and "Role (sender) account email"
    this role email is the one you will be using in the C2DMMessaging.register method and your application's server later on
  2. You are testing on an android emulator that is not correctly configured for this test
    For your android emulator to be configured for this task the following should be done:
    Create a new AVD (Android Virtual Device) by clicking the menu Window > Android SDK and AVD Manager > Virtual Devices > New
    choose the target Google APIs (Google Inc.) - API Level 8 ( if this option is not available please download it from Available packages > Third Party Add-ons > Google Inc. )
    fill the rest as you please
    start the new AVD and navigate to Settings > Accounts & Sync > add account > Google > any google account.
    repeat your test
  3. Before i can fill in the other cases i need to see the rest of the Google classes you are using and the Service C2DMReceiver you created


i can't comment due to being a low reputation user

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!