I am using Picasso to download images from the web but sometimes it works and sometimes it fails. It's very irritating and I can't find a way to fix this. Any help?
My code to download image:
public void imageDownload(Context ctx, String url){
Picasso.with(ctx)
.load(spp)
.into(getTarget(url));
}
//Using the Picasso Target Class
private Target getTarget(String url){
final String temp = url;
Target target = new Target(){
#Override
public void onBitmapLoaded(final Bitmap bitmap, Picasso.LoadedFrom from) {
new Thread(new Runnable() {
#Override
public void run() {
File file = new File(Environment.getExternalStorageDirectory().getPath() + "/" + temp);
try {
file.createNewFile();
FileOutputStream ostream = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 80, ostream);
ostream.flush();
ostream.close();
} catch (IOException e) {
Log.e("IOException", e.getLocalizedMessage());
}
}
}).start();
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
};
return target;
}
I call this method as:
imageDownload(getActivity(),"image.jpg");
from documentation of method into(Target t):
Note: This method keeps a weak reference to the {#link Target}
instance and will be garbage collected if you do not keep a
strong reference to it. To receive callbacks when an image is
loaded use {#link #into(android.widget.ImageView, Callback)}
You don't keep a reference to the instance of Target you have created so it can be collected by garbage collector as documentation states. This can explain random behavior you observed.
One solution is suggested in the documentation ( use into(android.widget.ImageView, Callback)).
Another solution is to keep a reference to an instance of Target.
target = getTarget(url); //declared as a field of a class
Picasso.with(ctx)
.load(spp)
.into(target);
Related
I'm trying to download multiple pictures using picasso. here's my code:
for(int i=1; i <=20; i++){
String url = img_url + i + "/profile.jpg";
String img_dir = img_dir + i;
Picasso.with(this).load(url).into(picassoImageTarget(getApplicationContext(),img_dir, img_name));
}
Url of the site looks like this:
site.com/img/equipment/1/profile.jpg,
site.com/img/equipment/2/profile.jpg,
site.com/img/equipment/3/profile.jpg
and so on ...
i tried
Picasso.with(this).load(url).into(picassoImageTarget(getApplicationContext(),img_dir, img_name));
without the for loop and it is working. images are not download when i place it inside the loop.
here's my Target
private Target picassoImageTarget(Context context, final String imageDir, final String imageName) {
Log.d("picassoImageTarget", " picassoImageTarget");
ContextWrapper cw = new ContextWrapper(context);
final File directory = cw.getDir(imageDir, Context.MODE_PRIVATE); // path to /data/data/yourapp/app_imageDir
return new Target() {
#Override
public void onBitmapLoaded(final Bitmap bitmap, Picasso.LoadedFrom from) {
new Thread(new Runnable() {
#Override
public void run() {
final File myImageFile = new File(directory, imageName); // Create image file
FileOutputStream fos = null;
try {
fos = new FileOutputStream(myImageFile);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
Log.i("image", "image saved to >>>" + myImageFile.getAbsolutePath());
}
}).start();
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
if (placeHolderDrawable != null) {}
}
};
}
please help. thanks.
Targets are held in WeakReferences.
You need to hold a reference to the Targets you want to keep to prevent them from being garbage collected.
Maybe your code would look something like:
final class MyLoader {
final ArrayList<Target> targets = new ArrayList<>(20);
void load(...) {
for(...) {
Target target = picassoImageTarget(...);
targets.add(target);
picasso.load(...).into(target); // TODO: Maybe remove from list when complete.
}
}
}
I have a list of urls for images. I want to use Picasso to download these images to local storage on Android. I am using following method (using custom target). However I don't see all the images getting downloaded.
public void downloadAllMapImages(List<ProjectMapModel> models) {
List<Target> targetList=Lists.newArrayList();
for(ProjectMapModel model:models){
for (Map.Entry<Integer, String> entry : model.getMaps().entrySet()) {
String url=entry.getValue();
Target target=mapImageTarget(model.getProjectId(),entry.getKey());
targetList.add(target);
Picasso.with(this)
.load(url)
.into(target);
}
}
if(!isImageDownloadSuccessful)
showDownloadMapImageDataError();
//whatever images were downloaded, we need to write them to database
listDetailPresenter.onImageDownloadingSuccessful(projectMapOutputModels);
}
private Target mapImageTarget(final int projectId, final int mapType) {
//create the file name
final File file = new File(this.getExternalFilesDir(null) + File.separator + projectId+"_"+ mapType + ".jpg");
final String filePath=file.getAbsolutePath();
return new Target() {
#Override
public void onBitmapLoaded(final Bitmap bitmap, Picasso.LoadedFrom from) {
// loading of the bitmap was a success
// write the image to file
new Thread(new Runnable() {
#Override
public void run() {
OutputStream outputStream = null;
try {
outputStream = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
} catch (IOException e) {
Log.e(DashboardActivity.class.getName(), e.toString());
isImageDownloadSuccessful=false;
} finally {
try {
outputStream.close();
Log.i(DashboardActivity.class.getName(), "image saved");
} catch (IOException e) {
Log.e(DashboardActivity.class.getName(), e.toString());
isImageDownloadSuccessful=false;
}
}
projectMapOutputModels.add(new ProjectMapOutputData(projectId,filePath,mapType));
Log.i("image", "image saved to >>>" + filePath);
}
}).start();
//notify presenter that this image has been download and the path can be sent to presenter
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
// loading of the bitmap failed
// TODO show error message toast. dont write file path to database. continue with next image
isImageDownloadSuccessful=false;
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
};
}
Am I missing anything? Any help on this is really appreciated!
I am using Picasso for my image handling and use it to download images from a backend server and save to the local device. I use Target to save the image
Picasso.with(context)
.load(url)
.into(target);
Since the target code gets a bitmap, I have to use bitmap.compress() to write the image to local disk and I use JPEF format with quality of 100 assuming this will preserve the original quality.
Reading this it seems like this might not be what I want. In one case, the image on the backend was 90kb and the image that was written was 370kb. The original image can be generated using an arbitrary quality value. What is the easiest way to save the image using Picasso without the size/quality changing?
Target target = new Target() {
#Override
public void onPrepareLoad(Drawable arg0) {
}
#Override
public void onBitmapLoaded(final Bitmap bitmap, Picasso.LoadedFrom arg1) {
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
FileOutputStream out = null;
try {
out = new FileOutputStream(imagePath);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
protected void onPostExecute(Void info) {
}
}.execute();
}
#Override
public void onBitmapFailed(Drawable arg0) {
}
};
UPDATE: more elegant solution here: https://github.com/square/picasso/issues/1025#issuecomment-102661647
Solved the problem using this solution.
In my PagerAdapter's instantiateItem() method, I run an AsyncTask that will download the image, save it to a file and then call Picasso with that image file.
PagerAdapter instantiateItem():
RemoteImageToImageViewAsyncTask remoteImageToImageViewAsyncTask =
new RemoteImageToImageViewAsyncTask(imageView, url, imagePath);
remoteImageToImageViewAsyncTask.execute();
RemoteImageToImageViewAsyncTask
public class RemoteImageToImageViewAsyncTask extends AsyncTask<Void, Void, Void> {
ImageView imageView;
String url;
File output;
public RemoteImageToImageViewAsyncTask(ImageView imageView, String url, File output) {
this.imageView = imageView;
this.url = url;
this.output = output;
}
#Override
protected Void doInBackground(Void... params) {
// Downloads the image from url to output
ImageDownloader.download(url, output);
return null;
}
#Override
protected void onPostExecute(Void params) {
Picasso.with(context)
.load(output)
.into(imageView);
}
}
I’m working on this code where I populate the city picture using Picasso and getting it from expansion file. Then, I followed the instructions here and here.
No one worked for me. The code bellow and few more details follows:
final ExpansionFileLoader expansionFileLoader = new ExpansionFileLoader(this, App.Constants.EXPANSION_FILE_VERSION_NUMBER);
Picasso picasso = new Picasso.Builder(this)
.downloader(new Downloader() {
#Override
public Response load(Uri uri, int networkPolicy) throws IOException {
Log.d("CityActivity", "loading the image file of the chosen city");
InputStream inputStream = expansionFileLoader.getInputStream(uri.toString());
return new Response(inputStream, false, -1);
}
#Override
public void shutdown() {
}
})
.build();
picasso.load(Uri.parse(App.ViewData.selectedCity.image_file_path)).into(PicassoTools.setBackgroundTarget(findViewById(R.id.navigation_drawer_layout)));
I have put breakpoints in the downloader and the main reason is because load is not being called for some reason. So probably Im doing some basic mistake when creating the downloader or Picasso builder.
Btw, I'm using Picasso 2.5.
That is my target settings(which is just a detail, and won't matter for the load method to be called):
public static Target setBackgroundTarget(View view) {
return setBackgroundTarget(view, null);
}
public static Target setBackgroundTarget(View view, Runnable callback) {
Target target = new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
int sdk = android.os.Build.VERSION.SDK_INT;
if (sdk < android.os.Build.VERSION_CODES.JELLY_BEAN) {
view.setBackgroundDrawable(new BitmapDrawable(App.context.getResources(), bitmap));
} else {
view.setBackground(new BitmapDrawable(App.context.getResources(), bitmap));
}
if (callback != null) callback.run();
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
Log.e("PICASSO", "Bitmap Failed");
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
// use placeholder drawable if desired
Log.d("PICASSO", "Bitmap Preload");
}
};
view.setTag(target);
return target;
}
Let me know any other detail you wanna know. Thanks a lot for the help.
Cheers
I'm trying to show my news in a custom ListView. Each news is included of some images and I want to
1.download images from server
2.save in local storage
3.save path of images into SQLite
4.show images in ListView using my custom adapter.
I just have problem with steps 1 & 2. I can get news from server and show them in my ListView
and show images from cache by add below code in my adapter:
Picasso.with(context).load(image[position]).into(iv);
By using Picasso.with(context).load(image[position]).into(target) , just I can save one
image in storage.
Please suggest me your idea ...
UPDATE: When I use below code, just one image (last index of my image array) being saved!
How can I save all images in array with this code?!
#Override
protected void onPostExecute(Void result) {
SaveImages();
pDialog.dismiss();
super.onPostExecute(result);
}
String fileName = null;
public void SaveImages() {
for(int i = 0; i < image.length; i++) {
Picasso.with(this).load(image[i]).into(target);
fileName = "image-" + i + ".jpg";
}
}
Target target = new Target() {
#Override
public void onPrepareLoad(Drawable arg0) {
}
#Override
public void onBitmapLoaded(Bitmap bitmap, LoadedFrom arg1) {
File file = new File(Environment.getExternalStorageDirectory().getPath() +"/" + fileName);
try {
file.createNewFile();
FileOutputStream ostream = new FileOutputStream(file);
bitmap.compress(CompressFormat.JPEG, 75, ostream);
ostream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void onBitmapFailed(Drawable arg0) {
}
};
Try to put Target target definition before call to Picasso.with(this).load(image[i]).into(target);
P.S. Using the following code and I saved images very well. Thanks, anyway.
My Code:
final String fileName = mDataset.get(i).getAid() + ".jpg";
Target target = new Target() {
#Override
public void onPrepareLoad(Drawable arg0) {
return;
}
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom arg1) {
try {
File file = null;
// judge "imgs/.nomedia"'s existance to judge whether path available
if(LightCache.testFileExist(GlobalConfig.getFirstStoragePath()
+ "imgs" + File.separator +".nomedia") == true)
file = new File(GlobalConfig.getFirstStoragePath()
+ "imgs" + File.separator + fileName);
else file = new File(GlobalConfig.getSecondStoragePath()
+ "imgs" + File.separator + fileName);
file.createNewFile();
FileOutputStream ostream = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 80, ostream);
ostream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void onBitmapFailed(Drawable arg0) {
return;
}
};
Picasso.with(GlobalConfig.getContext())
.load(Wenku8API.getCoverURL(mDataset.get(i).getAid()))
.into(target);
Custom target for storing photo in phone gallery.
public class TargetPhoneGallery implements Target
{
private final WeakReference<ContentResolver> resolver;
private final String name;
private final String desc;
public TargetPhoneGallery(ContentResolver r, String name, String desc)
{
this.resolver = new WeakReference<ContentResolver>(r);
this.name = name;
this.desc = desc;
}
#Override
public void onPrepareLoad (Drawable arg0)
{
}
#Override
public void onBitmapLoaded (Bitmap bitmap, LoadedFrom arg1)
{
ContentResolver r = resolver.get();
if (r != null)
{
MediaStore.Images.Media.insertImage(r, bitmap, name, desc);
}
}
#Override
public void onBitmapFailed (Drawable arg0)
{
}
}
Picasso.with(context).load(image[position]).into(new TargetPhoneGallery(view.getContentResolver(), "image name", "image desc"));
although this post is old, it seems the question hasn't been answered yet.
Reading your code, it appears the call you make to picasso could be asynchronous.
You should definitely check that, as if it is the case, you are starting image.length tasks, changing the filename at each new task, leading all tasks to complete and save to the last filename that was set.
To solve this, you should override Target constructor and add a filename parameter so it's ready when the task ends, in your onBitmapLoaded listener.