I have a scrollview which has small thumbnails of images loaded via AsyncTask and throws the image URL unto a imageView.
They are added dynamically and then on top, is a main imageView which holds the image of the thumbnail you clicked.
Everything runs great until you have about 10+ images in the thumbnails...
I am loading the mainImage url via the same way as the thumbnails, so when they click an image in the thumb, it loads it up top.
I am recycling the bitmap in the method itself, but it seems to be running out of memory and crashing when loading more than 10 images (thumbnails load ok, but crashes when i click to load the main image)
any help appreciated
this is the code i am using the load the images (thumbnails + main):
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
ImageView bmImage;
public DownloadImageTask(ImageView bmImage) {
this.bmImage = bmImage;
}
protected Bitmap doInBackground(String... urls) {
String urldisplay = urls[0];
Bitmap mIcon11 = null;
try {
InputStream in = new java.net.URL(urldisplay).openStream();
mIcon11 = BitmapFactory.decodeStream(in);
} catch (Exception e) {
Log.e("Error", e.getMessage());
e.printStackTrace();
}
return mIcon11;
}
protected void onPostExecute(Bitmap result) {
bmImage.setImageBitmap(result);
}
}
just implement this on ur image ... it will reduce ur image by 4 times
public static Bitmap getImage(byte[] image) {
BitmapFactory.Options config = new BitmapFactory.Options();
config.inPreferredConfig = Bitmap.Config.RGB_565;
config.inSampleSize = 4;
return BitmapFactory.decodeByteArray(image, 0, image.length,config);
}
You should read the two following Android official tutorials. They will teach you how to load large bitmap efficiently, and they provide working code samples that we use in our production apps
Displaying Bitmaps Efficiently
Loading Large Bitmaps Efficiently
you are not closing the input stream. So all the time when images urls executed input stream objects created which are expensive and causing you OutofMemoryError. add below code after catch block.
in.close();
in = null;
You probably have to work with some sort of cache strategy for the bitmaps, try having a look on the library Universal Image Loader, it works very well for what you need
Recycling bitmaps by yourself should only be done when you know for sure that it won't be used anymore.
Try to resize the bitmap to a smaller size (with a factor like 1.5 example: width=widt*1.5)
then strech(fit xy or whatever) in your view.it will lower the quality of the image due to the factor you decide. it is a quick dirty way. also making bit map rgb like Niun Goia mentions works well.
You shouldn't be using a ScrollView for this. You should be using a ListView. When you use a ScrollView (with a LinearLayout or something like that inside it), all the elements in the list (no matter how big it is) will try to be loaded immediately. Try a ListView instead (which is made for that exact reason, it only loads in memory the ui elements of the rows that are visible).
You can use android:largeHeap="true" to request a larger heap size
answer
I use this..
Options opt = new Options();
opt.inPurgeable = true;
opt.inInputShareable = true;
opt.inPreferredConfig = Bitmap.Config.RGB_565;
Related
I am learning Android dev. and am developping a Hearthstone app using a Hearthstone API for fun.
My users can search for specific cards and now I wish to implement a Card Displayer that displays cards by their type, and lets the user swipe right or left to display the next one in my JSONArray. My API request gives me one and each JSONObject has an img attribute with the cards image URL.
Therefore, when the user swipes I am doing the following:
// Swipe right -> number - 1 (Previous page)
// Swipe left -> number + 1 (Next page)
public void displayCardNumber(int number) {
APIRequests apiRequests = new APIRequests();
try {
// Gets the JSONObject at 'number' and retrieves its img URL.
JSONObject card = (JSONObject) cardsArray.get(number);
String currentImageURL = (String) card.get("img");
// Here is where my problem is.
Bitmap bitmap = apiRequests.getImageBitmap(currentImageURL);
if (bitmap != null) {
setNewImage(bitmap);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
But apiRequest.getImageBitmap(URL) is where I have a problem.
I must download the image in order to display it, but not only does the following block of code not work when I download more than one image, I must also find an efficient way of displaying my cards (that requires background download perhaps?).
// Returns the image's bitmap using the URL
protected Bitmap getImageBitmap(String currentImageURL) {
try {
Bitmap bitmap = BitmapFactory.decodeStream((InputStream)new URL(currentImageURL).getContent());
return bitmap;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
Why can't I download more than 1 image? Is my way of getting my Bitmap false?
Thank you.
Use Glide https://github.com/bumptech/glide .This is faster.
Bitmap theBitmap = Glide.
with(this).
load(imgUrl). //image url as string
asBitmap().
into(100, 100). // Width and height
get();
You can also use it with another way like this:
Glide.with(context).load(url) // image url
.thumbnail(0.5f)
.crossFade()
.diskCacheStrategy(DiskCacheStrategy.ALL)
.placeholder(imgId) // If no image found the imgId is the default image id for the imageView
.into(imageView); // imageView to show the image
I have found a solution,
In order to load my image and display it I now use a library called Android Universal Image Loader that lets me do the following:
// Load image, decode it to Bitmap and display Bitmap in ImageView
ImageLoader imageLoader = ImageLoader.getInstance();
ImageView i = (ImageView)view.findViewById(R.id.cardDisplay);
imageLoader.displayImage(currentImageURL, i);
I retrieve the correct URL in my AsyncTask's doInBackground and display it with the library's method.
I'm downloading an image from a URL and displaying it in an ImageView. I need to download the image at its full original size. I've tried Glide, Picasso and Universal Image Loader with no success. Is there any library or mehod out there to achieve this? I even tried making my own AsyncTask to do it, something like this:
public class ImageLoader extends AsyncTask<Void, Void, Bitmap> {
#Override
protected Bitmap doInBackground(Void... params) {
try {
URL url = new URL(bundle.getString("selectedImage"));
HttpURLConnection conn =
(HttpURLConnection)url.openConnection();
conn.setReadTimeout(6000);
conn.setConnectTimeout(6000);
conn.setRequestMethod("GET");
conn.setDoInput(true);
conn.connect();
int respose = conn.getResponseCode();
InputStream is = conn.getInputStream();
BufferedInputStream bufferedInputStream = new
BufferedInputStream(is);
bitmap = BitmapFactory.decodeStream(bufferedInputStream);
return bitmap;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
but no success. Anyone have anything to help me?
1) Try to use Volley library.
https://developer.android.com/training/volley/request.html#request-image
2) Use WebView instead ImageView
I'm not really sure what you mean by "its full original size". I haven't experienced any automagic scaling of images simply by downloading them.
Maybe you could double-check that you have an appropriate android:scaleType on the target ImageView. You can read more on the different values of the scale type property here: http://developer.android.com/reference/android/widget/ImageView.ScaleType.html
If you want to pan the image, like an "unscaled" web page in the browser (typically when the image is bigger than the screen), you might need to add further logic to manage this. Maybe it could be as easy as having a ScrollView wrap your ImageView (which then would wrap its content, of course).
The error was down to photo bucket giving me a scaled down URL instead I used flikr and on my device I get an image almost identical to my original (Picasso limit is 2048x2048) but on other devices I still seem to get a 1080 x 910 image, will investigate further but it seems the answer is not to use photo bucket
I have an array of ParseObjects that are being displayed in a list view. Three text views are loaded into my custom cell, and then there's an image in a ParseFile that should also be loaded. The code I have gets the first cell to load correctly, but in every other cell the image doesn't load. Here's my code:
this.origImage = (ParseFile) posts.get(position).get("image");
try {
Log.d("MyMessage", "Gonna convert image");
this.imageData = this.origImage.getData();
this.options = new BitmapFactory.Options();
this.options.inDither = true;
this.options.inPreferredConfig = Bitmap.Config.ARGB_8888;
this.options.inSampleSize = 8;
this.notConvertedYet = BitmapFactory.decodeByteArray(this.imageData, 0, this.imageData.length, this.options);
if (this.notConvertedYet != null)
this.myBitmap = rotateImage(90, this.notConvertedYet);
else
this.myBitmap = this.notConvertedYet;
mHolder.picImageView.setImageBitmap(this.myBitmap);
Log.d("MyMessage", "Converted image");
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
What is happening that's messing it up?
when its bound to a list adapter, what does the
posts.get(position).get("image");
do actually? Is the parse SDK calling a series of AsyncTasks for the network Http GET on the actual URL in parse's file CDN?
You may have to find out more about what its doing because , as is, it may not be very efficient as used by the Adapter.getView()...
Any image loader framework like Volley, like Universal Image Loader, like AQuery will work like an api where you make a call providing the ImageView and the CDN URL for the image as parms. The framework will handle multithreading, pooled Parse.com connections , memCache and fileCache for all images. When using parse to store bitmaps in the file CDN you can still use any of the image loading frameworks.
AQuery sample in an adapter...
ImageView thumbnail=(ImageView)vi.findViewById(R.id.imageView1); // thumb image
mAquery.id(thumbnail).width(110).image($thumbUrl_CDN,
true, true, 0, R.drawable.default_video, null, 0, mAspectRatio);
Your code looks ok.. maybe the reason all the imageViews are black is that the adapter did not have time to get the files loaded across the network and the loop statement did not block the UI thread??
I would like to load images into a gallery view from a url?
I first make them a bitmap using this.
URL aURL = new URL(myRemoteImages[position]);
URLConnection conn = aURL.openConnection();
conn.setUseCaches(true);
conn.connect();
Object response = conn.getContent();
if (response instanceof Bitmap) {
Bitmap bm = (Bitmap)response;
InputStream is = conn.getInputStream();
/* Buffered is always good for a performance plus. */
BufferedInputStream bis = new BufferedInputStream(is);
/* Decode url-data to a bitmap. */
bm = BitmapFactory.decodeStream(bis);
bis.close();
is.close();
Log.v(imageUrl, "Retrieving image");
/* Apply the Bitmap to the ImageView that will be returned. */
i.setImageBitmap(bm);
How could I go about caching this bitmap? So when the user swipes the screen it doesn't reload over and over?
EDIT: I CALL getImage() to retreive the text url for each url.
i use both of these in a asyncTask. preExecute i call getImage()
and doInBackground i set the gallery to the imageAdapter.
private class MyTask extends AsyncTask<Void, Void, Void>{
#Override
protected Void doInBackground(Void... arg0) {try {
getImages();
Log.v("MyTask", "Image 1 retreived");
getImage2();
Log.v("MyTask", "Image 2 retreived");
getImage3();
Log.v("MyTask", "Image 3 retreived");
getImage4();
Log.v("MyTask", "Image 4 retreived");
} catch (IOException e) {
Log.e("MainMenu retreive image", "Image Retreival failed");
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void notUsed){
((Gallery) findViewById(R.id.gallery))
.setAdapter(new ImageAdapter(MainMenu.this));
}
}
EDIT: getView() method
public View getView(int position, View convertView, ViewGroup parent) {
ImageView i = new ImageView(this.myContext);
try {
URL aURL = new URL(myRemoteImages[position]);
URLConnection conn = aURL.openConnection();
conn.setUseCaches(true);
conn.connect();
Object response = conn.getContent();
if (response instanceof Bitmap) {
Bitmap bm = (Bitmap)response;
You could store your images on the SDCard and on the launch of your application you need to initialize a component that keeps a HashMap<String,Bitmap> and initialize the map with the contents of a folder from the SDCard.
When you will need an image, you will first check if your HashMap contains the key of that image, let say myMap.contains(myFileName) and if it does you will fetch the image from the map, and if the image is not contained in your map you will need to download it, store id on the SDCard and put in in your map.
I'm not sure if this is the best solution, since if you have a large number of Bitmaps your application can run out of resources. Also I think storing Drawable instead of Bitmap will be less memory consuming.
EDIT:For your problem you need create a custom class that has a member Drawable and execute the URLConnection just when you first create your objects. After that in the getView() method you will just use myObj.getMyDrawable() to access the drawable for that specific object.
Since Android 4 it is possible to cache HTTP responses directly by the HttpUrlConnection. See this article: http://practicaldroid.blogspot.de/2013/01/utilizing-http-response-cache.html
Caching images on android is an level oriented task:
Generally at Caching at two levels:
Runtime Heap memory in a form key-value, where key being a identifier for image and value is object of bitmap.(Refer Here)
Most optimised Way for implementing it is LRUCache:
Which Basically maintains a LinkedList for recently accessed items where dump the items accessed at earliest due to memory limitation.
As this backing pixel data for a bitmap is stored in native memory. It is separate from the bitmap itself, which is stored in the Dalvik heap. The pixel data in native memory is not released in a predictable manner, potentially causing an application to briefly exceed its memory limits and crash.
private LruCache<String, Bitmap> mMemoryCache;
#Override
protected void onCreate(Bundle savedInstanceState) {
// 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;
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);
}
Disk Storage: As memory has limited storage with restricted lifecycle.
Memory Cache is good for speeding up in accessing recently viewed images but you can not rely on this for images available on this cache.
UIComponents with indefinite and large data set can easily fill up memory and resulting in loss of images.
Memory cache may get effected in situations like going in background while on call.
Disk Cache can help you to make images persist for longer.
One of it's Optimized way of using it is DiskLruCache
So When you look up for a bitmap in memory cache is result is nil you can try to access it from disk cache and incase you don't find it here too and then just load it from internet.
For Implementation Refer here
I've been looking everywhere to see if there is a standard way of achieving this but I find a different solution every-time.
Basically, I am trying to build a Custom ListView with an image and two-three lines of text besides it.
In order to optimize it, I understand that the following have to be used:
convertView: Basically if the view was already inflated, use it
Lazy-Loading: Do not load the content of the rows until they are called for
Background Downloading & Caching: Download images in their own threads and then update the row (and possible cache them?)
I could manage 1 and 2 but the third one is really confusing me.
Is there a best practice for this?
Thanks
Thanks to Mark for his help. This is one way of doing what he suggested (just in case some else is curious):
private class DownloadImageTask extends AsyncTask<Object, Integer, Bitmap> {
private ImageView iv;
protected Bitmap doInBackground(Object... params) {
try {
iv = (ImageView) params[0];
URL aURL = new URL("http://URLTOIMAGE/img" + params[1] + ".png" );
URLConnection conn = aURL.openConnection();
conn.connect();
InputStream is = conn.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is);
Bitmap bm = BitmapFactory.decodeStream(bis);
bis.close();
is.close();
return bm;
} catch (IOException e) {
return null;
}
}
protected void onPostExecute(Bitmap result) {
iv.setImageBitmap((Bitmap) result);
}
}
And it would be used as follows:
new DownloadImageTask().execute(new Object[] {ImageViewHandle, position});
Note that this is not a working code. This was taken from a larger code base so you will have to make appropriate changes to make this work.
It may be a bit early to describe something as "best practice". AsyncTask or a thread monitoring a LinkedBlockingQueue are fine ways to offload something like image fetching.
You may be interested in my ThumbnailAdapter, which handles the background downloading and caching for you.