13

So I have a notification in my app. The notification has some text and an image which I fetch from a LruCache which I populate at the start. The problem is that on Kit-Kat devices (Moto E, XIomi) in my case, the notification stops updating if it has already updated a fixed number of times, lets say n (this n is usually fixed for each device, and is <10). This is my code for updating

            String title = CommonUtils.getTitleFromID(id, context);
            Bitmap bitmap = DataProvider.getInstance(context).diskLruImageCache.getBitmap(id + "");
            if (playbackPaused) {
                bigView.setImageViewResource(R.id.pause, R.drawable.pause_noti);
                smallView.setImageViewResource(R.id.pause1, R.drawable.pause_noti);
                remoteViews.setImageViewResource(R.id.pause2, R.drawable.pause_noti);
                playbackPaused = false;
                Log.d(TAG, "noti-false");
            }

            bigView.setTextViewText(R.id.title, title + "");
            bigView.setImageViewBitmap(R.id.img, bitmap);

            smallView.setTextViewText(R.id.title1, title);
            smallView.setImageViewBitmap(R.id.img1, bitmap);

            mNotificationManager.notify(NOTIFY_ID, notification);

I tried with a bit of debugging and all and I found that if I do not set the bitmap, (and just do
bitmap = null)
then everything works fine.

I don't understand why this is happening. This issue is android version specific since I already tested on Nexus 5 (Lollipop) and other Android 5 phones but there this doesn't happen. Does someone have any idea what the reason behind this might be?

I am not expecting an exact situation. Even some ideas in the right direction will be very helpful. Thanks !!

I am also adding the code for my DiskLruCache in case that might be needed,

public class DiskLruImageCache {

    private DiskLruCache mDiskCache;
    private Bitmap.CompressFormat mCompressFormat = Bitmap.CompressFormat.JPEG;
    private int mCompressQuality = 70;
    private static final int APP_VERSION = 1;
    private static final int VALUE_COUNT = 1;
    private static final String TAG = "DiskLruImageCache";

