I am using following example to display internet images in my activity.
http://developer.android.com/resources/tutorials/views/hello-gridview.html
In custom image adapter I'm directly loading images from internet and assigning it to imageview.
Which shows images in gridview and every thing works fine but it is not efficient way.
When ever i scroll gridview it again and again loads images and thats why gridview scrolls very slow
Is there caching or some useful technique available to make it faster?
Create a global and static method which returns a Bitmap. This method will take parameters: context,imageUrl, and imageName.
in the method:
check if the file already exists in the cache. if it does, return the bitmap
if(new File(context.getCacheDir(), imageName).exists())
return BitmapFactory.decodeFile(new File(context.getCacheDir(), imageName).getPath());
otherwise you must load the image from the web, and save it to the cache:
image = BitmapFactory.decodeStream(HttpClient.fetchInputStream(imageUrl));
FileOutputStream fos = null;
try {
fos = new FileOutputStream(new File(context.getCacheDir(), imageName));
}
//this should never happen
catch(FileNotFoundException e) {
if(Constants.LOGGING)
Log.e(TAG, e.toString(), e);
}
//if the file couldn't be saved
if(!image.compress(Bitmap.CompressFormat.JPEG, 100, fos)) {
Log.e(TAG, "The image could not be saved: " + imageName + " - " + imageUrl);
image = BitmapFactory.decodeResource(context.getResources(), R.drawable.default_cached_image);
}
fos.flush();
fos.close();
return image;
preload a Vector<SoftReference<Bitmap>> object with all of the bitmaps using the method above in an AsyncTask class, and also another List holding a Map of imageUrls and imageNames(for later access when you need to reload an image), then set your GridView adapter.
i recommend using an array of SoftReferences to reduce the amount of memory used. if you have a huge array of bitmaps you're likely to run into memory problems.
so in your getView method, you may have something like(where icons is a Vector holding type SoftReference<Bitmap>:
myImageView.setImageBitmap(icons.get(position).get());
you would need to do a check:
if(icons.get(position).get() == null) {
myImageView.setImageBitmap(defaultBitmap);
new ReloadImageTask(context).execute(position);
}
in the ReloadImageTask AsyncTask class, simply call the global method created from above with the correct params, then notifyDataSetChanged in onPostExecute
some additional work may need to be done to ensure you don't start this AsyncTask when it is already running for a particular item
You will need to implement the caching yourself. Create a proxy class that will download the images. In the getView ask this class to download an image by passing a url. In the proxy class create a HashMap that will map a url to a Bitmap. If the key for the passed url doesn't exist, download the image and store it. Otherwise returned the stored bitmap converted to an imageView.
Of course you can't afford to store as many images as you like. You need to set a limit, for example 10 images, based on the image size you expect to have. When the limit is exceeded, you need to discard old images in the favor of new ones.
You could try DroidFu. My app uses the ImageCache. There's also some manner of web-based imageview or something of the sort in the library. See in particular WebImageView and WebGalleryAdapter: http://mttkay.github.com/droid-fu/index-all.html
Edited to add: The droid-fu project is deprecated in favor of Ignition. https://github.com/mttkay/ignition
Related
I have a item object, containing a Uri variable called image. something like this
http://icons.iconarchive.com/icons/designcontest/vintage/256/Type-icon.png
I tried to get from the class method the Uri and use it for setting up the ImageView called Image in my viewholder
viewHolder.Image.setImageURI(item.getImage());
but it didn't work, so I tried something more complex without success
//Image
URL url = null;
try {
url = new URL(item.getImage().toString());
} catch (MalformedURLException e) {
e.printStackTrace();
}
Bitmap bmp = null;
try {
bmp = BitmapFactory.decodeStream(url.openConnection().getInputStream());
viewHolder.Image.setImageBitmap(bmp);
} catch (IOException e) {
e.printStackTrace();
}
I have allowed internet permissions, and if I use some images from local with Uri.parse it works without any problem.
Here's the error I get from logcat
E/AndroidRuntime: java.lang.NullPointerException: Attempt to invoke virtual method 'java.net.URLConnection java.net.URL.openConnection()' on a null object reference
I think the problem is something in the url definiton but I'll ask you if someone had my same problem and how you solved. Thanks in advance!
for this purpose try to use a specific image library like Picasso, Glide or Fresco. The first one is the simplest with nice fluent interface and it loads images pretty fast:
Please check it, as you may don't know it. Here is a short description taken from its official site (link below):
Images add much-needed context and visual flair to Android
applications. Picasso allows for hassle-free image loading in your
application—often in one line of code!
Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);
Many common pitfalls of image loading on Android are handled
automatically by Picasso:
Handling ImageView recycling and download cancelation in an adapter.
Complex image transformations with minimal memory use.
Automatic memory and disk caching.
From: http://square.github.io/picasso/
As it already Open Source project you can check the code to improve it or make your own version fitted to your actual needs.
Hope it help
In all likelihood, url = new URL(item.getImage().toString()); is throwing a MalformedURLException, which your catch block is suppressing. As a result, url is null, which is why you're getting a NullPointerException. A constructor can't return a null, so this is the only thing I can imagine.
Unless you really know what you're doing, I recommend you use a library like Glide to do image fetching.
I am using the following method to get an image from a given URL.
protected Bitmap doInBackground(String... urls) {
String urlDisplay = urls[0];
Bitmap scaledImage = null;
try {
InputStream in = new java.net.URL(urlDisplay).openStream();
scaledImage = Bitmap.createScaledBitmap(BitmapFactory.decodeStream(in), 380, 250, false);
} catch (Exception e) {
Log.e("Error", e.getMessage());
e.printStackTrace();
}
return scaledImage;
}
Is there anyway to get a scaled image without having to download the full sized image first? It would greatly increase load times.
Sadly, server side manipulations can't be done and before downloading, there's no way to scale it. However, if you still want to save the load time on consecutive refreshes, then you probably can go ahead with saving the bitmap received in a shared preference. This way if the image is already stored in the shared preference, you don't need to download it again and apply scaling to it.
CAUTION: In case the image you are downloading is changing after a while, you can put a check to download it after every "n" (say 7) days and replace the existing stored image with this one.
NOTE: Though there are many answers already available which tells you how to store the image bitmap in Shared Preference / local storage, let me know in case you need that info/ code-snippet too.
You can not perform bitmap operations on a remote image without downloading it first. Therefore there is no way to do what you want to do unless the server you are getting the image from supports different image sizes or a parameter to resize it on the server side.
So my app lists all the files and folders in a given Dropbox folder. They are listed in a custom ListView using an adapter.
Currently I use drawables for the image and folder icons. I loop through the Dropbox folder structure and add the needed drawable to a Bitmap array. This is done in an Async Task (doInBackground).
I then call the Adapter in the PostExecute like this :
adapter = new ImageAdapter(this, pix, paths);
lstView.setAdapter(adapter);
This then shows all the files and images (with default drawable icons) in the ListView.
The next step I want to do is start loading the thumbnails from dropbox.
So for every image in the List, I want to replace the drawable with a thumbnail retreived from dropbox.
Again this should be done in an Async task so the user can still scroll through the listview.
With dropbox, you can load thumbails like this:
if(fileInfo.thumbExists)
{
file = fileSystem.openThumbnail(fileInfo.path, ThumbSize.XS, ThumbFormat.PNG);
Bitmap image = BitmapFactory.decodeStream(file.getReadStream());
thumbs.add(image);
file.close();
}
In the code above, thumbs is a Bitmap Array.
I was planning on using Universal Image Loader or Picasso. But you cannot pass in a Bitmap Array into either of those. It has to be a URL or URI.
How can I achieve this? I'm guessing I need another async task, but I'm not sure how to update my adapter.
Considerations:
I don't want to wait until all thumbnails are downloaded before
starting to display them
Are there memory considerations if there are lots of thumbails?
Can you only display thumnails in the Visibile part of the
listview, and start loading more when they scroll?
Remember - I am using an array of Bitmaps, I don't have any URLS. Would I be best saving each Bitmap to the sd card and then using UIL or Picasso to load using the URI? But how would you know which images went to which position in the ListView?
SO the steps in my code would ideally be:
-Load the Listview with the files and folders with dummy images (already doing this!)
-Get the thumbnails from the decodeStream and load into the Bitmap Array
-Load the thumbnails into the correct position into the ListView
For each row of the list view, run a separate thread to download and show the thumbnail image.
I guess, you can avoid the use of a bitmap array and directly feed the downloaded image to the list view row.
I) Modify your getView() call back of the adapter like the one below:
ImageView thumbnail=Container.findViewById(R.id.thumbnail)
loadImageInBackground(thumbnail,URL).execute();
II) loadImageInBackground will be a class that implements AsyncTask
a) data members: ImageView thumbnail,String URL
b) Constructor: Initialize these data members with instances passed.
c) onPostExecute: Set a dummy image to the thumbnail here, that will appear until the required image is downloaded.You can even set an animating image here to give it a better feel.
d) doInBackground:
mUrl = new URL(strUrl);
HttpURLConnection conn = (HttpURLConnection) mUrl.openConnection();
conn.setDoInput(true);
conn.connect();
InputStream is = conn.getInputStream();
bmImg = BitmapFactory.decodeStream(is);
is.close();
}
catch (IOException e)
{
e.printStackTrace();
}
e) onPostExecute: associate the this.thumbnail to this.bmImg.
This way for each row of your list view an object of loadImageInBackground class will be initialized and will download the image from URL and set it to the image View container of the corresponding row.
The images will be displayed on the Thumbnail as soon as they are downloaded. There will be no Blocking, I mean, the user can interact with the app even when the images are being downloaded.
If you have implemented a lazy loading methodology, then this approach will not cause any outOfMemory exceptions in any case.And also, new images will only be downloaded when the user scrolls.
Point II.c provides for setting loading animation/image on the thumbnail until the actual image has been downloaded.
I am writing an app that is essentially a flipcard that shows word/hint on one side and picture on other side relevant to it.I am using viewflipper for the two views.Problem is that the picture loads from internet.App access the db,extracts url and then loads picture.That means the change in view takes as much time as it takes to download the picture.I want to flip card immediately and load picture so that user do not thinks that app is slow.Rather they should know that picture is being loaded,hence the delay.Pls suggest improvement in code.My code for loading picture in flipcard is:
public void setBMP(String s) //String passed is url extracted from column of db uing
{ //internal db
try{
//String url1 = "c.getString(3)";
String url1= s;
System.out.println(url1);
URL ulrn = new URL(url1);
HttpURLConnection con = (HttpURLConnection)ulrn.openConnection();
InputStream is = con.getInputStream();
Bitmap bmp = BitmapFactory.decodeStream(is);
if (null != bmp)
{
im.setImageBitmap(bmp);
}
else
System.out.println("The Bitmap is NULL");
}catch(Exception e){}
}
}
For changing view, i have set up actionListener.As soon as user touches screen card flips and image loads.
Also is it possible to preload the images in background while user is viewing some other card.Or is it possible to cache the cards viewed?
I would go the asnyctask route, because that way you can load/disable spinners (or wahtever loading animations) as well. Check out this answer for a really simple example. If you want to add spinners you need to start them in the onPreExecute() of the asnyctask (just add it to the example) and disable them in onPostExecute after you image is downloaded.
Using AsyncTask to load Images in ListView
it seems to me that creating a Runnable that gets the bmp and saves it to a hash map file and then, when it's needed if downloaded, it opens the file, and if not it downloads it from the web.
try looking at fedorvlasov's Lazy adapter for reference.
you can use his Image loader
I am designing an Android application which will be displaying some news that will be retrieved from a remote URL in xml format, say http://adomain/latest.xml
The XML file has the following format:
<articles>
<article>
<id>6</id>
<title>A sample title</title>
<image>http://adomain/images/anImage.jpg</image>
<lastupdate>1326938231</lastupdate>
<content><![CDATA[Sample content]]></content>
</article>
...
</articles>
I have created an Updater Service which listens to Connectivity Changes and when the system has a connection over the internet, it tries to download the xml file. Then parse it and save data. The Updater runs on a separate thread, every 10 minutes.
My question is:
What is the best way to handle the images?
a) Should I perform lazy loading on images when a news item is displayed
OR
b) Should I download the image when I parse the xml file?
I recommend lazy loading as the news item is displayed, so you don't use excessive bandwidth (and potentially cost for the user). No point in downloading images if the user never wants to look at them.
for images, I think you always follow lazy loading because, image loading may take some time, and also an efficient lazy loader can help you to avoid any future memory issue.
Just fetch <Image> tag data from your XMl.
String imgURL = your <Image> value;
ImageView imageView = new ImageView(this);
Bitmap bmp = BitmapFactory.decodeStream(new java.net.URL(imgURL).openStream());
imageView.setId(i);
imageView.setImageBitmap(bmp);
This will work to set image and you also get image in "bmp".
Android provide directly show image from URL:
For Store Image to Sd Card:
File file = new File (pathOfSdCard, iamgeName);
try {
file.createNewFile();
FileOutputStream out = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 10, out);
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
Put Line to your AndroidMenifest:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
This all i using in my app. it works fine.
I hope this will helps you a lot.