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
Related
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 have an image located at an HTTP url , when i try to load this in a webview inside an activity it doesn't take much time to load , but when i am trying to set it to an ImageView by creating a bitmap as below
fis = new FileInputStream(f);
fis = new java.net.URL(filePath).openStream();
bfis = new BufferedInputStream(fis);
b = BitmapFactory.decodeStream(fetch(filePath), null, o2);
fis.close();
It takes a lot of time , How can I efficiently set the image to ImageView ?
Also can u explain why it loads quickly in webView while it takes a lot of time in while fetching it from inputstream
UrlImageViewHelper will automatically download, save, and cache all
the image urls the BitmapDrawables. Duplicate urls will not be loaded
into memory twice. Bitmap memory is managed by using a weak reference hash table, so as soon as the image is no longer used by you, it will be garbage collected automatically.
https://github.com/koush/UrlImageViewHelper
I have an Android Gallery widget that displays several ImageViews with pictures from the web. The images are stored as jpgs, and get converted to bitmap before added to the ImageViews. Each jpg is 8kb. I'm not doing any scaling on the images.
When I use the gallery with 1 or 2 pictures, it works fine, scrolling is smooth, etc. With 3, it starts to get a little choppy, and at 5 or 10 pictures the application is pretty much unusable.
Does anyone have any ideas as to why the performance is so bad? Does anyone have suggestions for alternatives? Thank you-
#elevine: my method to construct bitmap from jpg url:
private Bitmap getBitmapFromURL(String input) throws IOException {
URL url = new URL(input);
URLConnection conn = url.openConnection();
conn.connect();
InputStream is = conn.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is);
Bitmap bm = BitmapFactory.decodeStream(bis);
bis.close();
is.close();
return bm;
}
This is my getView method from my ImageAdapter. I'm beginning to suspect this is where my problem lies... Am I grabbing the images way too many times? Thanks for your help!
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView = new ImageView(mContext);
Bitmap bm;
try {
imageView.setImageBitmap(getBitmapFromURL(urls.get(position)));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//mageView.
imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
imageView.setLayoutParams(new Gallery.LayoutParams(100,100));
return imageView;
}
If you are calling getBitmapFromURL from your Activity, then it might be blocking the UI thread. Make sure this code runs on a separate Thread or inside an AsyncTask. You may also want to cache the Bitmaps inside a WeakHashmap. Check for the image inside the cache before grabbing it from the network.
Here are some tip itthat might come handy:
Try to cache the image first on memory with WeakReference and also on disk (if its possible) so you don't have to waste mobile resources on downloading the image all over again.
Performance could be bad if you override the GalleryAdapter and you are not helping the adapter to recycle your views List items
Also try to execute download operation on a different Thread, consider using AsyncTask.
Here is an interesting ImageManager you might take use
One other item to consider as well is the animationDuration. You can set this to 0 and it will help with the scrolling performance. I needed to do this recently with a Google TV app I'm working on otherwise there was a second or two pause. Pre-caching your images is also recommended and reducing the number of images that are displayed at a time if possible.
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