3

I'm using GCM to get notified when an image is posted, and then I download and process it:

public class GcmBroadcastReceiver extends WakefulBroadcastReceiver
{
    @Override
    public void onReceive(Context context, Intent intent)
    {
        DataUtils.log("In GcmBroadcastReceiver! threadname is " + Thread.currentThread().getName());

        // Explicitly specify that GcmIntentService will handle the intent.
        ComponentName comp = new ComponentName(context.getPackageName(), GcmIntentService.class.getName());
        // Start the service, keeping the device awake while it is launching.
        startWakefulService(context, (intent.setComponent(comp)));
        setResultCode(Activity.RESULT_OK);
    }
}

This is the beginning of my GcmIntentService:

public class GcmIntentService extends IntentService
{
    public static final int NOTIFICATION_ID = 1;

    public GcmIntentService()
    {
        super("GcmIntentService");
    }


    @Override
    protected void onHandleIntent(Intent intent)
    {

        DataUtils.log("In GcmIntentService onHandleIntent(), threadname is " + Thread.currentThread().getName());

        Bundle extras = intent.getExtras();
        GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
        // The getMessageType() intent parameter must be the intent you received in your BroadcastReceiver.
        String messageType = gcm.getMessageType(intent);

        if (!extras.isEmpty()) // has effect of unparcelling Bundle
        {
            /*
             * Filter messages based on message type. Since it is likely that GCM will be
             * extended in the future with new message types, just ignore any message types you're
             * not interested in, or that you don't recognize.
             */
            if (GoogleCloudMessaging.MESSAGE_TYPE_SEND_ERROR.equals(messageType))
            {
                DataUtils.log("In GcmIntentService - Send error: " + extras.toString());
            } else if (GoogleCloudMessaging.MESSAGE_TYPE_DELETED.equals(messageType))
            {
                DataUtils.log("In GcmIntentService - Deleted messages on server: " + extras.toString());
            // If it's a regular GCM message, do some work.
            } else if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType))
            {

                String notificationType = extras.getString(MyAppApi.GCM_MSG_TYPE_KEY);

                if(DataUtils.isEmpty(notificationType)) {

                    DataUtils.log("In GcmIntentService - notificationType is empty!");

                } else if(notificationType.equalsIgnoreCase(MyAppApi.GCM_IS_NEW_WALLPAPER)) {

                    //We're about to receive a new image!
                    DataUtils.log("In GcmIntentService - Receiving a new image!");
                    processNewWallpaper();

                } else if(notificationType.equalsIgnoreCase(MyAppApi.GCM_IS_FRIEND_NOTIFICATION)) {

                    //We're about to receive a friend notification
                    DataUtils.log("In GcmIntentService - Receiving a friend notification!");
                    processFriendNotification();

                } else {
                    //Unknown
                    DataUtils.log("In GcmIntentService - Receiving unknown message type! " + notificationType);
                }

            } else {

                DataUtils.log("In GcmIntentService - Unknown GCM message: " + extras.toString());
            }
        }

        //Release the wake lock provided by the WakefulBroadcastReceiver.
        GcmBroadcastReceiver.completeWakefulIntent(intent);

    }
}

It seems that randomly the service will die. From the log:

01-13 20:00:44.436: I/ActivityManager(375): Process com.grakk.android (pid 23227) has died.
01-13 20:00:44.444: W/ActivityManager(375): Scheduling restart of crashed service com.grakk.android/.GcmIntentService in 11426ms

What the code does when it receives a GCM message is to download an image, then it shows the user a notification (this is similar to a normal chat app).

A tester told me that once he received an image but didn't get the notification, which means that the service itself is started and does part of the work, but doesn't complete it.

The notification code is run in processNewWallpaper(), along with the download and processing of the image. Here's the code:

...

if(senderContact == null) {
    sendNotification(null, message, true);
} else {
    sendNotification(senderContact.getName(), message.trim(), false);
}

...

Notification method:

...

// Put the message into a notification and post it. This is just one simple example
// of what you might choose to do with a GCM message.
@SuppressWarnings("deprecation")
@TargetApi(16)
private void sendNotification(String name, String message, boolean isAnonymous)
{
    Context context = GcmIntentService.this;
    NotificationManager mNotificationManager = (NotificationManager)this.getSystemService(Context.NOTIFICATION_SERVICE);

    PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, ContactsActivity.class), 0);

    Notification.Builder mBuilder = new Notification.Builder(this)
        .setSmallIcon(R.drawable.ic_launcher)
        .setContentTitle(context.getString(R.string.app_name));

    String textToShow = null;
    if(DataUtils.isEmpty(message))
    {
        if(isAnonymous) {
            textToShow = context.getString(R.string.notification_text_anonymous);
        } else {
            textToShow = String.format(getResources().getString(R.string.notification_text_friend), name);
        }
    } else {
        textToShow = message;
    }

    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
        mBuilder.setStyle(new Notification.BigTextStyle().bigText(textToShow));
    }

    mBuilder.setContentText(textToShow);
    mBuilder.setAutoCancel(true);

    Uri alarmSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
    mBuilder.setSound(alarmSound);
    mBuilder.setContentIntent(contentIntent);

    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
        mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
    } else {
        mNotificationManager.notify(NOTIFICATION_ID, mBuilder.getNotification());
    }
}

I am able to reproduce this by sending myself an image, and then pressing the Android back button repeatedly until I am no longer in the app. I can follow the log messages that show that the image is downloaded, however it dies before the notification is shown.

This doesn't always happen. Sometimes the notification is shown, sometimes it's not.

