I have 20 URL of different images. I need to download the 20 images from the URL and display it in Grid view. For me its taking much time to download the image content. Following is the code i'm using in Image Adapter class.
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView;
if (convertView == null) { // if it's not recycled, initialize some attributes
imageView = new ImageView(mContext);
imageView.setLayoutParams(new GridView.LayoutParams(220, 200));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setPadding(1, 1, 1,0);
} else {
imageView = (ImageView) convertView;
}
imageView.setImageDrawable(GetDrawableImage(url));
return imageView;
}
private Drawable GetDrawableImage(String zurlP)
{ InputStream InputStreamL = null;
Drawable DrawableImageL = null;
try {
InputStreamL = (InputStream) new URL(zurlP).getContent();
DrawableImageL = Drawable.createFromStream(InputStreamL, "src");
} catch (MalformedURLException e) {
} catch (IOException e) {
}
return DrawableImageL;
}
Is there any easiest way(Less Time Consuming) to perform the same task?
Yes.., You can use Asynctask or Painless threading to load large images from server. And Cache the Images once you download. You can use [Fedor lazylist] for that (https://github.com/thest1/LazyList)
Various possibilities.
Multi Threading
Cacheing
Pain less threading
Large bitmaps loading
Or you can simply go with thumb nails. Means get thumb nails from server instead of bit images. Loading will be effective
You need to download images in other thread. You can use image loader.
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.
What can be cause for this error:
java.lang.OutOfMemoryError: Failed to allocate a 10111384 byte allocation with 1305618 free bytes and 1275KB until OOM
Error happens in this line :
Bitmap bmp = BitmapFactory.decodeByteArray(currentTour.getTourImage(),0,currentTour.getTourImage().length);
My ListView adapter View :
public View getView(int position, View view, ViewGroup parent) {
if (view == null)
view = getLayoutInflater().inflate(R.layout.tour_item, parent, false);
if (position % 2 == 1) {
view.setBackgroundColor(Color.rgb(189, 230, 247));
} else {
view.setBackgroundColor(Color.WHITE);
}
TourAnounceRow currentTour = tourAnounceList.get(position);
if (currentTour != null) {
TextView tourName = (TextView) view.findViewById(R.id.lblTourName);
tourName.setText(currentTour.getTourName());
TextView place = (TextView) view.findViewById(R.id.lblPlace);
place.setText(currentTour.getPlace());
RatingBar tourStarCount = (RatingBar) view.findViewById(R.id.ratingBarTourStarCount);
tourStarCount.setRating(Float.parseFloat(String.valueOf(currentTour.getHotelStarsCount())));
TextView adultCount = (TextView) view.findViewById(R.id.lblAdultCount);
adultCount.setText(String.valueOf(currentTour.getAdultCount()));
TextView childrenCount = (TextView) view.findViewById(R.id.lblChildCount);
childrenCount.setText(String.valueOf(currentTour.getChildrenCount()));
TextView mealCount = (TextView) view.findViewById(R.id.lblMealCount);
mealCount.setText(String.valueOf(currentTour.getDailyMealCount()));
TextView price = (TextView) view.findViewById(R.id.lblPrice);
price.setText(String.valueOf(currentTour.getPrice()));
ImageView ivTourImage = (ImageView) view.findViewById(R.id.ivTourImage);
ivTourImage.setScaleType(ImageView.ScaleType.FIT_XY);
Bitmap bmp = BitmapFactory.decodeByteArray(currentTour.getTourImage(), 0,currentTour.getTourImage().length);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.WEBP, 10, stream);
ivTourImage.setImageBitmap(bmp);
}
return view;
}
This happens because your bitmap it's to large, you can optimize the load of the bitmap, take a look to this link
I would suggest, especially since you're using this inside a ListView, that you take a look at the Glide Library (https://github.com/bumptech/glide). The library will handle both an asynchronous call to load the image, as well as the resizing of the image to fit your ImageView (saving memory).
You also seem to be hold a lot of byte[] images in memory (based on tourAnounceList), you want to only hold a reference to them when possible; either as a URL/URI or a local resource integer (R.drawable.my_image).
The code should look like this:
ImageView ivTourImage = (ImageView) view.findViewById(R.id.ivTourImage);
Glide.with(parent.getContext()).load(currentTour.getImageUrl())
.fitCenter().into(ivTourImage);
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 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;
I have a large list of objects, all of whom have a path to their image "ex. http://www.google.com/image.jpg" and I need to download the image and save the drawable to the object..
I was using AsyncTask, but even if I use my own threads I always end up with 'OutOfMemoryError' at some arbitrary point in the list. The images are never larger than 82Kb (Is this too big for android tablets?) in size, but I think the sheer number of images is causing the process as a whole to fail.
Here is what I'm currently doing.
class DownloadImageTask extends AsyncTask<ArrayList<Item>, Void, Void> {
private static int num =1;
#Override
protected Void doInBackground(ArrayList<Item>... items) {
try {
if(items.length == 0)
return null;
HttpURLConnection connection;
InputStream input;
for(ArrayList<Item> itemlist : items) {
for(Item i : itemlist) {
Log.d(JusTouchMenu.TAG,"[Item]Image request to url:"+i.getImagePath());
try {
connection = (HttpURLConnection)new URL(i.getImagePath()).openConnection();
connection.setRequestProperty("User-agent","Mozilla/4.0");
connection.connect();
input = connection.getInputStream();
i.setImage(new BitmapDrawable(BitmapFactory.decodeStream(input)));//Requires a drawable
connection.disconnect();
} catch(Exception e) {Log.e(JusTouchMenu.TAG,"[Item]Unable to download image # '"+i.getImagePath()+"'",e);}
Log.v(JusTouchMenu.TAG, "[Item]Image decoded # '"+i.getImagePath()+"' #"+num++);
for(Tag pt : i.tags()) {
Log.d(JusTouchMenu.TAG,"[Item->Tag]Image request to url:"+pt.getImagePath());
try {
connection = (HttpURLConnection)new URL(pt.getImagePath()).openConnection();
connection.setRequestProperty("User-agent","Mozilla/4.0");
connection.connect();
input = connection.getInputStream();
pt.setImage(new BitmapDrawable(BitmapFactory.decodeStream(input))); //Requires a drawable
connection.disconnect();
} catch(Exception e) {
Log.e(JusTouchMenu.TAG,"[Item->Tag]Unable to download image # '"+i.getImagePath()+"'",e);
}
Log.v(JusTouchMenu.TAG, "[Item->Tag]Image decoded # '"+pt.getImagePath()+"' #"+num++);
}
}
}
} catch(Exception e) {
Log.e(JusTouchMenu.TAG,"Error decoding image inside AsyncTask",e);
}
return null;
}
Thanks!
How many of these images are you trying to get at once? How are you using them?
You should probably consider using the capabilities of Gallery or GridView and thereby only be loading the images that are in view at the moment. You can even get fancy and cache them so scrolling is nice and smooth. Trying to load all of the images into an ArrayList when youre not likely to be able to display them all at once is not a mobile friendly approach.
Here are some decent references to caching approaches but you should probably get started with a basic gallery or grid view loading as needed and then add the caching on.
Lazy load of images in ListView
http://code.google.com/p/libs-for-android/wiki/ImageLoader