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
}
}
Related
I'm having a little trouble with setting my device wallpaper while having my Firebase Storage code inside and AsyncTask.
Firstly, is it proper to have this code contained in an AsyncTask at all?
Secondly, the wallpaper is successfully downloaded and set, but for some reason the AsyncTask isn't waiting for that to complete before dismissing the ProgressDialog and showing a Toast telling me that the wallpaper has been set successfully. What might be going wrong here?
Thirdly, even though the code is within doInBackground() of the AsyncTask, it doesn't seem like it's actually running separate from the main thread, because my app completely freezes while the code is being run (and eventually unfreezes when it's finished).
Any insight for me? I'm learning as I go and am pretty confused.
Here's the code for reference below.
private class wallpaperTaskHome extends AsyncTask<Void, Void, String> {
private ProgressDialog dialog = new ProgressDialog(PhotoDetailsActivity.this);
onPreExecute()
#Override
protected void onPreExecute() {
// Progress dialog to show user that the app is working...
dialog.setCancelable(false);
dialog.setCanceledOnTouchOutside(false);
dialog.setMessage(getString(R.string.wallpaper_please_wait));
dialog.show();
}
doInBackground()
#Override
protected String doInBackground(Void... params) {
// Get Intent data from intent passed from Photos.java class.
Intent intent = getIntent();
int position = intent.getExtras().getInt("Image int");
final StorageReference fullSizeImagesStoragePath = mStorageRef.child("images_full_size/" + position + ".jpg");
fullSizeImagesStoragePath.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
#Override
public void onSuccess(Uri uri) {
// Retrieve a WallpaperManager
final WallpaperManager wallpaperManager = WallpaperManager
.getInstance(PhotoDetailsActivity.this);
Glide
.with(PhotoDetailsActivity.this)
.asBitmap()
.load(uri)
.into(new SimpleTarget<Bitmap>() {
#Override
public void onResourceReady(#NonNull Bitmap resource, #Nullable Transition<? super Bitmap> transition) {
try {
wallpaperManager.setBitmap(resource, null, true, WallpaperManager.FLAG_SYSTEM);
} catch (IOException e) {
}
}
});
}
});
return null;
}
onPostExecute()
protected void onPostExecute(String result) {
if (dialog.isShowing()) {
dialog.dismiss();
}
// Show a Toast message on successful wallpaper change
showToastWallpaperApplied();
}
}
Thanks for any help you could provide me with!
EDIT: I've changed my doInBackground() method to look like below, which essentially does the same thing, but now at least it's trying to call the whole code asynchronously because it gives me the error: java.lang.IllegalArgumentException: You must call this method on the main thread
final WallpaperManager wallpaperManager = WallpaperManager
.getInstance(this);
Intent intent = getIntent();
int position = intent.getExtras().getInt("Image int");
final StorageReference fullSizeImagesStoragePath = mStorageRef.child("images_full_size/" + position + ".jpg");
GlideApp.with(PhotoDetailsActivity.this)
.asBitmap()
.load(fullSizeImagesStoragePath)
.into(new SimpleTarget<Bitmap>() {
#Override
public void onResourceReady(#NonNull Bitmap resource, #Nullable Transition<? super Bitmap> transition) {
try {
wallpaperManager.setBitmap(resource, null, true, WallpaperManager.FLAG_SYSTEM);
} catch (IOException e) {
}
}
});
It points to .into(new SimpleTarget<Bitmap>() as being the method that wants to run only on the main thread. Help?
My guess is glide internally will call onSuccess on UI thread, then your task of setting wallpaper still runs on ui thread, even you wrap it in the async task. I suggest you checking the implementation of the related methods of glide.
Edit:
If that the case, then you will need to wrap your set wallpaper task into an async task too, which will be very tedious to be implement. My suggestion is to use Rxjava to chain these tasks together, which imo will be a better approach.
Edit:
Took a look at the docs,
https://github.com/bumptech/glide/wiki/Loading-and-Caching-on-Background-Threads
it said
If you actually want to interact with a decoded image on a background
thread, instead of using downloadOnly you can use the version of
into() that returns a FutureTarget
So you can try loading your image synchronously on a background thread(by async task) to avoid blocking ui, but make sure to call it with application context to avoid memory leaks.
Bitmap myBitmap = Glide.with(applicationContext)
.load(yourUrl)
.asBitmap()
.centerCrop()
.into(500, 500)
.get()
I am running following operations in the background thread:
Store byte image data locally as a jpeg.
Fetch that image as a bitmap.
Run face-detection algorithm and fetch facial points
Place an overlay according to those points
And finally save that bitmap again.
While performing these operations, I am displaying a progress bar. But the progress bar animation gets stuck i.e it becomes unresponsive until the thread completes its work(update: nothing in the whole UI seems to function while the background work is running).
Doesn't this make actual intentions of using background thread obsolete?
What approach should I take to overcome this problem?
My code is:
//Show ProgressBar
Runnable worker = () -> {
//My Operations
runOnUiThread(() -> {
//Hide ProgressBar
});
}
You can use the async approach, it will work
private class MyAsyncTask extends AsyncTask<String, Progress, Bitmap> {
protected void onPreExecute() {
// Runs on the UI thread before doInBackground
progressBar.setVisibility(ProgressBar.VISIBLE);
}
protected Bitmap doInBackground(String... strings) {
// Some long-running task like downloading an image.
// convert your bitmap
return someBitmap;
}
protected void onProgressUpdate(Progress... values) {
//update progress
}
protected void onPostExecute(Bitmap result) {
//hide progress bar
}}
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() {
}
});
I've found a lot of posts explaining how to load images inside a ListView using AsyncTask, the problem is that all, at least the ones I have found, do it by instantiating and starting an AsyncTask every single time the getView method is called inside the ListView adapter.
I've done this already, all works well, but when I go through the ListView really quickly, it throws an exception at .execute saying that it refuses to create anymore threads.
So my question is how to overcome this issue while at the same time keep using AsyncTasks ? (I could use a single, "normal, Java" Thread but the project specifications say I'm not allowed.
Thanks and cheers
I found this very usefull:
class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {
private String url;
private final WeakReference<ImageView> imageViewReference;
public BitmapDownloaderTask(ImageView imageView) {
imageViewReference = new WeakReference<ImageView>(imageView);
}
#Override
// Actual download method, run in the task thread
protected Bitmap doInBackground(String... params) {
// params comes from the execute() call: params[0] is the url.
return downloadBitmap(params[0]);
}
#Override
// Once the image is downloaded, associates it to the imageView
protected void onPostExecute(Bitmap bitmap) {
if (isCancelled()) {
bitmap = null;
}
if (imageViewReference != null) {
ImageView imageView = imageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}
}
see the complete post here
I would go for Aquery for Android
aq.id(R.id.image1).image("http://www.vikispot.com/z/images/vikispot/android-w.png");
Learn more: android-query - ImageLoading
or Volley:
Volley: Easy, Fast Networking for Android
You should rather use IntentService instead of AsyncTask and also cache the images you already downloaded so next time no data needs to be fetched. Or you can take a look at existing projects like SmartImageView that should do all you need
I would like to load a large/complex svg image file into my imageView. Loading the svg takes time to load on my HTC Desire S and Lenovo a60. About 5 Second and 10 seconds respectively.
Right now my app becomes unresponsive for about 5 seconds until the imageView is fully loaded
I load the image using this simple code..
svg = SVGParser.getSVGFromResource(getResources(), R.raw.gf);
mImageView.setImageDrawable(svg.createPictureDrawable());
I was looking something like (webView)
webView.setWebViewClient(new WebViewClientEx(MainActivity.this, DEBUG) {
#Override
public void onPageFinished(final WebView view, String url) {
...........
.............}
});
which I used in my previous project...
Finally my questions:
1.) What is the best approach to make the app look responsive?
I was planning to use asynctask but don't know how to use it... Is it applicable here?
2.) Is there listener after the image is fully loaded?
My approach here is to show the progressDialog by the time I load the imageView and hide it after the imageView has fully loaded.
another other suggestions which you think is better to use for showing/hiding progressDialog ? Thanks!
Use an async task here. (a Loader would work aswell but async task is a bit easier to explain.)
private class LoadSVGTask extends AsyncTask<Integer, Void, Drawable> {
protected Drawable doInBackground(Integer... res) {
mProgressBar.setVisibility(View.Visible);
svg = SVGParser.getSVGFromResource(getResources(),res);
Drawable d = svg.createPictureDrawable();
return d;
}
// gets executed in main thread
protected void onPostExecute(Drawable result) {
mProgressBar.setVisibility(View.Gone);
mImageView.setImageDrawable(result);
}
}
Launch the Task with:
new LoaderSVGTask().execute(R.raw.gf, null, null);
see: http://developer.android.com/reference/android/os/AsyncTask.html
Just refer AsyncTask in that
protected void onPreExecute (){
progressDialog.show(......);
}
protected abstract Result doInBackground (Params... params){
//load your image from here
}
protected void onPostExecute (Result result){
progressDialog.dismiss();
}