I read somewhere (and have observed) that starting threads is slow. I always assumed that AsyncTask created and reused a single thread because it required being started inside the UI thread.
The following (anonymized) code is called from a ListAdapter's getView method to load images asynchronously. It works well until the user moves the list quickly, and then it becomes "janky".
final File imageFile = new File(getCacheDir().getPath() + "/img/" + p.image);
image.setVisibility(View.GONE);
view.findViewById(R.id.imageLoading).setVisibility(View.VISIBLE);
(new AsyncTask<Void, Void, Bitmap>() {
#Override
protected Bitmap doInBackground(Void... params) {
try {
Bitmap image;
if (!imageFile.exists() || imageFile.length() == 0) {
image = BitmapFactory.decodeStream(new URL(
"http://example.com/images/"
+ p.image).openStream());
image.compress(Bitmap.CompressFormat.JPEG, 85,
new FileOutputStream(imageFile));
image.recycle();
}
image = BitmapFactory.decodeFile(imageFile.getPath(),
bitmapOptions);
return image;
} catch (MalformedURLException ex) {
// TODO Auto-generated catch block
ex.printStackTrace();
return null;
} catch (IOException ex) {
// TODO Auto-generated catch block
ex.printStackTrace();
return null;
}
}
#Override
protected void onPostExecute(Bitmap image) {
if (view.getTag() != p) // The view was recycled.
return;
view.findViewById(R.id.imageLoading).setVisibility(
View.GONE);
view.findViewById(R.id.image)
.setVisibility(View.VISIBLE);
((ImageView) view.findViewById(R.id.image))
.setImageBitmap(image);
}
}).execute();
I'm thinking that a queue-based method would work better, but I'm wondering if there is one or if I should attempt to create my own implementation.
The key is explained in the example List13.java
Basically you have to track the scroll state of your listView and notify
the adapter when it is ready to do something of slow with the just visibile
items.
Also note that saving images to the disk is a very slow process. Using a
memory-based cache stategy would improve a lot your perfomance's
application.
I can see you decode image and compress it back to disk, after that you decode it again. Not very effective I think. You could just save the stream from network to disk, after that decompress it. That would be just one decompress instead of 3 compress/decompress. Will save you a lot of CPU processing time.
I think AsyncTask creates several threads for several images. So several images are being compressed/decompressed at the same time, several threads fight for CPU time, not very good. As far as I know AsyncTask uses thread pool, so it doesn't start new thread for each image. But anyway several threads at the same time is not so good. I agree a queue would be much more effective. The implementation is not so hard to create it yourself. I use my own queue implementation and I'm quite happy with it.
If you have your own thread I think it would be possible to give it a lower priority. That will make the UI more responsive.
You certainly need some kind of in-memory cache, otherwise UI can't be fast enough. Decompress is slow. You can store not all images but only most used ones. You may use SoftReference for cache implementation. You may use inSampleSize option to make your bitmaps smaller and occupy less memory Strange out of memory issue while loading an image to a Bitmap object.
I made a complete example of LazyList and posted the source, may also be helpful Lazy load of images in ListView.
Related
I'm using AsyncTask to download images from the Internet. I'm showing them in ViewPager.
I call it from instantiateItem method in MyPagerAdapter using:
downloader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,imageView, url);
My downloader looks like:
protected Bitmap doInBackground(Object... params) {
//(...)
//downloading image using httpConnection and decoding it to bitmap using BitmapFactory
//(...)
Log.d("myApp", "returning");
start = System.currentTimeMillis();
return bitmap;
}
#Override
protected void onPostExecute(Bitmap bitmap) {
long stop = System.currentTimeMillis();
long period = stop-start;
Log.d("myApp-trace","it took: "+period);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
}
}
The problem is, it takes about ~2000 miliseconds between returing from doInBackground and entering onPostExecute.
What is strange, when I swipe the ViewPager later (after the first images are loaded) it works normally and takes ~0-5 miliseconds.
Do you have any ideas what can it be caused by?
Ok, I've found a solution. The problem was in Fragments - when I was starting new activity with ViewPager, it took a lot of time to save old fragments' bundles (there was a list of many objects as Serializable). I noticed that by looking into DDMS method profiling. There was no my own methods, so I thought it may be something with fragments operations.
I am encoding a bitmap to base64.
after i pick the image from image picker, and set it to imageView, i convert it to base64. which i carry out on background thread.
It gives me the same feel as if i am carrying out the operation on UI thread itself. i.e. my UI freezes for 10-20 secs until the encoding is complete.
what can be done? so that my UI doesnt freezes.
where am i going wrong?
My function to display the chosen image in imageView
private void showSelectedImage(Uri selectedImageUri) {
blurred.setImageBitmap(null);
imglocation = selectedImageUri.toString();
Ion.with(this).load(selectedImageUri.toString()).asBitmap().setCallback(new FutureCallback<Bitmap>() {
#Override
public void onCompleted(Exception e, Bitmap result) {
if (e == null) {
blurred.setImageBitmap(result);
blurred.buildDrawingCache();
bitmap = blurred.getDrawingCache();
new Convert2base64().execute(bitmap);
}
}
});
}
This is my AsyncTask
private class Convert2base64 extends AsyncTask<Bitmap, Integer, String> {
protected String doInBackground(Bitmap... urls) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 90, stream);
byte[] image = stream.toByteArray();
String img_str = Base64.encodeToString(image, 0);
return img_str;
}
protected void onPostExecute(String result) {
bio.setText("Base64 encoded String\n" + result);
}
}
EDIT
//blured is my imageView which is just a name rightnow. i am not carrying out the blurring task right now.
I set the image picked by image picker in the Imageview, Thats fine, in the main thread, next two lines, i.e. buildDrawingCache() and getDrawingCache() cannot be called in background thread and is only to be run in the UI thread, I dont get any error in the process, it works as intended. But since i carry the task on the background thread, i am expecting it not to freeze the UI when its converting the image in the background.
Remove the setText from postExecute method, if you are checking the result then rather check in logcat using Log.d("response",result);
The encoded text is a large amount of data, as Marcin correctly pointed out. That large amount you are loading back in the UI thread by calling setText. Thats what is freezing the UI and not the encoding process.
I don't know what the blurred view is, but I assume it's an ImageView that renders the image set to it as blurred. That blurring process is on your UI Thread, and thus causes the freeze.
Your code looks correct, you should profile it to find what takes so long. Ie. by using System.currentTimeMillis(). Actually, what looks strange to me is that you return this image as a string from doInBackground, this will be a very long string - and then you put it into a TextView. Maybe this is what takes so long. So first step is to comment out this setText line in onPostExecute and see if that helps.
i have an android mobile app and im trying to check if a specific LatLng is at water, so im using google static map api to get an image of the location, and then to check if the image is blue.
im using this code -
private boolean result;
public boolean IsWater(LatLng position)
{
imageView = (ImageView) this.findViewById(R.id.imageView);
checkText= (TextView) this.findViewById(R.id.checkText);
String lati = Double.toString(position.latitude);
String longi = Double.toString(position.longitude);
String url = "http://maps.googleapis.com/maps/api/staticmap?center="+lati+"," + longi + "&zoom=20&size=1x1&style=element:labels%7Cvisibility:off&style=element:geometry.stroke%7Cvisibility:off";
Picasso.with(MainActivity.this).load(url)
.into(imageView, new com.squareup.picasso.Callback() {
#Override
public void onSuccess() {
Bitmap bitmap = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
int pixel = bitmap.getPixel(0, 0);
int blueValue = Color.blue(pixel);
if(blueValue>250)
result =true;
}
#Override
public void onError() {
result =false;
}
});
return result;
}
the problem, i think, is that it is not synchronized, and IsWater get to the last line and return a null for result before the onSuccess kicks in...
any thoughts?
Picasso loads images on a background thread by default. The operation you are running is asynchronous. Therefore, it does not block your method from returning result before the onSuccess callback has been called.
The problem is Picasso is running Async. within the calling method "isWater", so what ends up happening is the method will return 'false' because instead of waiting on Picasso to finish because it isn't in serial execution. This is due to the function call's stack frame being popped off the stack once it reaches the return statement.
What you need to do is the following by using a Target.
// make sure to set Target as strong reference
private Target loadtarget;
public void loadBitmap(String url) {
if (loadtarget == null){
loadtarget = new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
// do something with the Bitmap
handleLoadedBitmap(bitmap);
}
#Override
public void onBitmapFailed() {
}
};
}
Picasso.with(this).load(url).into(loadtarget);
}
public void handleLoadedBitmap(Bitmap b) {
// do something here
}
This code was taken from here, and should offer you some insight on how to get it work for your goal.
A Target is essentially an object that holds the bitmap you need so it is still in memory. Generally used for custom view objects though as a field. Here is documentation Target docs
Asynchronous execution is one of the hardest things to wrap ones head (and subsequently ones code) around. In all of the JavaScript frameworks I've used, the network communication is done in a background thread. The intended effect is that the User Interface thread is left free to keep the user from thinking that things locked up. Mouse-overs and tool-tips will all still work, while a background thread is dragging data out of a slow server.
The code patterns, on the other hand, aren't as nicely shaped.
My problem is/was still basically thinking linearly, or functionally, instead of embracing the event-driven nature of modern JavaScript: Passing a function to an asynchronous method to completely handle that response. Not just return a value, but perform the full task that the value was needed for. The callback can call the other functions to assist with that task, and may be able to fill in a cache (of whatever sort) so that other functions that may need this data do not necessarily have to wait for another response. This often (to me) feels backwards from the logic pattern I was following to solve the original purpose of the code.
I've stumbled on this pattern-flip many times, coming from C/C++ as my first programming language. It can sometimes help to avoid the anonymous function pattern of callback definition and define one's callback functions with names, then pass the name to the asynchronous call, but that is extra steps and extra memory use in the long run. The big hurdle is thinking in terms of Event and EventHandler, versus function and data.
I hope this helps a little.
Is there any way that I can increase the speed of setting the image? What I am trying to say is suppose I have some hundreds of images and each of those images can be changed automatically or by pressing forward/rewind buttons. (Think of the media player situation where you can forward or rewind as many files, same I am doing with images and some audio behind).
Now, if I continuously keep pressing RW/FW buttons, the layout appears to be black for some time or asks for force close or wait of the application.
Any one know how to increase this speed of reading the bitmap and setting it to the imageview?
Here is a part of my code,
private void playAudio() {
msg = new Message();
msg.obj = (filepath);
imageHandler.sendMessage(msg);
}
private Handler imageHandler = new Handler() {
public void handleMessage(Message msg) {
try {
path = (String) msg.obj;
imagePath = RaconTours.PATH + path;
imagenamearray = imagePath.split("/");
currentImagename = imagenamearray[8];
i++;
if (!imagePath.equalsIgnoreCase(staticpath)) {
isSeekBarChangedManually = false;
staticpath = imagePath;
Bitmap snoop = readBitmap(Uri.fromFile(new File(filepath)));
image.setImageBitmap(snoop);
image.setMaxZoom(4f);
} catch (Exception e) {
e.printStackTrace();
}
}
};
Hope you get some idea with this code, this is just a small piece of the big cake.
I am not getting the method to solve this. I tried disabling the button also until some action is being performed but that is also of no use.
Any help, cheers
I think the only way to make this process "faster" is to load and cache the bitmaps in a buffer before you actually need them. When you need a bitmap, you check this forward-cache to see if it's already loaded. If it is, then it means it's already in memory -> you just need to set it for the proper ImageView (or whatever you use). If not, then you load it from the disk.
Of course, you need to know that which bitmaps you'll need most likely in the near future for this to work.
You can't really make disk I/O work any faster you see.
My code mainly decodes a series of bitmap objects to be displayed in an ImageView later. To get more control over the duration to display each image. So i implemented the code in a Runnable. Here is a snippet of that runnable.
final File file[]=dir.listFiles();
iv.postDelayed(
new Runnable() {
#Override
public void run() {
i++;
Bitmap bitmap = decodeFile(file[i]);
iv.setImageBitmap(bitmap);
iv.postDelayed(this, 5000);
}
}, 5000);
However, I don't seem to have a good control over the looping here. I am not able to use a for loop since "i" cant be assigned to zero in this case. This code works as long as the "file[i]" has a File reference to it. As soon as it displays all the images once, the app just force closes do to arrayindexoutofbounds exception. I just want to continue the loop infinitely. Any pointers?
Here decodeFile() calls another function to rescale the size of the bitmap.
I'm not really sure what you are doing, but why not use a while loop instead? Something like while (true) { } would cause the infinite loop.
sense you want to do some work that might take a while and you want do repeat it, try looking at an android component called service and send it the images by intent.
http://developer.android.com/guide/topics/fundamentals/services.html
probably an IntentService would be fine, and help alot
http://developer.android.com/reference/android/app/IntentService.html
I suggest an AsyncTask
File means a file array is given the task to be processed in the background.
Bitmap means you call publishProgress with your bitmap, you fetch it on the UI thread in onProgressUpdate
Void means you simply stop once it is done.
The good thing here is that the decoding is done on a background thread, but the iv.setBitmap is still called on the UI thread, which is mandatory.
Just reset to zero once you reach the end:
final File file[]=dir.listFiles();
iv.postDelayed(
new Runnable() {
#Override
public void run() {
if (i == file.length) {
i = 0;
}
else {
i++;
}
Bitmap bitmap = decodeFile(file[i]);
iv.setImageBitmap(bitmap);
iv.postDelayed(this, 5000);
}
}, 5000);