I am using ACTION_GET_CONTENT and load selected images using Glide. I would like to display the image file size in bytes, but does Glide support a way to get this data?
I could find ways to get file size when using ACTION_GET_CONTENT, but I am afraid that if I use those methods, it may unnecessarily read the same file twice. (I read it once for the size, and Glide reads it once more to get the image). This would be particularly bad if the image is a remote image (users could select images from Google Drive).
You can get the bitmap using SimpleTarget and then you can calculate the size in kb,mb by fetching bytes using getByteCount or getAllocationByteCount
Glide
.with(context)
.load("https://www.somelink.com/image.png")
.asBitmap()
.into(new SimpleTarget<Bitmap>() {
#Override
public void onResourceReady(Bitmap resource, Transition<? super Bitmap> transition) {
int byteCount = Integer.MIN_VALUE;
if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.KITKAT){
byteCount = resource.getByteCount();
}else{
byteCount = resource.getAllocationByteCount();
}
int sizeInKB = byteCount / 1024;
int sizeInMB = sizeInKB / 1024;
image.setImageBitmap(resource);
}
});
Related
Now, in my app, each placeholder has size, that was set in xml and after image was downloaded it resizes ImageView height with horrible visual effect.
How can I get image size, that is downloading and should be displayed in
ImageView, to make the same placeholder size like in Facebook application live feed? Maybe I should include image ratio for each link in json file that is downloading from server, or Fresco, Glide or Picasso has some tricks to acheive this?
If you want to resize the placeholder to the actual image dimensions before downloading the image, you have to somehow get this information from your server. You could, like you've already suggested, include the image dimensions / aspect ratio to the request and then use that information to properly size your views upfront.
Because of this horrible visual effect you've mentioned, Fresco doesn't support wrap_content but it supports setting an aspect ratio for the DraweeView that you can set using your server data.
See also http://frescolib.org/docs/wrap-content.html
Try using ResourceTranscoder with glide request as mentioned below:
Glide
.with(this)
.load(imageUrl)
.asBitmap()
.transcode(new BitmapSizeTranscoder(), Size.class)
.into(new SimpleTarget<Size>() {
#Override public void onResourceReady(Size size, GlideAnimation glideAnimation) {
Log.w("SIZE", String.format(Locale.ENGLISH, "%dx%d", size.width, size.height));
}
});
class Size {
int width;
int height;
}
class BitmapSizeTranscoder implements ResourceTranscoder<Bitmap, Size> {
#Override public Resource<Size> transcode(Resource<Bitmap> toTranscode) {
Bitmap bitmap = toTranscode.get();
Size size = new Size();
size.width = bitmap.getWidth();
size.height = bitmap.getHeight();
return new SimpleResource<>(size);
}
#Override public String getId() {
return getClass().getName();
}
}
I have a problem loading nine patch images from url into a view as the view's background.
I can load nine patch images from resources which works fine.
I set the target for Picasso as follows:
view.setTag(new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, LoadedFrom from) {
Log.d("LOG", bitmap.getWidth() + " " + bitmap.getHeight());
BitmapDrawable bitmapDrawable = new BitmapDrawable(activity.getResources(), bitmap);
byte[] ninePatchChunk = bitmap.getNinePatchChunk();
if (NinePatch.isNinePatchChunk(ninePatchChunk)) {
view.setBackground(new NinePatchDrawable(activity.getResources(), bitmap, ninePatchChunk, new Rect(), null));
} else {
view.setBackground(bitmapDrawable);
}
}
}
This function (loading image from assets) works fine:
Picasso.with(activity)
.load(R.drawable.nine_patch_button)
.into(view.getTag()); //view.getTag() is the target
But I need to download the background image from internet.
Picasso.with(activity)
.load(uri_to_nine_patch_button)
.into(view.getTag()); //view.getTag() is the target
In the second case the image is stretched and not displayed as a nine-patch-image. When I load an image from URI, the log output will always be the same (41, 28), but when I load the image from assets, the log output differs from device to device (108, 75 and 38, 27).
In the first case with same output bitmap.getNinePatchChunk() is null, image stretched, nothing works.
Any ideas for solution?
Best regards
My solution was using this class: https://gist.github.com/knight9999/86bec38071a9e0a781ee
I have a gridview with thumbnails from sdcard. I use an asynctask to lazy load images. It works perfect when i scroll slowly however when I scroll too fast, different images loading on same grid item multiple times and it takes 6 7 second to finally load the correct image. I tried to check if the position of the view is visible by using getFirstVisiblePosition and getLastVisiblePosition, and this time some of the images never loads.
Did you try to cache your images ? With LruCache for exemple.
Here is the doc :
http://developer.android.com/reference/android/util/LruCache.html
And here the official tutorial
http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html
Quick use to it :
LruCache<String, Bitmap> mMemoryCache;
public void onCreate(Bundle b)
{
.....
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
final int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
#Override
protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in kilobytes rather than
// number of items.
return bitmap.getByteCount() / 1024;
}
};
....
}
public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
}
public Bitmap getBitmapFromMemCache(String key) {
return mMemoryCache.get(key);
}
EDIT:
If you're loading you image asynchronously you have to look this tutorial :
http://developer.android.com/training/displaying-bitmaps/process-bitmap.html
It shows you how to handle concurrency and cancel current task if one is running
Im caching my Bitmaps in GridView to LruCache. I made manager for this, see below:
private LruCache<String, Bitmap> mMemoryCache;
public LruCacheManager(){
init();
}
private void init(){
// Get max available VM memory, exceeding this amount will throw an
// OutOfMemory exception. Stored in kilobytes as LruCache takes an
// int in its constructor.
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
// Use 1/8th of the available memory for this memory cache.
final int cacheSize = maxMemory / 8;
//Log.i("ImageCache","cacheSize: " + cacheSize);
if(mMemoryCache == null){
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
#Override
protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in kilobytes rather than
// number of items.
// The cache size will be measured in kilobytes rather than
// number of items.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
return bitmap.getByteCount() ;
} else {
return bitmap.getRowBytes() * bitmap.getHeight();
}
}
};
}
}
public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null) {
Log.i("LruCacheManager","Bitmap is getting added, " + key);
mMemoryCache.put(key, bitmap);
}
}
public Bitmap getBitmapFromMemCache(String key) {
return mMemoryCache.get(key);
}
when I call addBitmapToMemoryCache() in my AsyncTask to save Bitmaps to MemoryCache.
But when i call getBitmapFromMemoryCache() its null.
//get cached Bitmap
LruCacheManager imCache = new LruCacheManager();
String imageKey = categoryNames[position];
Bitmap cachedBm = imCache.getBitmapFromMemCache(imageKey);
//Decide whatever use cached image or not
if (cachedBm != null) {
Log.i("AdapterGridView","Using cached image, " + imageKey);
viewHolder.icon.setImageBitmap(cachedBm);
} else {
//starts Asynctask to scale pictures and show them, happens off the main thread
new AsyncTaskImageLoader(viewHolder.icon, imageKey, mContext, imCache, mThumbIds[position]).execute();
}
Which means, AsyncTask is called again and again. In AsyncTask im adding the Bitmaps to LruCache. Because returned Bitmap is null, there is no Bitmap saved in LruCache. But i have no clue why.
I also searched online and it has maybe to do something with recycling/Garbage Collector.
So how can i properly load cached images?
Any help or clarification is appriciate.
EDIT:
I call this inside BaseAdapter in getView() method. I think it has something to do with it. For the first time, each image is added to Cache, but then, the first image is added like 10 times.
First I would set an arbitrary memory size and try with 1 image. The rest looks good... If what I have below doesn't work, give us printouts of your memory, etc. You might not have any.
In my version I get memory by
final int maxMemory = (int) (Runtime.getRuntime().maxMemory());
then set it by a fraction ( I think I picked an 8th)
I do the /1024 when I return get the size of, I do not do it for setting the memory. So if you have 1/1000 of the memory you think you have, that would be the likely issue..
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
final int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
#Override
protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in kilobytes rather than
// number of items.
return bitmap.getByteCount() / 1024;
}
};
URL url = new URL("http://s2.goodfon.ru/image/260463-1920x1200.jpg");
Bitmap bitmap = BitmapFactory.decodeStream((InputStream) url.getContent(), null, options);
if(bitmap != null)
Log.i("Success", "BITMAP IS NOT NULL");
String key = "myKey";
Log.i("Get is null", "putting myKey");
mMemoryCache.put(key, bitmap);
Bitmap newBitmap = mMemoryCache.get(key);
if(newBitmap == null)
Log.i("newBitmap", "is null");
Hello, here is a code. I get bitmap from URL successfully (Log says Bitmap is not null and I can display it easy). Then I am trying to put it into LruCache and get it back, but it return null. (Log says newBitmap is null). Where is my mistake? Please, tell me.
Android 4.1.2 Cache size 8192 Kb.
If it is 1.19 MB on disk but ~ 9 MB in memory, that means that as a compressed JPEG file, it's 1.19 MB and once you extract that into a Bitmap (uncompressed) that can be displayed, it will take up 9 MB in memory. If it's a 1920 x 1200 pixel image as suggested by the url in your code snippet, the image will take up 1920 x 1200 x 4 bytes of memory (4 bytes for each pixel to represent ARGB values from 0 to 256 times 2.3 million total pixels = 9,216,000 bytes). If you're using 1/8 of your available memory for this cache, it's possible/likely that 9MB exceeds that total memory space so the Bitmap never makes it into the cache or is evicted immediately.
You're probably going to want to downsample the image at decoding time if it's that large (using BitmapFactory.Options.inSampleSize...lot's of documentation on the web for using that if you're not already familiar).
Also, you're using Runtime.maxMemory to compute your cache size. This means you're requesting the maximum amount of memory that the whole VM is allowed to use.
http://developer.android.com/reference/java/lang/Runtime.html#maxMemory%28%29
The more common approach is the use the value given back to you by the ActivityManager.getMemoryClass() method.
Here's an example code snippet and the method definition in the docs for reference.
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
int memClassBytes = am.getMemoryClass() * 1024 * 1024;
int cacheSize = memClassBytes / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize)
http://developer.android.com/reference/android/app/ActivityManager.html#getMemoryClass%28%29
You can also recycle bitmaps that pops out from lrucache
final Bitmap bmp = mLruCache.put(key, data);
if (bmp != null)
bmp.recycle();
The Android example was wrong when dividing Runtime maxMemory by 1024 in the following line:
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
The unit of the maxMemory is Byte which is the same with the 'cacheSize' ('/ 8' just means it will use eighth of the available memory of the current Activity). Therefore, '/ 1024' will make the 'cacheSize' extremely small such that no bitmap can be actually 'cached' in 'mMemoryCache'.
The solution will be delete '/ 1024' in the above code.