C2DM Broadcast Intent giving an ANR. How to avoid it?
I am using the tutorial from C2DM Tutorial and when I run with this code in my app(my app has many threads and also I run this C2DMessaging.register(getApplicationContext(), SENDERS_EMAIL_ID); in one more thread.
But still even after I run this on a separate thread I get the below error message and an ANR pops up, thought I see that in my background my app started perfectly!!!!
The way I start the C2DM.register(...) is
new Thread() {
public void run() {
try{
C2DMessaging.register(getApplicationContext(), SENDERS_EMAIL_ID);
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
}
}.start();
The error log that I get when I do this is
E/ActivityManager( 162): ANR in com.example.myapp
E/ActivityManager( 162): Reason: Broadcast of Intent { act=com.google.android.c2dm.intent.REGISTRATION cat=[com.example.myapp] flg=0x10 cmp=com.example.myapp/.c2dm.C2DMBroadcastReceiver (has extras) }
E/ActivityManager( 162): Load: 0.63 / 0.24 / 0.32
E/ActivityManager( 162): CPU usage from 5001ms to 0ms ago:
E/ActivityManager( 162): 98% 29787/com.example.myapp: 98% user + 0.4% kernel / faults: 2 minor
E/ActivityManager( 162): 1% 162/system_server: 0.4% user + 0.6% kernel / faults: 93 minor
E/ActivityManager( 162): 0.8% 127/sdcard: 0% user + 0.8% kernel
E/ActivityManager( 162): 0.6% 128/adbd: 0% user + 0.6% kernel / faults: 182 minor
E/ActivityManager( 162): 0.6% 224/dhd_dpc: 0% user + 0.6% kernel
E/ActivityManager( 162): 0.2% 240/com.android.phone: 0.2% user + 0% kernel
E/ActivityManager( 162): 0.2% 29429/kworker/u:3: 0% user + 0.2% kernel
E/ActivityManager( 162): 54% TOTAL: 49% user + 1.9% kernel + 2.1% iowait + 0.1% softirq
E/ActivityManager( 162): CPU usage from 525ms to 1057ms later with 99% awake:
E/ActivityManager( 162): 98% 29787/com.example.myapp: 96% user + 1.8% kernel
E/ActivityManager( 162): 96% 29787/com.example.myapp: 96% user + 0% kernel
E/ActivityManager( 162): 5.6% 162/system_server: 0% user + 5.6% kernel
E/ActivityManager( 162): 5.6% 172/ActivityManager: 1.8% user + 3.7% kernel
E/ActivityManager( 162): 1.8% 170/SensorService: 1.8% user + 0% kernel
E/ActivityManager( 162): 2.7% 127/sdcard: 0% user + 2.7% kernel
E/ActivityManager( 162): 2.7% 128/adbd: 0% user + 2.7% kernel / faults: 156 minor
E/ActivityManager( 162): 2.7% 128/adbd: 0% user + 2.7% kernel
E/ActivityManager( 162): 1.3% 11683/adbd: 0% user + 1.3% kernel
E/ActivityManager( 162): 1.3% 224/dhd_dpc: 0% user + 1.3% kernel
Any pointers will be really appreciated!
Thanks.
The code for C2DMBroadcastReceiver is
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
/**
* Helper class to handle BroadcastReciver behavior.
* - can only run for a limited amount of time - it must start a real service
* for longer activity
* - must get the power lock, must make sure it's released when all done.
*
*/
public class C2DMBroadcastReceiver extends BroadcastReceiver {
@Override
public final void onReceive(Context context, Intent intent) {
// To keep things in one place.
C2DMBaseReceiver.runIntentInService(context, intent);
setResult(Activity.RESULT_OK, null /* data */, null /* extra */);
}
}
for the C2DMBaseReceiver it is
import java.io.IOException;
import android.app.AlarmManager;
import android.app.IntentService;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.PowerManager;
import android.util.Log;
/**
* Base class for C2D message receiver. Includes constants for the strings used
* in the protocol.
*/
public abstract class C2DMBaseReceiver extends IntentService {
private static final String C2DM_RETRY = "com.google.android.c2dm.intent.RETRY";
public static final String REGISTRATION_CALLBACK_INTENT = "com.google.android.c2dm.intent.REGISTRATION";
private static final String C2DM_INTENT = "com.google.android.c2dm.intent.RECEIVE";
private static final String TAG = "MyApp";
// Extras in the registration callback intents.
public static final String EXTRA_UNREGISTERED = "unregistered";
public static final String EXTRA_ERROR = "error";
public static final String EXTRA_REGISTRATION_ID = "registration_id";
public static final String ERR_SERVICE_NOT_AVAILABLE = "SERVICE_NOT_AVAILABLE";
public static final String ERR_ACCOUNT_MISSING = "ACCOUNT_MISSING";
public static final String ERR_AUTHENTICATION_FAILED = "AUTHENTICATION_FAILED";
public static final String ERR_TOO_MANY_REGISTRATIONS = "TOO_MANY_REGISTRATIONS";
public static final String ERR_INVALID_PARAMETERS = "INVALID_PARAMETERS";
public static final String ERR_INVALID_SENDER = "INVALID_SENDER";
public static final String ERR_PHONE_REGISTRATION_ERROR = "PHONE_REGISTRATION_ERROR";
private static final String WAKELOCK_KEY = "MyApp";
private static PowerManager.WakeLock mWakeLock;
private final String senderId;
/**
* The C2DMReceiver class must create a no-arg constructor and pass the
* sender id to be used for registration.
*/
public C2DMBaseReceiver(String senderId) {
// senderId is used as base name for threads, etc.
super(senderId);
this.senderId = senderId;
}
/**
* Called when a cloud message has been received.
*/
protected abstract void onMessage(Context context, Intent intent);
/**
* Called on registration error. Override to provide better error messages.
*
* This is called in the context of a Service - no dialog or UI.
*/
public abstract void onError(Context context, String errorId);
/**
* Called when a registration token has been received.
*/
public void onRegistered(Context context, String registrationId)
throws IOException {
// registrationId will also be saved
}
/**
* Called when the device has been unregistered.
*/
public void onUnregistered(Context context) {
}
@Override
public final void onHandleIntent(Intent intent) {
try {
Context context = getApplicationContext();
if开发者_StackOverflow (intent.getAction().equals(REGISTRATION_CALLBACK_INTENT)) {
handleRegistration(context, intent);
} else if (intent.getAction().equals(C2DM_INTENT)) {
onMessage(context, intent);
} else if (intent.getAction().equals(C2DM_RETRY)) {
C2DMessaging.register(context, senderId);
}
} finally {
// Release the power lock, so phone can get back to sleep.
// The lock is reference counted by default, so multiple
// messages are ok.
// If the onMessage() needs to spawn a thread or do something else,
// it should use it's own lock.
mWakeLock.release();
}
}
/**
* Called from the broadcast receiver. Will process the received intent,
* call handleMessage(), registered(), etc. in background threads, with a
* wake lock, while keeping the service alive.
*/
static void runIntentInService(Context context, Intent intent) {
if (mWakeLock == null) {
// This is called from BroadcastReceiver, there is no init.
PowerManager pm = (PowerManager) context
.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
WAKELOCK_KEY);
}
mWakeLock.acquire();
// Use a naming convention, similar with how permissions and intents are
// used. Alternatives are introspection or an ugly use of statics.
String receiver = context.getPackageName() + ".c2dm.MyC2dmReceiver";
intent.setClassName(context, receiver);
context.startService(intent);
}
private void handleRegistration(final Context context, Intent intent) {
final String registrationId = intent.getStringExtra(EXTRA_REGISTRATION_ID);
String error = intent.getStringExtra(EXTRA_ERROR);
String removed = intent.getStringExtra(EXTRA_UNREGISTERED);
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "dmControl: registrationId = " + registrationId
+ ", error = " + error + ", removed = " + removed);
}
if (removed != null) {
// Remember we are unregistered
C2DMessaging.clearRegistrationId(context);
onUnregistered(context);
return;
} else if (error != null) {
// we are not registered, can try again
C2DMessaging.clearRegistrationId(context);
// Registration failed
Log.e(TAG, "Registration error " + error);
onError(context, error);
if ("SERVICE_NOT_AVAILABLE".equals(error)) {
long backoffTimeMs = C2DMessaging.getBackoff(context);
Log.d(TAG, "Scheduling registration retry, backoff = "
+ backoffTimeMs);
Intent retryIntent = new Intent(C2DM_RETRY);
PendingIntent retryPIntent = PendingIntent
.getBroadcast(context, 0 /* requestCode */, retryIntent,
0 /* flags */);
AlarmManager am = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.ELAPSED_REALTIME, backoffTimeMs,
retryPIntent);
// Next retry should wait longer.
backoffTimeMs *= 2;
C2DMessaging.setBackoff(context, backoffTimeMs);
}
} else {
try {
onRegistered(context, registrationId);
C2DMessaging.setRegistrationId(context, registrationId);
} catch (IOException ex) {
Log.e(TAG, "Registration error " + ex.getMessage());
}
}
}
}
I think it's problem not with sending registration info to google but with receiving registration_id from google in C2DMBroadcastReceiver. Did you start Service in BroadcastReceiver for registrartion handling? Please add code of your C2DMBroadcastReceiver.
You should NOT use the class C2DMessaging and certainly not from within a thread (by the way don't use thread, use async tasks with a callback or an intent services).
Anyway since C2DM has been deprecated you should give a look at http://developer.android.com/guide/google/gcm/index.html
have fun :)
精彩评论