android memory leak in notification service - android

I have a service which creates a notification and then updates it with certain information periodically. After about 12 mins or so the phone crashes and reboots, I believe it is caused by a memory leak in the following code to do with how I am updating the notification, could someone please check/advise me if this is the case and what I am doing wrong.
onCreate:
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
createNotification:
private void createNotification() {
Intent contentIntent = new Intent(this,MainScreen.class);
contentIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent appIntent =PendingIntent.getActivity(this,0, contentIntent, 0);
contentView = new RemoteViews(getPackageName(), R.layout.notification);
contentView.setImageViewResource(R.id.image, R.drawable.icon);
contentView.setTextViewText(R.id.text, "");
notification = new Notification();
notification.when=System.currentTimeMillis();
notification.contentView = contentView;
notification.contentIntent = appIntent;
}
updateNotification:
private void updateNotification(String text){
contentView.setTextViewText(R.id.text, text);
mNotificationManager.notify(0, notification);
}
Thanks in advance.

I stumbled upon the same problem. Looks like that if you don't "cache" the RemoteView and Notification in the service, but re-create them from scratch in the "update" routine this problem disappears. Yes, I know it is not efficient, but at least the phone does not reboot from out of memory errors.

I had the very same problem. My solution is close to the one that #haimg said, but I do cache the notification (just the RemoteView is recreated). By doing so, the notification won't flash again if you are looking at it.
Example:
public void createNotification(Context context){
Notification.Builder builder = new Notification.Builder(context);
// Set notification stuff...
// Build the notification
notification = builder.build();
}
public void updateNotification(){
notification.bigContentView = getBigContentView();
notification.contentView = getCompactContentView();
mNM.notify(NOTIFICATION_ID, notification);
}
And in the methods getBigContentView and getCompactContentView I return a new RemoteViews with the updated layout.

Related

How to process GCM notifications, launching activity per alert without erasing other alerts

I guess it is hard to explain just by reading the title of this question. I am coding an app that gets ambient factors alerts (temperature, etc) sent by a known server to GCM, and then GCM sends it back to the phone. The whole GCM works well. The problem is when notifications arrive. It is thought to send notifications to the phone when an alert happens (a trigger). Then clicking on the alert launches the activity to display the alert. That is OK, but if there is 2 or more alerts on waiting to be clicked, it will only process one, ignoring the rest ("mensaje"). This is how my notification inside a class that extends extends GcmListenerService looks like.
public static void showAlerts(Context context, String mensaje)
{
Intent notificationIntent = new Intent(context.getApplicationContext(), MainActivity.class);
notificationIntent.putExtra("mensaje", mensaje);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
| Intent.FLAG_ACTIVITY_SINGLE_TOP);
Random r = new Random();
PendingIntent pendingIntent = PendingIntent.getActivity(context, r.nextInt(),
notificationIntent, 0);
NotificationManager notificationManager = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new NotificationCompat.Builder(context)
.setContentTitle(context.getString(R.string.app_name))
.setContentText("Nueva alerta recibida")
.setSmallIcon(R.drawable.termometrorojo)
.setNumber(UtilidadesGCM.num_notificaciones++)
.setOnlyAlertOnce(true)
.setContentIntent(pendingIntent)
.setWhen(System.currentTimeMillis())
.setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE)
.setDefaults(Notification.DEFAULT_LIGHTS)
.setAutoCancel(true).build();
notificationManager.notify(0, notification);
}
Then in MainActivity, I have the code to process this, and open the activity to display the alert
private final BroadcastReceiver mHandleMessageReceiver = new BroadcastReceiver()
{
#Override
public void onReceive(Context context, Intent intent)
{
String nuevaAlerta = intent.getExtras().getString("mensaje");
procesaAlerta(nuevaAlerta);
//mDisplay.append(nuevaAlerta + "\n");
}
};
public void procesaAlerta (String alerta)
{
Intent intent = new Intent(this, Alertas.class);
intent.putExtra("mensaje" , alerta);
startActivity(intent);
}
The Alertas class will parse the message fine and display it in its activity, but will only do that once. If there are more than 2 alerts stacked to be read, it only process one. If there is one, it works ok. Sorry if I odn't explain better, but it hard not showing all the code.
Thanks!
Try with writing this line
notificationManager.notify( new Random().nextInt(), notification);
instead of
notificationManager.notify(0, notification);
Your notification id is every time same so your last notification only work. Every new notification id is replaced by id 0. So i use random id instead of fixed id 0. I think above code will work for you

