0

From the smartwatch I receive a DataMap object which contains two DataMap Objects, one containing a list of strings (title, subtitle, content ...) and the other one containing a list of Assets

List<DataMap> rootItemDataMap [...]
  DataMap itemFieldsDataMap = rootItem.getDataMap(Constants.ROOT_ITEM_FIELDS);
  DataMap itemImagesDataMap = rootItem.getDataMap(Constants.ROOT_ITEM_IMAGES);

when onDataChanged is being called, I convert itemFieldDataMap in Strings and itemImagesDataMap in a list of Assets. for each Asset I want to convert the Asset in Bitmap and put it in a list of Bitmaps. but I have an OutOfMemoryError:

List<Bitmaps> imagesList = new ArrayList<>();
for (int j = 0; j < itemImagesDataMap.size(); j++) {
        Asset asset = itemImagesDataMap.getAsset(Constants.EXTRA_IMAGE +j);
        Bitmap bitmap = WearableUtils.loadBitmapFromAsset(mGoogleApiClient, asset);
        imagesList.add(bitmap);
}

the method that do the bad job is loadBitmapFromAsset:

public static Bitmap loadBitmapFromAsset(GoogleApiClient mGoogleApiClient, Asset asset) {
    InputStream assetInputStream = Wearable.DataApi.getFdForAsset(mGoogleApiClient, asset).await().getInputStream();
    return BitmapFactory.decodeStream(assetInputStream);
}

I get this exception at runtime:

java.lang.OutOfMemoryError: Failed to allocate a 2531852 byte allocation with 1037608 free bytes and 1013KB until OOM
                                                     at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
                                                     at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
                                                     at android.graphics.BitmapFactory.decodeStreamInternal(BitmapFactory.java:635)
                                                     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:611)
                                                     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:649)
                                                     at com.mangomobi.showtime.contentmanager.WearableUtils.loadBitmapFromAsset(WearableUtils.java:71)
                                                     at com.mangomobi.showtime.contentmanager.ItemFactoryImpl.createItems(ItemFactoryImpl.java:48)
                                                     at com.mangomobi.showtime.contentmanager.WearableContentServiceImpl.onDataChanged(WearableContentServiceImpl.java:136)
                                                     at com.google.android.gms.wearable.WearableListenerService$zzc$1.run(Unknown Source)
                                                     at android.os.Handler.handleCallback(Handler.java:739)
                                                     at android.os.Handler.dispatchMessage(Handler.java:95)
                                                     at com.google.android.gms.wearable.WearableListenerService$zzb.dispatchMessage(Unknown Source)
                                                     at android.os.Looper.loop(Looper.java:148)
                                                     at android.os.HandlerThread.run(HandlerThread.java:61)

I have already tried using BitmapFactory.Options, but I read this SkImageDecoder::Factory returned null

The only way to properly show images in my WearList is to get the first image for each item received, as that itemImagesDataMap contains only one Asset. How can I load multiple Bitmaps in a for loop without getting memory errors in Android Wear, and generally speaking, in Android?

Panda
  • 45
  • 9
  • are you scaling the images to an appropriate size before sending them to the watch? – Sterling Feb 08 '17 at 15:04
  • No, because I don't know how to dynamically determine the appropriate size of the image based on the watch resolution. – Panda Feb 08 '17 at 20:07
  • Either just guess (e.g., 400px), or use the Data API to send the size from watch to phone. – Sterling Feb 08 '17 at 23:24
  • ok. Why use DataApi? How can I get the size? – Panda Feb 09 '17 at 00:40
  • The Data API is built to pass data between the two devices, so it's probably the right choice to share the screen size from the watch to the phone. The Message API would also work, if you're more comfortable with that. As for getting the size: `DisplayMetrics metrics = getResources().getDisplayMetrics(); displaySize = new Point(metrics.widthPixels, metrics.heightPixels);` – Sterling Feb 09 '17 at 04:28

1 Answers1

0

You may want to first check Loading Large Bitmaps Efficiently wherein it was mentioned that:

To avoid java.lang.OutOfMemory exceptions, check the dimensions of a bitmap before decoding it, unless you absolutely trust the source to provide you with predictably sized image data that comfortably fits within the available memory.

You may try using a method to calculate a sample size value that is power of two based on a target width and height as shown from the sample code in the documentation. As further discussed,

To use this method, first decode with inJustDecodeBounds set to true, pass the options through and then decode again using the new inSampleSize value and inJustDecodeBounds set to false.

To help you further, you may want to check suggested solutions in the following related SO posts:

  • fix the OutOfMemory error by reducing memory consumption with the use of inSampleSize as suggested in this SO post
  • use a static method to get a bitmap from the external storage as suggested here.
Community
  • 1
  • 1
Teyam
  • 7,686
  • 3
  • 15
  • 22
  • I have already tried using the BitmapFactory.Option as well as inJustDecodeBounds setting it to true and then to false. By doing that and following all the documentation tips, I can avoid the OutOfMemoryError but I get this message -> "SkImageDecoder::Factory returned null" and the images are not properly loaded in wear. Few posts about this message are online, I can't understand how to handle the situation – Panda Feb 08 '17 at 20:09