问题
Im developing a C2DM Messaging application. In that i order to receive the registration id im using the C2DMBroadcastReceiver
, C2DMBaseReceiver
and C2DMMessaging
class. I will be C2DMReceiver
in my package which extends the C2DMBaseReceiver
.
Here is my code snippet
C2DMMessaging.java
package com.google.android.c2dm;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.util.Log;
public class C2DMessaging {
public static final String EXTRA_SENDER = "sender";
public static final String EXTRA_APPLICATION_PENDING_INTENT = "app";
public static final String REQUEST_UNREGISTRATION_INTENT = "com.google.android.c2dm.intent.UNREGISTER";
public static final String REQUEST_REGISTRATION_INTENT = "com.google.android.c2dm.intent.REGISTER";
public static final String LAST_REGISTRATION_CHANGE = "last_registration_change";
public static final String BACKOFF = "backoff";
public static final String GSF_PACKAGE = "com.google.android.gsf";
// package
static final String PREFERENCE = "com.google.android.c2dm";
private static final long DEFAULT_BACKOFF = 30000;
/**
* Initiate c2d messaging registration for the current application
*/
public static void register(Context context,
String senderId) {
Intent registrationIntent = new Intent(REQUEST_REGISTRATION_INTENT);
registrationIntent.setPackage(GSF_PACKAGE);
registrationIntent.putExtra(EXTRA_APPLICATION_PENDING_INTENT,
PendingIntent.getBroadcast(context, 0, new Intent(), 0));
registrationIntent.putExtra(EXTRA_SENDER, senderId);
context.startService(registrationIntent);
Log.e("C2DM Services","Service Started");
}
/**
* Unregister the application. New messages will be blocked by server.
*/
public static void unregister(Context context) {
Intent regIntent = new Intent(REQUEST_UNREGISTRATION_INTENT);
regIntent.setPackage(GSF_PACKAGE);
regIntent.putExtra(EXTRA_APPLICATION_PENDING_INTENT, PendingIntent.getBroadcast(context,
0, new Intent(), 0));
context.startService(regIntent);
Log.e("C2DM Services","unregister");
}
/**
* Return the current registration id.
*
* If result is empty, the registration has failed.
*
* @return registration id, or empty string if the registration is not complete.
*/
public static String getRegistrationId(Context context) {
final SharedPreferences prefs = context.getSharedPreferences(
PREFERENCE,
Context.MODE_PRIVATE);
String registrationId = prefs.getString("dm_registration", "");
Log.e("C2DM Services","get registration id");
return registrationId;
}
public static long getLastRegistrationChange(Context context) {
final SharedPreferences prefs = context.getSharedPreferences(
PREFERENCE,
Context.MODE_PRIVATE);
Log.e("C2DM Services","getlastregchange");
return prefs.getLong(LAST_REGISTRATION_CHANGE, 0);
}
static long getBackoff(Context context) {
final SharedPreferences prefs = context.getSharedPreferences(
PREFERENCE,
Context.MODE_PRIVATE);
Log.e("C2DM Services","getbackoff");
return prefs.getLong(BACKOFF, DEFAULT_BACKOFF);
}
static void setBackoff(Context context, long backoff) {
final SharedPreferences prefs = context.getSharedPreferences(
PREFERENCE,
Context.MODE_PRIVATE);
Editor editor = prefs.edit();
editor.putLong(BACKOFF, backoff);
editor.commit();
Log.e("C2DM Services","setbackoff");
}
// package
static void clearRegistrationId(Context context) {
final SharedPreferences prefs = context.getSharedPreferences(
PREFERENCE,
Context.MODE_PRIVATE);
Editor editor = prefs.edit();
editor.putString("dm_registration", "");
editor.putLong(LAST_REGISTRATION_CHANGE, System.currentTimeMillis());
editor.commit();
Log.e("C2DM Services","clearregid");
}
// package
static void setRegistrationId(Context context, String registrationId) {
final SharedPreferences prefs = context.getSharedPreferences(
PREFERENCE,
Context.MODE_PRIVATE);
Editor editor = prefs.edit();
editor.putString("dm_registration", registrationId);
editor.commit();
Log.e("C2DM Services","setregid");
}
}
C2DMBroadcastReceiver.java
package com.google.android.c2dm;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class C2DMBroadcastReceiver extends BroadcastReceiver {
@Override
public final void onReceive(Context context, Intent intent) {
// To keep things in one place.
Log.e("C2DM Broadcast receiver","onReceive");
C2DMBaseReceiver.runIntentInService(context, intent);
setResult(Activity.RESULT_OK, null /* data */, null /* extra */);
}
}
Manifest file
<permission android:name="com.sample.gt.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="com.sample.gt.permission.C2D_MESSAGE" />
<!-- Permissions -->
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<service android:name="com.sample.gt.c2dm.C2DMReceiver" />
<!--
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="com.sample.gt.c2dm" />
</intent-filter>
<!-- Receive the registration id -->
<intent-filter>
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="com.sample.gt.c2dm" />
</intent-filter>
</receiver>
C2DMMessaging.java
public class C2DMessaging {
public static final String EXTRA_SENDER = "sender";
public static final String EXTRA_APPLICATION_PENDING_INTENT = "app";
public static final String REQUEST_UNREGISTRATION_INTENT = "com.google.android.c2dm.intent.UNREGISTER";
public static final String REQUEST_REGISTRATION_INTENT = "com.google.android.c2dm.intent.REGISTER";
public static final String LAST_REGISTRATION_CHANGE = "last_registration_change";
public static final String BACKOFF = "backoff";
public static final String GSF_PACKAGE = "com.google.android.gsf";
// package
static final String PREFERENCE = "com.google.android.c2dm";
private static final long DEFAULT_BACKOFF = 30000;
/**
* Initiate c2d messaging registration for the current application
*/
public static void register(Context context,
String senderId) {
Intent registrationIntent = new Intent(REQUEST_REGISTRATION_INTENT);
registrationIntent.setPackage(GSF_PACKAGE);
registrationIntent.putExtra(EXTRA_APPLICATION_PENDING_INTENT,
PendingIntent.getBroadcast(context, 0, new Intent(), 0));
registrationIntent.putExtra(EXTRA_SENDER, senderId);
context.startService(registrationIntent);
Log.e("C2DM Services","Service Started");
}
..........
}
Now my problem is,
I calling the Register of the C2DMMessaging from my activity by passing the context, The service is created in the C2DMMessaging, After that im not receiving anything in the C2DMBroadcastReceiver's onReceive().
This is the code i got from vogille.de. This is working fine if i use this as such but when im using this in my application this problem is coming.
I have gone thro' some post n googled something in which i found that the problem may be in manifest
file.
i don know where im wrong. Can anyone help on this?
回答1:
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>
回答2:
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!
回答3:
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.
- 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 - 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 - 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
来源:https://stackoverflow.com/questions/6705904/c2dmbroadcastreceivers-onreceive-is-not-executing-for-registration