Unable to cancel the notification from the notification tray when different layouts used for expand/collapse

I have a share option in the notification tray. So, I have used 2 custom views in my code.
1. Expandable Notification Layout
2. Normal Notification Layout
The UI is working fine. But, notifications are misbehaving. If I click the first notification or share first item in the notification, it works perfectly. But, If I click the last notification, then it opens the app but notification is not cleared from the notification tray. Also, the strange thing is, after clicking the notification,small icon which comes in the status bar disappears and now if i click the notification, it doesn't respond. I'm cancelling the notification. That's why second time, when i click it doesn't work. This was not happening when I used default builder layout for normal view.
Here is my code:
//setup a expanded notification layout
RemoteViews expandedView=new RemoteViews(context.getPackageName(), R.layout.custom_notification);
expandedView.setImageViewResource(R.id.image_logo, R.drawable.ic_launcher);
SimpleDateFormat dateFormat = new SimpleDateFormat("hh:mm aa");
String time = dateFormat.format(new Date(when));
expandedView.setTextViewText(R.id.current_time, time);
expandedView.setTextViewText(R.id.title, context.getResources().getString(R.string.app_name));
expandedView.setTextViewText(R.id.text, message);
expandedView.setTextViewCompoundDrawables(R.id.share, R.drawable.gcm_share, 0, 0, 0);
//set a normal notification layout
RemoteViews collapsedView=new RemoteViews(context.getPackageName(), R.layout.custom_notification_normal_layout);
collapsedView.setTextViewText(R.id.text, message);
notificationId = ((int) System.currentTimeMillis() % 1000000);
//register listenr for Share Icon
setBtnListeners(expandedView, requestID, message, context, notificationId);
`Bitmap largeIcon = ((BitmapDrawable) context.getResources().getDrawable(R.drawable.ic_launcher)).getBitmap()`;
//Construct notification Builder
NotificationCompat.Builder mNotifyBuilder = new NotificationCompat.Builder(context);
mNotifyBuilder
.setWhen(when)
.setSmallIcon(icon)
.setLargeIcon(largeIcon)
.setContentTitle(message)
.setStyle(new NotificationCompat.BigTextStyle().bigText(message));
notification = mNotifyBuilder.build();
//assign the vies to the notification builder
notification.bigContentView = expandedView;
notification.contentView = collapsedView;
//create pending Intent
Intent notificationIntent;
//notificationIntent = new Intent(context, Launcher.class);
notificationIntent = new Intent(context, SplashActivity.class);//Sritapana189
notificationIntent.putExtra(BundleKeys.NORMAL_PUSH, 1);
notificationIntent.putExtra(BundleKeys.DEFAULT_NOTI_NAV, nav);
notificationIntent.putExtra(BundleKeys.FROM_GCM, true);
notificationIntent.putExtra(ApplicationConstants.GCMKeys.GCM_NOTIFICATION_ID, notificationId);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent contentIntent = PendingIntent.getActivity(context, requestID, notificationIntent, 0); //Modified
notification.contentIntent = contentIntent;
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notificationManager.notify(notificationId, notification);
//Registering the share icon of notification tray
private static void setBtnListeners(RemoteViews notificationView, int requestId, String message, Context context, int notificationId) {
Intent shareBtnIntent = new Intent(context, GcmTransparentActivity.class);
shareBtnIntent.putExtra(ApplicationConstants.GCMKeys.BREAKING_NEWS, message);
shareBtnIntent.putExtra(ApplicationConstants.GCMKeys.GCM_NOTIFICATION_ID, notificationId);
PendingIntent pendingIntent = PendingIntent.getActivity(context, requestId, shareBtnIntent, 0);
notificationView.setOnClickPendingIntent(R.id.share, pendingIntent);
}
//SplashActivity recieving Pending Inent
//here I'm retrieving the payload data and saving it and then cancelling the notification
Utility.cancelNotification(gcmNotificationId, getApplicationContext());
public static void cancelNotification(int id, Context ctx) {
String ns = Context.NOTIFICATION_SERVICE;
NotificationManager nMgr = (NotificationManager) ctx.getSystemService(ns);
nMgr.cancel(id);
}
The only change i did in my code is I added custom layout for the normal view.After this change, it's misbehaving when last notification item is clicked. Is there anything I'm missing. please help me to sort out this strange issue.
try this,
problem may be because you are not getting the notification id properly
quick fix can be,
Notification nNotificationManager nMgr = (NotificationManager) ctx.getSystemService(ns);
nMgr.cancel(id);
replace this with ,
Notification nNotificationManager nMgr = (NotificationManager) ctx.getSystemService(ns);
nMgr.cancelAll();
I resolved my issue. The problem was with the RemoteViews. I had made them local and making them final solved my issue
final RemoteViews expandedView=new RemoteViews(context.getPackageName(), R.layout.custom_notification);
final RemoteViews collapsedView=new RemoteViews(context.getPackageName(), R.layout.custom_notification_normal_layout)

