Why does Picasso running in a background thread block onActivityResult? - android

We are working on a share mode for photos in an app, and one of the share options is to load an overlay for the image online. If the user shares the photo before the overlay finishes loading we are sending an intent with the unmodified image to share to other apps with startActivityForResult, and then in onActivityResult we are returning the user to the normal photo view from the share view.
The issue we are running into is that when the user returns from sharing the photo, onActivityResult isn't called until the background thread that is loading the overlay finishes. Is there a reason that a background thread would suddenly start to block the UI thread?
Here is the code we are using inside of our overlay view class which extends ImageView to manage this:
private void asyncLoadImage() {
new Thread(new Runnable() {
#Override
public void run() {
loadImage();
}
}).start();
}
private void loadImage() {
try {
overlayBitmap = Picasso.with(getContext()).load(url).get();
if (overlayBitmap != null) {
displayBitmap();
} else {
displayError();
}
} catch (IOException ignored) {
displayError();
}
}
We have tried using Picasso targets as well instead of creating our own thread and we run into the exact same issue, the only solution so far has been to use Ion asynchronously (however, using Ion synchronously or trying to cancel the Ion request futures before we call startActivityForResult result in the same UI thread blocking issues). This feels like a huge hack since we are using Picasso everywhere else in the app.
Is there a reason why these background tasks would be blocking the UI thread when returning to the activity?

Looking at your code you are creating a new thread when you don't need to. Picasso has a configurable executor method for threading.
I had an issue when loading over 100+ images using picasso and it would lock up and block the UI Thread on me but this was due to it creating a new Thread every time I called picasso to get an image. I solved this by doing a little research and found that within picasso there is a configurable executor method.
This is my ImageHandler class implementation
public class ImageHandler {
private static Picasso instance;
public static Picasso getSharedInstance(Context context)
{
if(instance == null)
{
instance = new Picasso.Builder(context).executor(Executors.newSingleThreadExecutor()).memoryCache(Cache.NONE).indicatorsEnabled(true).build();
return instance;
}
else
{
return instance;
}
}
}
I don't know that this is your issue, but it would be worth a try if you haven't implemented it yet.
Here is the way I use it to load images
ImageHandler.getSharedInstance(getApplicationContext()).load(imString).skipMemoryCache().resize(width, height).into(image, new Callback() {
#Override
public void onSuccess() {
layout.setVisibility(View.VISIBLE);
}
#Override
public void onError() {
}
});

Related

Glide cache image on disk on background thread

