Stop IntentService from inside my application? - android

I'm building some kind of chat program that uses GCM to notify the user that a message is available. I followed the gcm client example using a WakefulBroadcastReceiver and IntentService and everything works as expected.
The BroadcastReceiver:
public class GcmBroadcastReceiver extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// 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);
}
}
and the IntentService:
public class GcmIntentService extends IntentService {
public static final int NOTIFICATION_ID = 1;
private NotificationManager mNotificationManager;
public GcmIntentService() {
super("GcmIntentService");
}
public static final String TAG = "IOAN";
#Override
protected void onHandleIntent(Intent intent) {
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
if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType)) {
// Post notification of received message.
sendNotification(extras.getString("sender"), extras.getString("message"));
Log.i(TAG, "Received: " + extras.toString());
}
}
// Release the wake lock provided by the WakefulBroadcastReceiver.
GcmBroadcastReceiver.completeWakefulIntent(intent);
}
//... more stuff
}
So the IntentService sends a notification to my application.
Now, when the user closes my application (back button -> are you sure you want to exit? -> yes), I want to also have a check box "do you still want to receive messages?" (or something like that), and if the user chooses not to receive messages, I want to stop the service that handles the gcm message and restart it when the app runs again.
How do I do that?
Edit: I tried stopService(new Intent(MainActivity.this,GcmIntentService.class)); but I still get the notifications.
Edit 2: Seems like a boolean value in SharedPreferences works fine... any better solution?

Related

How can I know the application state of an Android App to display or not a notification?