Transaction to a specific fragment if we receive a push notification while app is running

Is it possible not launch new activity if we receive a push notification while the app is running?
My activity works with fragments and I want to do transition to a determinate fragment when the notification is received. My activity have data that I need to show the fragments. The problem is that when I receive the push notification while the app is running the method onDestroy is called and here I clear the data and then the app crash because the data are null. How can I do to not create new activity when the app receive a push notification while is running? In case the app is running I want that if you click the notification do a transition fragment, not create again the activity.
Thanks in advance.
First of all, I think that you mean "notification" to be a "message", but not android.app.Notification class.
And second, I don't think it's a best practise to raise new GUI when receiving a message, which would interrupt the user interaction. For details, please refer to: http://developer.android.com/training/notify-user/index.html and http://developer.android.com/design/patterns/notifications.html.
At last, if you really wanna do what you stated in your thread, I wonder why the data used to generate the show-data fragment is held in the activity. Try holding the data in an android.app.IntentService object, and then generate transfer the data to new activity, and then use android.app.Fragment.setArguments method to transfer the data from activity to fragment.
I think that this code will help you. This which you need is PendingIntent, it make transaction to desired activity.
/**
* Issues a notification to inform the user that server has sent a message.
*/
private static void generateNotification(Context context, String title,
String message) {
//get the default notification sound URI
Uri uriNotificationSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
//make intent to the application. if the application is opened just maximize it.
Intent homeIntent = new Intent(context, 'your desired activity');
homeIntent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
PendingIntent contentIntent = PendingIntent.getActivity(context, 0,
homeIntent, 0);
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("eCommCongress")
.setContentText(message)
.setLights(Color.GREEN, 1500, 1500)
.setSound(uriNotificationSound)
.setAutoCancel(true)
.setContentIntent(contentIntent);
NotificationManager mNotificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
// mId allows you to update the notification later on.
mNotificationManager.notify(counter, mBuilder.build());
counter++;
}
It is perfectly possible and I do such a thing in one of my apps. First, you need to declare your activity as android:launchMode="singleTop",
Then, when you build you must configure your pending intent not to fire a new instance of your activity:
mNotificationManager = (NotificationManager)
this.getSystemService(Context.NOTIFICATION_SERVICE);
Intent intent = new Intent("YOUR ACTION HERE");
intent.setClassName(this, MainActivity.class.getName());
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent resultPendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this);
mBuilder.setContentIntent(resultPendingIntent);
Notification notification = mBuilder.build();
notification.defaults |= Notification.DEFAULT_VIBRATE | Notification.DEFAULT_LIGHTS;
notification.flags |= Notification.FLAG_SHOW_LIGHTS | Notification.FLAG_AUTO_CANCEL;
mNotificationManager.notify(idNotificacion,notification);
Now all you have to do is to override your onNewIntent inside your Activity and do whatever you want with your fragment:
#Override
protected void onNewIntent(Intent intent) {
MiLog.i(getApplicationContext(),"IntentShit","new intent received");
MiLog.i(getApplicationContext(),"IntentShit","Action: "+intent.getAction());
if(intent.getAction()!=null && intent.getAction().equals("YOUR ACTION HERE"){
//DO your stuff here
}
}
You should also take a look at this page for more info:
http://www.intridea.com/blog/2011/6/16/android-understanding-activity-launchmode

Notifications bar lags when onGoing notification is on

My application has a notification in the notifications bar that shows the battery level. The notification works but when the checkbox in the preferences is clicked and the notification appears, i try to pull down the notifications bar and it lags. I don't know way but i think that it's because the notification check every second the battery level but i'm not sure.. I post the code:
#SuppressLint("NewApi")
private void checkPref(Intent intent){
this.registerReceiver(this.batteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
SharedPreferences myPref = PreferenceManager.getDefaultSharedPreferences(MainActivity.this);
boolean pref_opt1 = myPref.getBoolean("firstDependent", false);
int level= intent.getIntExtra(BatteryManager.EXTRA_LEVEL,-1);
if (pref_opt1){
NotificationManager notifi = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new Notification.Builder(getApplicationContext())
.setContentTitle("Battery Informations")
.setContentText("Battery level"+" "+level+"%")
.setSmallIcon(R.drawable.icon_small_not)
//.setLargeIcon(aBitmap)
.setTicker(level+"%")
.build();
notification.flags = Notification.FLAG_ONGOING_EVENT;
Intent i = new Intent(MainActivity.this, MainActivity.class);
PendingIntent penInt = PendingIntent.getActivity(getApplicationContext(), 0 , i , 0);
notifi.notify(215,notification);
} else {
NotificationManager notifi = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
notifi.cancel(215);
}
}
Maybe the problem is this one this.registerReceiver(this.batteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));. But i know only this way to update the notification when the battery level changes.. Some helps please?
Check whether 'if-case' code to show notification is getting executed repeatedly. If so try limiting show notification to once via a flag value and reset the flag during notifi.cancel().

How can I modify a textview from a non-activity class? Android

I want to modify a TextView from an alarmmanager class's onReceive method this class does not extend activity. The alarm sends an intent that creates a notification and I want to make it so clicking the notification will open a textview with a particular string that I get in this onReceive method.
RemoteViews contentView = new RemoteViews(context.getPackageName(),R.layout.notify_layout);
contentView.setTextViewText(R.id.notetext,my_message);
notif.contentView = contentView;
I tried to do this using the RemoteView above with the notification intent but it doesn't seem to work.
I know I can't access this using findViewById, but what approach should I take here?
This is from the code that handles the intent
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.notify_layout);
NotificationManager nm = (NotificationManager)
getSystemService(NOTIFICATION_SERVICE); nm.cancel(getIntent().getExtras().getInt("com.myapp.NotificationDisp"));
Then I have this whole notification thing here. I know that its deprecated and I should be using builder but I need this to be compatible back to gingerbread which it seems builder is not.
Intent i = new Intent("com.MyApp.NotificationDisp");
i.putExtra("com.MyApp.NotificationDisp", 1);
nm = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
#SuppressWarnings("deprecation")
Notification notif = new Notification(R.drawable.ic_launcher,
"Myapp", System.currentTimeMillis());
//
RemoteViews contentView = new RemoteViews(context.getPackageName(),
R.layout.notify_layout);
contentView.setTextViewText(R.id.notetext,
my_message);
notif.contentView = contentView;
PendingIntent contentIntent = PendingIntent.getActivity(context, 0,
i, 0);
notif.contentIntent = contentIntent;
//
notif.setLatestEventInfo(context, "Myapp", my_message, contentIntent);
notif.vibrate = new long[] { 100, 250, 100, 500};
nm.notify(1, notif);
So this brings up a notification with the correct text in my status bar but when I click on it it only shows a blank textview. The xml notify_layout should be setup fine.

Categories

Resources