    public DiskLruImageCache( Context context,String uniqueName, int diskCacheSize,
                              Bitmap.CompressFormat compressFormat, int quality ) {
        try {
            final File diskCacheDir = getDiskCacheDir(context, uniqueName );
            mDiskCache = DiskLruCache.open( diskCacheDir, APP_VERSION, VALUE_COUNT, diskCacheSize );
            mCompressFormat = compressFormat;
            mCompressQuality = quality;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private boolean writeBitmapToFile( Bitmap bitmap, DiskLruCache.Editor editor )
            throws IOException {
        OutputStream out = null;
        try {
            out = new BufferedOutputStream( editor.newOutputStream( 0 ), Utils.IO_BUFFER_SIZE );
            return bitmap.compress( mCompressFormat, mCompressQuality, out );
        } finally {
            if ( out != null ) {
                out.close();
            }
        }
    }


    private boolean writeUriToFile( Uri bitmap, DiskLruCache.Editor editor )
            throws IOException, FileNotFoundException {
        OutputStream out = null;
        try {
            out = new BufferedOutputStream( editor.newOutputStream( 0 ), Utils.IO_BUFFER_SIZE );
            return true;
        } finally {
            if ( out != null ) {
                out.close();
            }
        }
    }

    private File getDiskCacheDir(Context context, String uniqueName) {

        // Check if media is mounted or storage is built-in, if so, try and use external cache dir
        // otherwise use internal cache dir
        final String cachePath =
                Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) ||
                        !Utils.isExternalStorageRemovable() ?
                        Utils.getExternalCacheDir(context).getPath() :
                        context.getCacheDir().getPath();

        return new File(cachePath + File.separator + uniqueName);
    }

    public void put( String key, Bitmap data ) {

        DiskLruCache.Editor editor = null;
        try {
            editor = mDiskCache.edit( key );
            if ( editor == null ) {
                return;
            }

            if( writeBitmapToFile( data, editor ) ) {
                mDiskCache.flush();
                editor.commit();
                if ( BuildConfig.DEBUG ) {
                    Log.d( "cache_test_DISK_", "image put on disk cache " + key );
                }
            } else {
                editor.abort();
                if ( BuildConfig.DEBUG ) {
                    Log.d( "cache_test_DISK_", "ERROR on: image put on disk cache " + key );
                }
            }
        } catch (IOException e) {
            if ( BuildConfig.DEBUG ) {
                Log.d( "cache_test_DISK_", "ERROR on: image put on disk cache " + key );
            }
            try {
                if ( editor != null ) {
                    editor.abort();
                }
            } catch (IOException ignored) {
            }
        }

    }

    public void put( String key, Uri data ) {

        DiskLruCache.Editor editor = null;
        try {
            editor = mDiskCache.edit( key );
            if ( editor == null ) {
                return;
            }

            if( writeUriToFile( data, editor ) ) {
                mDiskCache.flush();
                editor.commit();
                if ( BuildConfig.DEBUG ) {
                    Log.d( "cache_test_DISK_", "image put on disk cache " + key );
                }
            } else {
                editor.abort();
                if ( BuildConfig.DEBUG ) {
                    Log.d( "cache_test_DISK_", "ERROR on: image put on disk cache " + key );
                }
            }
        } catch (IOException e) {
            if ( BuildConfig.DEBUG ) {
                Log.d( "cache_test_DISK_", "ERROR on: image put on disk cache " + key );
            }
            try {
                if ( editor != null ) {
                    editor.abort();
                }
            } catch (IOException ignored) {
            }
        }

    }

    public synchronized Bitmap getBitmap( String key ) {

        Bitmap bitmap = null;
        DiskLruCache.Snapshot snapshot = null;
        try {

            snapshot = mDiskCache.get( key );
            if ( snapshot == null ) {
                return null;
            }
            final InputStream in = snapshot.getInputStream( 0 );
            if ( in != null ) {
                final BufferedInputStream buffIn =
                        new BufferedInputStream( in, Utils.IO_BUFFER_SIZE );
                bitmap = BitmapFactory.decodeStream(buffIn);
            }
        } catch ( IOException e ) {
            e.printStackTrace();
        } finally {
            if ( snapshot != null ) {
                snapshot.close();
            }
        }

        if ( BuildConfig.DEBUG ) {
            Log.d( "cache_test_DISK_", bitmap == null ? "" : "image read from disk " + key);
        }

        return bitmap;

    }

    public boolean containsKey( String key ) {

        boolean contained = false;
        DiskLruCache.Snapshot snapshot = null;
        try {
            snapshot = mDiskCache.get( key );
            contained = snapshot != null;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if ( snapshot != null ) {
                snapshot.close();
            }
        }

        return contained;

    }

    public boolean removeKey( String key ) {

        boolean removed=false;
        DiskLruCache.Snapshot snapshot = null;
        try {
            removed = mDiskCache.remove( key );
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if ( snapshot != null ) {
                snapshot.close();
            }
        }
        return removed;
    }

    public void clearCache() {
        if ( BuildConfig.DEBUG ) {
            Log.d("cache_test_DISK_", "disk cache CLEARED");
        }
        try {
            mDiskCache.delete();
        } catch ( IOException e ) {
            e.printStackTrace();
        }
    }

    public File getCacheFolder() {
        return mDiskCache.getDirectory();
    }

}
varunkr
  • 5,364
  • 11
  • 50
  • 99
  • just an observation about your code and a probable cause: You´re not freeing the bitmaps and it may be crashing out of surface memory. Do you have any traces about the problem?? – eduyayo Sep 01 '16 at 13:24
  • @eduyayo Hey thanks for the reply, I didn't notice any obvious traces. But where and how should I free the bitmap, this issue is really important for me, but I am not able to solve it. Can you please suggest the changes? Thanks !! – varunkr Sep 01 '16 at 13:29
  • after "use" you should call `bitmap.recycle()`. I´d sugest you to keep a reference to the previous used and recycle before setting a new one... – eduyayo Sep 01 '16 at 13:30
  • BTW! `writeBitmapToFile`when you preprocess and save whatever the bitmap, never lose the thing without recycling!! – eduyayo Sep 01 '16 at 13:34
  • @eduyayo If I recycle the bitmap I get an error Can't parcel a recycled bitmap at the line mNotificationManager.notify(NOTIFY_ID,musicNotification); :( I got this error earlier as well when I was trying to resolve this issue. – varunkr Sep 01 '16 at 14:32
  • I donno the exact point you're recycling the bitmap but you should recycle after it is not showing anymore and will be replaced by another or the UI component destroyed. This is a general MUST DO rule when using unmanaged bitmaps... Either you allow the runtime to manage resources or you take care of the trash – eduyayo Sep 01 '16 at 14:34
  • @eduyayo I wasn't doing it so far, so I tried to recycle it when the notification is to be changed but this haunting exception came back, but as u say it is a must do, I will try to think how to do it !! – varunkr Sep 01 '16 at 14:39
  • @eduyayo So I did recycle the bitmap and handled the exception but still same issue. However I must say that this bitmap in the notification is a very small one, I have much larger bitmap in my activity which changes along with the bitmap in the notification, basically both the bitmap in the notification and my activity are same, the activity one is much larger, but it changes without any problem. Can't understand why this bitmap in the notification is causing an issue !! – varunkr Sep 01 '16 at 15:29
  • Did u recycle the one in the activity???? – eduyayo Sep 01 '16 at 15:31
  • Didn't do that earlier but did that after seeing your comment, still no luck !! :( – varunkr Sep 01 '16 at 15:51
  • I don't know why you are sticking with `LruCache` but a personal experience has taught me to always rely on **Files concepts**. I always save these files in a `.temp` folder on `getExternalStorageDirectory`. I also think the way LruCache works, your bitmap files are being pushed back with the addition of new items and are being _garbage collected_. – Surya Teja Karra Sep 05 '16 at 02:49

1 Answers1

0

put if condition for kitkat and lower versions ... becoz after kitkat version changes in color in notification system.. my mipmap notify img siz is 96*96

  NOTIFICATION_ID= Integer.parseInt(arr_msg[7].toString());
mBuilder.setSmallIcon(R.mipmap.alert_nodification_icon)
        .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.alert_nodification_icon))
        .setContentTitle(notification_vehicle_no)
        .setStyle(new NotificationCompat.BigTextStyle()
                .bigText(notification_msg))
        .setContentText(notification_msg);}
    mBuilder.setContentIntent(contentIntent);
    Notification notification = mBuilder.build();
    notification.flags |= Notification.FLAG_AUTO_CANCEL;
    notification.defaults |= Notification.DEFAULT_SOUND;
    notification.defaults |= Notification.DEFAULT_VIBRATE;
    mNotificationManager.notify(NOTIFICATION_ID,notification);
Akash pasupathi
  • 304
  • 1
  • 14