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()
Related
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
}
}
In the following situation: What is my alternative to using an AsyncTask?
I am using AsyncTask to load images into an adapter. The adapter row has a number of TextViews and one ImageView. The ImageView is where I load the image. The image is being loaded from the internet. The problem is that when I scroll, the wrong image would show in a row/cell -- until the correct image has had time to arrive. How do I prevent this image mismatching from ever happening? I am asking this question because I want to understand how this works: I don't just want to get some library that might do the work for me (many libraries I have already tried, have failed).
Once again, the problem: the AsyncTask causes images to load into the wrong row so that the user can clearly see that the images are looking for their final destination.
I hope the question is clear. For completeness, below is the method that I am calling inside the getView method of the adapter to load each image. The method is also inside the adapter. What is my alternative to using an AsyncTask?
private void loadImage(final ImageView photo, String imageUrl) {
new AsyncTask<String, String, Bitmap>() {
#Override
protected Bitmap doInBackground(String... param) {
try {
Bitmap b = callToServer(imageUrl);//takes long
return b;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
#Override
protected void onPostExecute(Bitmap ret) {
if (ret != null) {
photo.setImageBitmap(ret);
}
}
}.execute(imageUrl);
}
The most common approach is using the setTag(...) and getTag(...) methods of the view that you create in the adapter. Once created, you add a tag that you need to link to the image that is then asynchronously loaded. When that task is finished, you can check the tag of the view and if it's still the same as when the async task has started you can set the image. Otherwise you can dismiss the image. Remember that the same view is re-used instead of created when you scroll. So the tag will have changed then.
Here's a pretty good example: Asynchronous Image Loader in Android ListView
The problem is that the rows get recycled and so does the image view. Then when the server response returns the image view already belongs to another data object.
There are different approaches to this. My solution is to extend ImageView and keep track of the image you want to load.
class RemoteImageView extends ImageView {
private String _uri;
public synchronized void loadRemoteImage(String uri) {
_uri = uri;
loadImage(this, uri) ; //this is your async call
}
private synchronized void onImageLoaded(String uri, Bitmap image) {
if(uri.equals(_uri)) { //this will set only the correct image
setImageBitmap(image);
}
}
}
As for your loading function:
private void loadImage(final ImageView photo, final String imageUrl) {
new AsyncTask<String, String, Bitmap>() {
#Override
protected Bitmap doInBackground(String... param) {
try {
Bitmap b = callToServer(imageUrl);//takes long
return b;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
#Override
protected void onPostExecute(Bitmap ret) {
if (ret != null) {
photo.onImageLoaded(imageUri, ret);
}
}
}.execute(imageUrl);
}
And then withing your adapter you should call the loadRemoteImage(imageUri)
I also suggest you combine this with a bitmap cache so as to acccelerate the process of fetching the image and the addition of placeholders :)
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 am attempting to use Picasso to get three Bitmap images from a URL
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.tab2);
Drawable d1 = new BitmapDrawable(Picasso.with(Tab2.this).load(zestimateImg1).get());
}
I am getting FATAL EXCEPTION with this code. I suspect it has to do with the fact that this should be done within AsyncTask, but I can't get it to work. If using that is avoidable, I would like to do this without using AsyncTask.
How can I get this code to run without crashing?
If the best way to do this is with AsyncTask, then that solution is ok.
None of above worked for me instead this
Handler uiHandler = new Handler(Looper.getMainLooper());
uiHandler.post(new Runnable(){
#Override
public void run() {
Picasso.with(Context)
.load(imageUrl)
.into(imageView);
}
});
Hope it may be useful for someone
You cannot make synchronous requests in the main thread. If you dont want to use an AsyncThread then just use Picasso together with a Target.
Picasso.with(Tab2.this).load(zestimateImg1).into(new Target(...);
I recommend you save a reference to your target like so:
Target mTarget =new Target (...);
This is because Picasso uses weak references to them and they might be garbage collected before the process is finished.
Just for the record:
Picasso.with(context).load(url).into(new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
Log.i(TAG, "The image was obtained correctly");
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
Log.e(TAG, "The image was not obtained");
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
Log.(TAG, "Getting ready to get the image");
//Here you should place a loading gif in the ImageView
//while image is being obtained.
}
});
Source: http://square.github.io/picasso/
onPrepareLoad() is called always after starting the request.
from can be "DISK", "MEMORY" or "NETWORK" to indicate where was the image obtained from.
try this:
Handler uiHandler = new Handler(Looper.getMainLooper());
uiHandler.post(new Runnable(){
#Override
public void run() {
Picasso.with(Context)
.load(imageUrl)
.into(imageView);
}
});
This is the same answer of Juan José Melero Gómez but with kotlin:
val imageBitmap = Picasso.get().load(post.path_image).into(object: Target {
override fun onPrepareLoad(placeHolderDrawable: Drawable?) {
Log.(TAG, "Getting ready to get the image");
//Here you should place a loading gif in the ImageView
//while image is being obtained.
}
override fun onBitmapFailed(e: Exception?, errorDrawable: Drawable?) {
Log.e(TAG, "The image was not obtained");
}
override fun onBitmapLoaded(bitmap: Bitmap?, from: Picasso.LoadedFrom?) {
Log.i(TAG, "The image was obtained correctly");
}
})
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();
}