I am developping an instant messaging application that uses Google Cloud Messaging. I send a notification when a user receive a message from a contact. Everything works properly. I set up the GCMBroadcastReceiver and the GCMMessageHandler. The problem is : the notification is always displaying even if the user is currently chatting with his contact. How can I know the application state in the GCMMessageHandler to not display the notification ?
Here is the GCMBroadcastReceiver :
public class GcmBroadcastReceiver extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// Explicitly specify that GcmMessageHandler will handle the intent.
ComponentName comp = new ComponentName(context.getPackageName(),
GcmMessageHandler.class.getName());
// Start the service, keeping the device awake while it is launching.
startWakefulService(context, (intent.setComponent(comp)));
setResultCode(Activity.RESULT_OK);
}
}
Here is the GCMMessageHandler :
protected void onHandleIntent(Intent intent) {
Bundle extras = intent.getExtras();
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
String messageType = gcm.getMessageType(intent);
mes = extras.getString("message");
if(IM_NOT_IN_THE_CHAT_ACTIVITY)
{
showNotification(mes);
}
What I'm struggling to get is that flag : IM_NOT_IN_THE_CHAT_ACTIVITY.
How can I know this in the IntentService ?
Doing this would do the trick I guess :
ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
List< ActivityManager.RunningTaskInfo > taskInfo = am.getRunningTasks(1);
String activity_name = taskInfo.get(0).topActivity.getClassName();
if(activity_name != ACTIVITY_NAME)
showNotification(mes);

GcmBroadcastReceiver / GcmIntentService dies

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?
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.
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.
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!

BroadcastReceiver in IntentService in WakefulBroadcastReceiver does not always work

So, I have this problem.
I'm using a dependency project that is some kind of GCM notification parser. It's a bit poorly written, however I'm forced to use it, becase of work related reasons. Anyways:
The main service (that extends IntentService) is launched with WakefulBroadcastReceiver.
After it receives message from GCM I does some magic and sends it to the main App using broadcast.
In main app I'm constantly running service with another BroadcastReceiver that catches messages and saves everything in database etc.
Why is it so complicated? Firstly - originally it was someone else's project and now I'm trying to fix bugs. Secondly - I have no access from dependency to the main application project so I pass messages with broadcasts.
And now, the fun part. I need to filter whether I want to show notification or not. While sending a message to my main AppService I check it with the history of previous messages and then I decide if I need to show this message to User or not. However, no matter what my decision is, my dependency still shows my notification.
So I added yet another broadcast, when after successful validation I launch in my dependency notification building method.
Here is the code:
My WakefulBroadcastReceiver:
public class GcmBroadcastReceiver extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
ComponentName comp = new ComponentName(context.getPackageName(), PushService.class.getName());
startWakefulService(context, (intent.setComponent(comp)));
setResultCode(Activity.RESULT_OK);
}
}
Here is my Depencency service
public NotificationCheckerReceiver notificationCheckerReceiver;
...
#Override
protected void onHandleIntent(Intent intent) {
Bundle extras = intent.getExtras();
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
String messageType = gcm.getMessageType(intent);
if (!extras.isEmpty()) {
if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType)) {
//Launch my "approval" receiving broadcast
launchBroadcastReceiver(extras, intent);
//send broadcast to main app with the message we will parse etc.
sendSmsBroadcast(...));
}
}
}
#Override
public void onDestroy() {
unregisterReceiver(notificationCheckerReceiver);
super.onDestroy();
}
//Launch to build notification
public void showNotification(Bundle extras){
...
//Basic notification builder
}
//Receive broadcast from DB if notification was already in the DB
private void launchBroadcastReceiver(Bundle extras, Intent intent){
Log.d(TAG, "Broadcast receiver loaded");
notificationCheckerReceiver = new NotificationCheckerReceiver(new NotiFlag() {
#Override
public void onReceiveApproval(Boolean flag, Intent intent, Bundle extras) {
Log.d(TAG, "Approved notification show");
showNotification(extras);
JustPushGcmBroadcastReceiver.completeWakefulIntent(intent);
}
}, intent, extras);
registerReceiver(notificationCheckerReceiver, new IntentFilter(notificationCheckerReceiver.INTENT_EVENT_NAME));
}
public void sendSmsBroadcast(String message, boolean isAppOnScreen){
...
//This works
}
}
and my "faulty" receiver:
public class NotificationCheckerReceiver extends BroadcastReceiver{
private final String TAG = getClass().getSimpleName();
public static final String INTENT_EVENT_NAME = "NOTIFLAG";
public static final String INTENT_FLAG_KEY = "FLAG";
Intent intent;
Bundle extras;
NotiFlag nofiFlag;
public NotificationCheckerReceiver(NotiFlag nofiFlag, Intent intent, Bundle extras){
Log.d(TAG, "Launched constructor NotificationChecker");
this.nofiFlag = nofiFlag;
this.intent = intent;
this.extras = extras;
}
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Launched onReceive");
Boolean bool = intent.getExtras().getBoolean(INTENT_FLAG_KEY);
Log.d(TAG, "___________Broadcast receiver got something and it is intent: "+bool);
if (bool != false) {
nofiFlag.onReceiveApproval(bool, this.intent, this.extras);
}
}
}
and lastly, what I'm sending from my main service:
public void sendNotificationCheckerBroadcast(Boolean message){
Intent flag = new Intent(NotificationCheckerReceiver.INTENT_EVENT_NAME);
flag.putExtra(NotificationCheckerReceiver.INTENT_FLAG_KEY, message);
DvLogs.d(TAG, "__________Sending intent: "+message);
sendBroadcast(flag);
}
What happens is that eveything to the point where I launch "sendNotificationCheckerBroadcast()". I get that I'm sending some kind of boolean... and that's it.
The funny part is: it SOMETIMES works.
I don't know why, but when for some reason it launches - everything is awesome.
EDIT:
When it works, because sometimes it does, I have this error:
01-15 11:20:22.204 3234-3234/pl.digitalvirgo.lafarge E/ActivityThread﹕ Service com.example.name.PushService has leaked IntentReceiver com.example.name.NotificationCheckerReceiver#43042b50 that was originally registered here. Are you missing a call to unregisterReceiver()?
android.app.IntentReceiverLeaked: Service com.example.name.PushService has leaked IntentReceiver com.example.name.NotificationCheckerReceiver#43042b50 that was originally registered here. Are you missing a call to unregisterReceiver()?
at android.app.LoadedApk$ReceiverDispatcher.<init>(LoadedApk.java:814)
at android.app.LoadedApk.getReceiverDispatcher(LoadedApk.java:610)
at android.app.ContextImpl.registerReceiverInternal(ContextImpl.java:1772)
at android.app.ContextImpl.registerReceiver(ContextImpl.java:1752)
at android.app.ContextImpl.registerReceiver(ContextImpl.java:1746)
at android.content.ContextWrapper.registerReceiver(ContextWrapper.java:479)
at com.example.name.PushService.launchBroadcastReceiver(Unknown Source)
at com.example.name.PushService.onHandleIntent(Unknown Source)
at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:65)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:157)
at android.os.HandlerThread.run(HandlerThread.java:61)
Maybe it's somehow related?
I know that I should unRegister this Receiver ... somewhere. Tried onStop, but as we can see - no success.
Edit2:
Weird.
I believe, that the problem is in onStop() method. Probably it's called too early (?) so my Receiver has no chance to work. When I launch app without unRegister everything works. Of course I get bug above, but still... it's something.
Any ideas guys?
Well. The problem was inside the idea of IntentService.
intentService kills itself after onHandleIntent() method.
So the solution for this problem is to change IntentService into Service remembering to handle stopping this thing.