I want to use Glide to eagerly download images and cache them on disk for a future use. I want to call this functionality from a background thread.
I've read Glide's caching documentation, but it doesn't explain how to download the image without having an actual target right now. Then I found this issue and tried to use a similar approach, but whatever I try I get this exception:
java.lang.IllegalArgumentException: You must call this method on the main thread
So, how can I tell Glide to cache an image from a background thread?
EDIT: I really want to call Glide's methods on background thread. I know that I can use Handler and other ways to offload it to UI thread, but that's not what I'm asking.
GlideApp.with(context)
.downloadOnly()
.diskCacheStrategy(DiskCacheStrategy.DATA) // Cache resource before it's decoded
.load(url)
.submit(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
.get() // Called on background thread
If want to load images in cache for future use in background thread then Glide have this functionality
here how you can do this
//here i passing application context so our glide tie itself with application lifecycle
FutureTarget<File> future = Glide.with(getApplicationContext()).downloadOnly().load("your_image_url").submit();
now if you want to retrieve the saved path that you want to store in your DB then you can do
File file = future.get();
String path = file.getAbsolutePath();
you can also do this in just one line returning a path string like this
String path = Glide.with(getApplicationContext()).downloadOnly().load("your_image_url").submit().get().getAbsolutePath();
You must call this method on the main thread
Wherever you call a method to use Glide or doing something from background, run inside:
runOnUiThread(new Runnable() {
#Override
public void run() {
// Here, use glide or do your things on UiThread
}
});
To use it inside the main thread and then error should be gone.
Did I get the question right?
private class update extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Void doInBackground(Void... params) {
try {
RequestOptions options = new RequestOptions()
.diskCacheStrategy(DiskCacheStrategy.ALL) // Saves all image resolution
.centerCrop()
.priority(Priority.HIGH)
.placeholder(R.drawable.null_image_profile)
.error(R.drawable.null_image_profile);
Glide.with(context).load(imageUrl)
.apply(options);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
//finish
}
}

OnCreate() in a class (extend Application) is called very slowly

I call a method(below deleteCache method) in OnCreate() in a class which extend Application class. This method is for deleting files. It uses Thread because I want this method won't affect UI flow.
public static void deleteCache(final Context context) {
new Thread(new Runnable() {
#Override
public void run() {
try {
File dir = context.getCacheDir();
deleteDir(dir);
} catch (Exception e) {}
}
}).start();
}
But with this, the OnCreate() method in App class(extend Application) is called very slowly. So when I launch the app, it takes about 5 seconds to show a first Activity.
I don't know why this happen.
Please give me a any hint of this problem.
As #Umer said, use AsyncTask if you run the in the UI-Thread. For Reference:
https://developer.android.com/reference/android/os/AsyncTask.html
Otherwise, if that won`t solve your problem, maybe your function is too complex or the data that will be deleted are very large.

How to update SurfaceView from another Thread?

I have a thread where I will get some Bitmap one by one. Then whenever I get a new Bitmap I need to update my SurfaceView which in the main thread. How can I do this?
Here is some pseudo code
new Thread() {
public void run() {
while (bitmapIncoming()) {
Bitmap bitmap = getNextBitmapAfterLongOperation();
mActivityReference.runOnUiThread(new Runnable() {
public void run() {
mActivityReference.updateSurfaceView(bitmap);
}
});
}
}
}.start();
In the thread, make a public interface which defines a function which is called every time a bitmap is retrieved(from the internet i assume) like:
public interface ImageRetrievedListener{
public void onImageReceived(Bitmap bitmap);
}
Implement this method inside your Activity where you are using the Surface View and in the onImageReceived callback, add the Bitmap received into the array of Bitmaps which the SurfaceView uses. Should work, if you still have any problems, do let me know.
//EDIT
Not sure if this is the best possible solution for this problem, but i'd solve it this way.

Android - Thread pool strategy and can Loader be used to implement it?

First the problem:
I'm working on the application that uses multiple FragmentLists
within a customized FragmentStatePagerAdapter. There could be,
potentially substantial number of such fragments say between 20 and 40.
Each fragment is a list in which each item could contain text or image.
The images need to be uploaded asynchronously from the web and cached to temp memory cache and also to SD if available
When Fragment goes off the screen any uploads and current activity should be cancelled (not paused)
My first implementation followed well known image loader code from Google. My problem with that code is that it basically creates one instance of AsyncTask per image. Which in my case kills the app real fast.
Since I'm using v4 compatibility package I thought that using custom Loader that extends AsyncTaskLoader would help me since that internally implements a thread pool. However to my unpleasant surprise if I execute this code multiple times each following invocation will interrupt the previous. Say I have this in my ListView#getView method:
getSupportLoaderManager().restartLoader(0, args, listener);
This method is executed in the loop for each list item that comes into view. And as I stated - each following invocation will terminate the previous one. Or at least that's what happen based on LogCat
11-03 13:33:34.910: V/LoaderManager(14313): restartLoader in LoaderManager: args=Bundle[{URL=http://blah-blah/pm.png}]
11-03 13:33:34.920: V/LoaderManager(14313): Removing pending loader: LoaderInfo{405d44c0 #2147483647 : ImageLoader{405118a8}}
11-03 13:33:34.920: V/LoaderManager(14313): Destroying: LoaderInfo{405d44c0 #2147483647 : ImageLoader{405118a8}}
11-03 13:33:34.920: V/LoaderManager(14313): Enqueuing as new pending loader
Then I thought that maybe giving unique id to each loader will help the matters but it doesn't seem to make any difference. As result I end up with seemingly random images and the app never loads even 1/4 of what I need.
The Question
What would be the way to fix the Loader to do what I want (and is there a way?)
If not what is a good way to create AsyncTask pool and is there perhaps working implementation of it?
To give you idea of the code here's stripped down version of Loader where actual download/save logic is in separate ImageManager class.
public class ImageLoader extends AsyncTaskLoader<TaggedDrawable> {
private static final String TAG = ImageLoader.class.getName();
/** Wrapper around BitmapDrawable that adds String field to id the drawable */
TaggedDrawable img;
private final String url;
private final File cacheDir;
private final HttpClient client;
/**
* #param context
*/
public ImageLoader(final Context context, final String url, final File cacheDir, final HttpClient client) {
super(context);
this.url = url;
this.cacheDir = cacheDir;
this.client = client;
}
#Override
public TaggedDrawable loadInBackground() {
Bitmap b = null;
// first attempt to load file from SD
final File f = new File(this.cacheDir, ImageManager.getNameFromUrl(url));
if (f.exists()) {
b = BitmapFactory.decodeFile(f.getPath());
} else {
b = ImageManager.downloadBitmap(url, client);
if (b != null) {
ImageManager.saveToSD(url, cacheDir, b);
}
}
return new TaggedDrawable(url, b);
}
#Override
protected void onStartLoading() {
if (this.img != null) {
// If we currently have a result available, deliver it immediately.
deliverResult(this.img);
} else {
forceLoad();
}
}
#Override
public void deliverResult(final TaggedDrawable img) {
this.img = img;
if (isStarted()) {
// If the Loader is currently started, we can immediately deliver its results.
super.deliverResult(img);
}
}
#Override
protected void onStopLoading() {
// Attempt to cancel the current load task if possible.
cancelLoad();
}
#Override
protected void onReset() {
super.onReset();
// Ensure the loader is stopped
onStopLoading();
// At this point we can release the resources associated with 'apps'
// if needed.
if (this.img != null) {
this.img = null;
}
}
}
Ok, so first things first. The AsyncTask that comes with android shouldn't drown out your app or cause it to crash. AsyncTasks run in a thread pool where there is at most 5 threads actually executing at the same time. While you can queue up many tasks to be executed , only 5 of them are executing at a time. By executing these in the background threadpool they shouldn't have any effect on your app at all, they should just run smoothly.
Using the AsyncTaskLoader would not solve your problem if you are unhappy with the AsyncTask loader performance. The AsyncTaskLoader just takes the loader interface and marries it to an AsyncTask. So it's essentially mapping onLoadFinished -> onPostExecute, onStart -> onLoadInBackground. So it's the same exact thing.
We use the same image loader code for our app that causes an asynctask to be put onto the threadpool queue each time that we try to load an image. In google's example they associate the imageview with its async task so that they can cancel the async task if they try to reuse the imageview in some sort of adapter. You should take a similar strategy here. You should associate your imageview with the async task is loading the image in the background. When you have a fragment that is not showing you can then cycle through your image views associated with that fragment and cancel the loading tasks. Simply using the AsyncTask.cancel() should work well enough.
You should also try to implement the simple image caching mechanism the async image view example spells out. We simply create a static hashmap that goes from url -> weakreference . This way the images can be recycled when they need to be because they are only held on with a weak reference.
Here's an outline of the image loading that we do
public class LazyLoadImageView extends ImageView {
public WeakReference<ImageFetchTask> getTask() {
return task;
}
public void setTask(ImageFetchTask task) {
this.task = new WeakReference<ImageFetchTask>(task);
}
private WeakReference<ImageFetchTask> task;
public void loadImage(String url, boolean useCache, Drawable loadingDrawable){
BitmapDrawable cachedDrawable = ThumbnailImageCache.getCachedImage(url);
if(cachedDrawable != null){
setImageDrawable(cachedDrawable);
cancelDownload(url);
return;
}
setImageDrawable(loadingDrawable);
if(url == null){
makeDownloadStop();
return;
}
if(cancelDownload(url)){
ImageFetchTask task = new ImageFetchTask(this,useCache);
this.task = new WeakReference<ImageFetchTask>(task);
task.setUrl(url);
task.execute();
}
......
public boolean cancelDownload(String url){
if(task != null && task.get() != null){
ImageFetchTask fetchTask = task.get();
String downloadUrl = fetchTask.getUrl();
if((downloadUrl == null) || !downloadUrl.equals(url)){
fetchTask.cancel(true);
return true;
} else
return false;
}
return true;
}
}
So just rotate through your image views that are in your fragment and then cancel them when your fragment hides and show them when your fragment is visible.

AsyncTask result differs from regular function

I have a View that recieves swipe gestures.
this should trigger the creation and setContentView(layout) for the main activity.
(the View is within a Layout on the Main activity)
When i try to do this within an asynctask it leaves out all the images for the layout?
as if it hasn't finished yet .
There is prolly something i'm not fully understanding .
this the partial code from the view.java
Main.mHandler.post(new Runnable()
{
public void run()
{
new Main.GetNavigationLayout().execute(url);
}
});
the url is a location of an .xml file I use to create a new layout.
GetNavigationLayout is the AsyncTask
code that is the AsyncTask in Main.java :
public static class GetNavigationLayout extends AsyncTask<String, Void, CustomLayout> {
boolean isDone = false;
#Override
protected EvadoLayout doInBackground(String... url)
{
CustomLayout layout = new CustomLayout(Main);
Looper.prepare();
try
{
String newpage = url[0];
Document doc = XmlImporter.GetNewPlayer(newpage);
if (doc == null)
{
layout = null;
}
layout.LoadXml(doc); // cause drawing of objects etc.
}
catch (Exception e)
{
e.printStackTrace();
}
isDone = true;
//Looper.loop(); // causes it to never return...
return layout;
}
#Override
protected void onPostExecute(CustomLayout layout)
{
if (isDone)
Main.setContentView(layout);
}
}
now this shows everythig besides images , whereas if i run this without AsyncTask it displays everything in the layout.
what am i missing?
layout.LoadXml(doc); // cause drawing of objects etc.
I reckon you are drawing the images here? I think this is the problem. Trying to draw from the AsyncTask's doInBackground() is wrong, since it is not the UI thread. You should do the drawing in onPostExecute() which runs in the UI thread
You can post to the UI in the onProgressUpdate(). Using this method will allow you to stay inside the doInBackground and post an update to the UI when you get a layout and continue inside the doInBackground. Use publishProgress() to send information from the doInBackground to the UI thread.

Categories

Resources