I'm not sure what are probable causes, nor how to debug this. Any tips?

Sandy
  • 2,572
  • 7
  • 40
  • 61
  • Have you tried creating a distinct, new `Intent` instance in order to call the service, instead of modifying the one passed to the `GcmBroadcastReceiver`? – matiash Jan 20 '15 at 20:16
  • can you elaborate on whats happening in `//lots of work here`, esp the notification builder part – Pararth Jan 21 '15 at 13:17
  • After you have done lots of work are you releasing the wake lock by GcmReceiver.completeWakefulIntent(intent);? – Dhir Pratap Jan 22 '15 at 06:53
  • Updated code! @DhirPratap Yes, I'm releasing the wake lock (see update). user2450263 It's basically downloading an image and resizing according to screen size. – Sandy Jan 22 '15 at 20:18

3 Answers3

1

Have you called the OnCreate() in the GcmIntentService class?

Some sample code below:

public class GcmIntentService extends IntentService {

    String mes;
    private Handler mHandler;

    public GcmIntentService() {
        super("GcmIntentService");
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mHandler = new Handler();
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        Bundle extras = intent.getExtras();

        GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);

        String messageType = gcm.getMessageType(intent);
        mes = extras.getString("title");
        showToast();
        Log.i("GCM", "Recevied: (" + messageType + ") " + extras.getString("title"));

        GcmReceiver.completeWakefulIntent(intent);
    }

    public void showToast() {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(getApplicationContext(), mes, Toast.LENGTH_LONG).show();
            }
        });
    }
}

EDIT: Add useful youtube tutorial for GCM here.

bjiang
  • 6,068
  • 2
  • 22
  • 35
  • I don't, however it would only contain `super.onCreate();` . Is it still needed? – Sandy Jan 16 '15 at 12:44
  • I added the following but I'm still getting the same message. ` @Override public void onCreate() { super.onCreate(); }` – Sandy Jan 16 '15 at 12:57
  • I was not initializing mHandler = new Handler();, thanks – saqibahmad May 07 '15 at 21:11
  • @saqibahmad Thanks, I just added youtube tutorial for GCM [here](https://www.youtube.com/watch?v=Vd8bIkE29U4). – bjiang May 07 '15 at 21:27
  • @bjiang, i watch your tutorial, and its perfect, can you guide me, i want that my notification stay in notice are till user click on it. – saqibahmad May 07 '15 at 21:32
  • @saqibahmad you can refer to my another answer [here](http://stackoverflow.com/a/27538616/4186942). or just search SO, it would be helpful. – bjiang May 07 '15 at 21:37
  • @bjiang do we have to add this code in onHandleIntent? – saqibahmad May 07 '15 at 21:51
  • @saqibahmad Check out official doc [here](https://developer.android.com/google/gcm/client.html#sample-receive), search for `onHandleIntent`. BTW, google will release tons of Android courses including `GCM` on Udacity [here](https://www.udacity.com/courses/android) before google I/O I think. – bjiang May 07 '15 at 21:55
  • @bjiang Thanks thats i am looking for, i am a web developer and recently start working on mobile apps. thanks for guide. it helps – saqibahmad May 08 '15 at 13:05
0

Sorry that I'm using answer (I can't comment yet).

I would try extracting the call to sendNotification from processNewWallpaper to right after processNewWallpaper(). If that didn't work you should post your code in processNewWallpaper(). My guess is that in some cases your code crashes inside processNewWallpaper and skips the sendNotification but since its being handled it wouldn't throw anything.

Also I have noticed that apps act differently if they'v been open in background or completely closed (use running apps key and close your app there). If you can consistently reproduce the problem it will be easier to solve it.

Kayvan N
  • 8,108
  • 6
  • 29
  • 38
  • Hi @kayvan. What do you mean by "since its being handled"? There are parts of the code inside `try {} catch` structures, however if an exception is encountered I log that to the logcat output. None of these warnings are being displayed. – Sandy Jan 23 '15 at 01:50
  • By being handled I meant same as try and catch and I thought of because one part of you code is working and the other part is not. Try moving your code around a bit like push the notification first and then try processing the image. – Kayvan N Jan 23 '15 at 09:23
  • I log any time an exception is encountered and caught, ad there are no messages in the log. I can move the notification, however I could have the issue that the notification occurs but the image processing fails. I would prefer to save the image and not notify than the other way around. – Sandy Jan 23 '15 at 14:31
  • Makes sense, but if moving notification resolved the issue, you will know that exactly what is causing the issue. And then you can fix and move it back ;) – Kayvan N Jan 23 '15 at 17:36
0

Is that all the logcat you have? Any exceptions or stack traces from the "crashed" service?

However, an idea, are you downloading images asynchronously and in a callback creating the notification?

You are releasing the wake lock at the end of onHandleIntent which will be called before any async code is executed. Releasing the wake lock will kill the service if the screen is off.

What you would need to do is conditionally release the wake lock in onHandleIntent only if no async work needs to be done. And in the callback for any async work release the wake lock. Just make sure there's no execution path that doesn't release the wake lock!

Hope that's it!

darnmason
  • 2,672
  • 1
  • 17
  • 23
  • Hi @darnmason. Yes, this is all that's shown on the logcat. No exception notices or similar. Since onHandleIntent() in not run in the UI thread, I'm not creating any additional threads to download or parse the image, so everything should be performed sequentially and the wake lock released at the end. – Sandy Jan 23 '15 at 01:48
  • Ah cool, strange one so, must be some way to get at the exception that's killing the service. Have you tried debugging and breaking on exceptions? – darnmason Jan 23 '15 at 01:57