GCM Android Push Notification: send OK but not delivered

I'm trying to send push notification to android devices. I have not problem to register the ID of the emulator or the devices, but it's impossibile to receive messages also if the send status is 200 OK.
I also try to look to firewall settings, but it's already turned off.
I tried also to use PushBots service from web: same thing. Device registered correctly, message sent, but not delivered to emulator or devices.
It was my mistake. I didn't notice the app was giving me exception when sending push from web for an error in the manifest Receiver.
Implemented the NotificationManager now I receive the push messagge into device.
public class GcmBroadcastReceiver extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// Explicitly specify that GcmMessageHandler will handle the intent.
ComponentName comp = new ComponentName(context.getPackageName(),
GcmMessageHandler.class.getName());
// Start the service, keeping the device awake while it is launching.
startWakefulService(context, (intent.setComponent(comp)));
setResultCode(Activity.RESULT_OK);
}
}
#Override
protected void onHandleIntent(Intent intent) {
Bundle extras = intent.getExtras();
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
String messageType = gcm.getMessageType(intent);
mes = extras.getString("title");
sendNotification(mes);
sendAlert(mes);
Log.i("GCM", "Received : (" +messageType+") "+extras.getString("title"));
GcmBroadcastReceiver.completeWakefulIntent(intent);
}

Android device performance drop when display is off

I am currently developing an app that uses GCM to send a notification to a number of devices that starts a service to start polling a RabbitMQ message queue and do some processing on each message received. I am using a WakefulBroadcastReceiver to keep the device awake once the service has started but I am seeing a significant performance hit if the device(s) screens are off as opposed to if I keep them on. I have also unchecked the 'Wi-Fi Optimization' option and made sure that 'Keep Wi-Fi on during sleep' is set to Always.
Is there something else I am missing? The service doesn't stop completely, it just slows down whenever the screen goes off. Whether I press the standby button or I let it go off after the set time. The devices are running a mix of Jelly Bean (4.2.2) and Kit Kat (4.4.2)
Thanks in advance!
Added code snippets below. The Worker class mentioned just calls another class that performs some data processing:
public class GcmBroadcastReceiver extends WakefulBroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
// Explicitly specify that GcmIntentService will handle the intent.
ComponentName comp = new ComponentName(context.getPackageName(), GcmIntentServicece.class.getName());
// Start the service, keeping the device awake while it is launching.
startWakefulService(context, (intent.setComponent(comp)));
setResultCode(Activity.RESULT_OK);
}
}
Intent Service 'onHandleIntent' method
#Override
protected void onHandleIntent(Intent intent)
{
Bundle extras = intent.getExtras();
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
String messageType = gcm.getMessageType(intent);
if (!extras.isEmpty())
{
if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType))
{
String message = extras.getString("message");
if (message.equals(START_PROCESSING))
{
Worker worker = new Worker(this, queueServerUrl, Integer.parseInt(queueServerPort), queueName, fileServerUrl);
Map<String, Integer> results = worker.StartWorking();
// Update the results
update(results, jobId);
}
}
}
// Release the wake lock provided by the WakefulBroadcastReceiver.
GcmBroadcastReceiver.completeWakefulIntent(intent);
}

Categories

Resources