I have two operations that I need to perform on 400 large image files. The files are being downloaded over a private network to folderA.
Operation 1: move the files from folderA to folderB
Operation 2: resize each file
The thing is I have a small time window for moving the files from folderA to folderB, in fact as soon as a photo hits folderA it needs to be in folderB. What is my best approach for doing this? Right now I am considering two avenues.
Approach 1 (what I have now, too slow):
Use a FileObserver to track when photos are in folderA
Use an IntentService to: get image from folderA, resize image, then save new image to folderB
Approach 2 (wondering):
Use a FileObserver to track when photos are in folderA
Use a service with a custom Handler that allows parallel threads and for each photo, create a new thread to do: get image from folderA, resize image, then save new image to folderB
Approach 3:
Use a FileObserver to track when photos are in folderA
Use an IntentService to: for-each file, first move the file from folderA to folderB; then start another IntentService to resize the file.
I already know that approach 1 is not fast enough as it is the one I am now using. So, between approach 2 and 3, which one is faster and why? For example we are talking about 400 large image files. Does the OS impose limits on how many concurrent threads a single service can have? Should I worry about increasing thread pool? How about in case 3 where I am using two services (MoveIntentService and ResizeIntentService), how do I tell android to run “all” the calls to MoveService before the calls to ResizeService?
I understand that ResizeIntentService queues all the calls. But I am okay with that. I just need for MoveIntentService to move the files very very very quickly.
Also, although I say 400 files, I don't really know how many files and so I cannot wait for folderB to be full before I start the resize portion. I am also aware of the case where I can use a single IntentService to first move a file and then start a thread to resize the file.
Again, is it cheap to move a file in android (i.e. pointer versus actual re-copy)? I am here assuming that it is very cheap to move a file relative to resizing a file.
BTW both folderA and folderB are some level under Environment.getExternalStorageDirectory()
Related
When I use ListView the getView() method is called many times. Every time when the getView() is called i load the image with Asyc task. I mean every time i reset the image which is annoying.
How to understand when to load the image?
You should cache loaded images, by storing i.e. on SD card, so once you got a copy there, no need to download it again. There's lot of ready-to-use classes that can do the job for you, like:
http://greendroid.cyrilmottier.com/reference/greendroid/widget/AsyncImageView.html
you must must have two flags.
One which says if you've already loaded the image, if true you do nothing.
One which says if you're currently loading the image, if true you do nothing.
The members will also help you on maintaining the state of the image.
Your code should look something like this:
private boolean isLoading = false;
private boolean hasLoaded = false;
if(!hasLoaded){
if(!isLoading){
isLoading = true;
//do async load
//on positive completition callback set hasLoaded to true
//on negative completition callback set isLoading to false
}
}
One of the best solution is to create image cache using the WeakReference. This way you can keep images in memory and only need load from server when they are not in memory. In this method the image would be removed from the memory when system encounter low memory situation. So your current activity would always keep the hard reference to the bitmap's required and the image cache would keep the weak reference to the bitmap's.
below reference links will help you
http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html
http://www.codeproject.com/Articles/35152/WeakReferences-as-a-Good-Caching-Mechanism
the Volley library (made by google) has a very intuitive class for an imageView that can have a url , called "NetworkImageView" .
you should check it out and watch the video, since they show that it's quite annoying to do it using asyncTask (plus the asyncTask is known to have a limit of tasks, about 255 or so) .
for setting the url, just use setImageUrl .
it has some useful methods for the phases of loading too: setDefaultImageResId , setErrorImageResId.
it's also supposed to have built in caching mechanism of some sort, but i haven't read much about it, so you might want to check out their samples.
this will remove the need to use asyncTasks for the listView's items.
one of my questions regarding the volley includes a sample code , here .
You can add a caching layer and optionally preloading the images. A good strategy for caching Images (Bitmap objects to be exact) is to use a strategy called LRU or least recently used.
Android support library has a class called LruCache that implements this strategy. So for example, when you download/load the image for the first time, you stick it into the cache. later, you can first check if it's already in cache and load it from there.
For preloading, A good rule of thumb is to preload the previous ten and the next ten items.
I have an application that uses a flip-book style animation with thousands of images. APK size isn't an issue due to it being an internal only application that will never be put on any app store.
The issue I am having is my animator is a sub-classed ImageView that switches out images at 15 frames per second and each time I call setImageURI Garbage Collection then runs. So is there an alternative method to setImageURI that wont cause Garbage Collection to be run?
Edit: A little more background information.
My app has ~12 sequences and 6 of them contain 1609 Images and different events need to be called at certain frames. Another need is for the user to be able to stop on any frame to survey the situation and either preform an action or continue down the sequence. A 3D engine would have been the ideal solution but this method was brought up as the user doesn't need complete freedom and is on a "rail" the entire time.
This is the meat of the application and where the problem occurs
try {
((BitmapDrawable) getDrawable()).getBitmap().recycle();
setImageURI(imgUri);
refreshDrawableState();
} catch (Exception e) {
e.printStackTrace();
}
The Garbage Collector is running because you're exchanging one image for another which dumps the previous. It's going to happen every time you lose references to the images.
Here are a couple possible methods to consider.
Since you mentioned it's a flipbook, build and assign an AnimationDrawable to the ImageView which will act as a frame-by-frame animation (exactly what a flipbook is). This loads all the images at once and will keep them around until you lose a reference to the AnimationDrawable.
Store all the images as SoftReferences to cache the images. A SoftReference will keep the object in memory until memory is needed to be collected. This will slow the frequency of the garbage collector (assuming this is the cause). Use BitmapFractory to build the image and setImageBitmap to assign it to the ImageView. The other advantage of this is it allows you to build images on threads before you assign it to the ImageView.
Maybe it's because your Image need to much memory, and the android need call the GB to free more mem trying to avoid crashing your program
In my android application the scenario is this -
I have several images stored in the SD card.
Now in my app I have an Image view and I have to update this image view continuously
like a slide show is running. This has to be done by fetching images one by one from SD card and setting them in the same ImageView. Also the interval of image change in ImageView is fixed.
So I have a cursor which stores the information for fetching these images.
Now I am wondering what should be my approach for moving the cursor one by one and fetching images from SD card and setting them in my ImageView.
Should I use Timer Task and Timer or should I go with Handler or something else?
Please help as this is initial point of my app and I want to start in the right direction.
Since you're retrieving large data from the SD card, it might be best to use a thread to retrieve/decode the bitmap.
In this case, a Timer would be useful because it runs on it's own Java thread for scheduled periods of time. However, you have to keep in mind that you can't update a UI element from a non-UI thread. Thus, you'd need a syncing element in place for when the bitmap is fully loaded to finally put the decoded bitmap in to the ImageView. In which case, you would need to use Handler anyway to send message queues to the UI thread.
Thus, I'd probably recommend using a combination of Handler and AsyncTask. Create an AsyncTask class that retrieves and decodes the bitmap in doInBackground(). Update the ImageView in onPostExecute(). Use a Handler to create and execute a new AsyncTask ever Nth milliesecond (whenever you update the view).
One advantage to this is it keeps all image retrieval in one class. You can open the cursor, retrieve, close it. This is safer than keeping a long-running cursor open.
Could someone tell me how to make a good mechanism for async. download of images for use in a ListView/GridView?
There are many suggestions, but each only considers a small subset of the typical requirements.
Below I've listed some reasonable factors (requirements or things to take into account) that I, and my collegues, are unable to satisfy at once.
I am not asking for code (though it would be welcome), just an approach that manages the Bitmaps as described.
No duplication of downloaders or Bitmaps
Canceling downloads/assigning of images that would no longer be needed, or are likely to be automatically removed (SoftReference, etc)
Note: an adapter can have multiple Views for the same ID (calls to getView(0) are very frequent)
Note: there is no guarantee that a view will not be lost instead of recycled (consider List/GridView resizing or filtering by text)
A separation of views and data/logic (as much as possible)
Not starting a separate Thread for each download (visible slowdown of UI). Use a queue/stack (BlockingQueue?) and thread pool, or somesuch.... but need to end that if the Activity is stopped.
Purging Bitmaps sufficiently distant from the current position in the list/grid, preferably only when memory is needed
Calling recycle() on every Bitmap that is to be discarded.
Note: External memory may not be available (at all or all the time), and, if used, should be cleared (of only the images downloaded here) asap (consider Activity destruction/recreation by Android)
Note: Data can be changed: entries removed (multi-selection & delete) and added (in a background Thread). Already downloaded Bitmaps should be kept, as long as the entries they're linked to still exist.
setTextFilterEnabled(true) (if based on ArrayAdapter's mechanism, will affect array indexes)
Usable in ExpandableList (affects the order the thumbnails are shown in)
(optional) when a Bitmap is downloaded, refresh ONLY the relevant ImageView (the list items may be very complex)
Please do not post answers for individual points. My problem is that that the more we focus on some aspects, the fuzzier others become, Heisenberg-like.
Each adds a dimension of difficulty, especially Bitmap.recycle, which needs to be called during operation and on Activity destruction (note that onDestroy, even onStop might not be called).
This also precludes relying on SoftReferences.
It is necessary, or I get OutOfMemoryError even after any number of gc, sleep (20s, even), yield and huge array allocations in a try-catch (to force a controlled OutOfMemory) after nulling a Bitmap.
I am resampling the Bitmaps already.
Check this example. As Its is used by Google and I am also using the same logic to avoid OutOfMemory Error.
http://developer.android.com/resources/samples/XmlAdapters/index.html
Basically this ImageDownlaoder is your answer ( As It cover most of your requirements) some you can also implement in that.
http://developer.android.com/resources/samples/XmlAdapters/src/com/example/android/xmladapters/ImageDownloader.html
In the end, I chose to disregard the recycling bug entirely. it just adds a layer of impossible difficulty on top of a manageable process.
Without that burden (just making adapters, etc stop showing images), I made a manager using Map<String, SoftReference<Bitmap>> to store the downloaded Bitmaps under URLs.
Also, 2-4 AsyncTasks (making use of both doInBackground and onProgressUpdate; stopped by adding special jobs that throw InterruptedException) taking jobs from a LinkedBlockingDeque<WeakReference<DownloadingJob>> supported by a WeakHashMap<Object, Set<DownloadingJob>>.The deque (LinkedBlockingDeque code copied for use on earlier API) is a queue where jobs can leave if they're no longer needed. The map has job creators as keys, so, if an Adapter demands downloads and then is removed, it is removed from the map, and, as a consequence, all its jobs disappear from the queue.
A job will, if the image is already present, return synchronously. it can also contain a Bundle of data that can identify which position in an AdapterView it concerns.
Caching is also done on an SD card, if available, under URLEncoded names. (cleaned partially, starting with oldest, on app start, and/or using deleteOnExit()
requests include "If-Modified-Since" if we have a cached version, to check for updates.
The same thing can also be used for XML parsing, and most other data acquisition.
If I ever clean that class up, I'll post the code.
I currently have a ListView with a custom adapter that gets information describing the content of the rows asynchronously. Part of each row is an image URL, that I'm planning to download asynchronously and then display.
My current plan for a strategy to download these images is:
Keep a cache of soft references to downloaded Bitmap objects.
When a getView() is called and the bitmap is in the cache, set the bitmap for the ImageView directly.
If the bitmap isn't in the cache, start loading it in a separate thread, after the download is complete add it to the cache and call notifyDataSetChanged() on the adapter.
I am also planning to kill pending downloads when the Activity object owning the ListView's onDestroy()-method (Or possibly even in the onPause()-method) is called, but most importantly I want to kill the download of pending images when the row goes off screen. I might only actually cancel the download after a short delay, so it can be resumed without wasting bandwidth if the row comes on-screen quickly again.
I, however, am unsure about a few things:
What is the best way to detect when a row goes off-screen so I can cancel the download?
Is calling notifyDataSetChanged() the best thing to do after the download has completed or is there a better way?
Also any comments on the whole strategy would be appreciated.
I don't think calling notifyDataSetChanged() is really needed... I would do it like that:
store URL as Tag in the view when created/updated
register a listener in downloader thread (async task???) for download keeping reference to the view and the URL
whenever image is downloaded asynchronously, I check TAG in the view and if it matches - i would update the ImageView (important to do it in UI thread, but when using async task, it is given). The image should also be stored on SD card (and every time you request URL you should check if it is not already downloaded).
every time when getView() reuses the view (passed view is not empty) I would check the Tag (old URL), replace it with the new URL and cancel the download of the oldURL.
I think it would be pretty much it (some corner cases might happen)...
I use the getFirstVisible and getLastVisible AdapterView properties to detect the visible rows, and put requests in a fixed size stack.
My project is open source and has a most permissive license, if you want to use it:
https://github.com/tbiehn/Android-Adapter-Image-Loader
-Travis
I found the remote resource managing / fetching in the Foursquared source code to be pretty helpful:
http://code.google.com/p/foursquared/source/browse/main/src/com/joelapenna/foursquared/util/RemoteResourceManager.java
It caches images on disk and handles all 3 of your feature requests. See an adapter for how to use it.
As for canceling a download when a row goes off screen you'll have to handle that yourself