Picasso Targets onBitmapLoaded not called in for loop - android

Below code snippet for load file into Bitmap and save this file in internal directory(i.e PNG,or JPG format)
final List<Target> targets = new ArrayList<Target>();
final List<Target> targetsNormal = new ArrayList<Target>();
for (int j = 0; j < defaultTileImage.size(); j++) {
final String slangTiles = defaultTileImage.get(j).getPairName() +
ApplicationConstants.SLANG_TILES;
final String normalTiles = defaultTileImage.get(j).getPairName() +
ApplicationConstants.NORMAL_TILES;
final int k = j;
Target target = new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
Log.i("Targets", "Loaded: " + k);
targets.remove(this);
saveIntoBitmap(bitmap, slangTiles);
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
targets.remove(this);
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
Log.i("Targets", "Preparing: " + k);
}
};
Target targetNormal = new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
Log.i("TargetsNormal", "Loaded: " + k);
targetsNormal.remove(this);
saveIntoBitmapSlang(bitmap, normalTiles);
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
targetsNormal.remove(this);
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
Log.i("TargetsNormal", "Preparing: " + k);
}
};
targetsNormal.add(targetNormal);
Picasso.with(MainActivity.this)
.load(defaultTileImage.get(j).getNormalTileImg()) // Start loading the current target
.resize(100, 100)
.into(targetNormal);
targets.add(target);
Picasso.with(MainActivity.this)
.load(defaultTileImage.get(j).getSlangTileImg()) // Start loading the current target
.resize(100, 100)
.into(target);
}
Also visited this link and implemented as per this guideline that make Target a strong reference . But unfortunately many time onBitmapLoaded not get any callback.
I truly appreciate your help in resolving the problem

Make sure targets and targetsNormal are global variables instead of local.
If they are local variables, they can be garbage collected as soon as the method has been left.

Related

I want to know if my code is thread-safe

I have a code like this, which makes a anonymous thread
everytime the loop goes. And the length of cityArray is 17.
for(int i=0; i<cityArray.length; i++)
{
final int finalI = i;
new Thread(new Runnable() {
#Override
public void run() {
setMarkerIcon(cityArray[finalI]);
}
}).start();
}
And it calls the setMarkerIcon method, which runs the getBestMarkerItemfromArrayList, then runs runOnUiThread to update view.
public void setMarkerIcon( final String city ){
final MarkerItem markerItem = getBestMarkerItemfromArrayList(city);
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
Log.i(TAG, "run: ");
if(markerItem != null){
Glide.with(getContext())
.load(R.drawable.back_level3)
.asBitmap()
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(new SimpleTarget<Bitmap>() {
#Override
public void onResourceReady(Bitmap bitmap, GlideAnimation<? super Bitmap> glideAnimation) {
Log.i(TAG, "onResourceReady: " + map.getMarkers().size() + "__" + city);
for (int i = 0; i < markerOptions.length; i++) {
if (map.getMarkers().get(i).getData().equals(city)) {
map.getMarkers().get(i).setIcon(BitmapDescriptorFactory.fromBitmap(createDrawableFromView(city, bitmap)));
break;
}
}
}
});
The code works well, and there seems to be no problem at the moment.
But I just want to make sure that I am using the thread correctly.
Thanks.

First GoogleMaps marker doesn't show, but the second one does

The marker has an icon property and it contains a bitmap of the user's Facebook profile picture.
I'm not really sure why the first marker doesn't load. But if you post a second, third or 4th (etc...) it works completely fine!
I don't have any other markers being added in my code other than the block of code below.
EDIT: This is important. The 'bitmap' value seems to be null on the first try. So it seems the if statement with the condition 'bitmap!=null' is catching it and preventing it from posting the first marker...
static Bitmap bitmap = null;
private Target loadtarget;
globalMap.setOnMapLongClickListener(new GoogleMap.OnMapLongClickListener() {
#Override
public void onMapLongClick(LatLng point) {
vibe.vibrate(100);
AccessToken accessToken = AccessToken.getCurrentAccessToken();
MarkerOptions marker = new MarkerOptions().position(
new LatLng(point.latitude, point.longitude))
.title("New Marker");
Log.e("TAG", accessToken.getUserId());
String imageURL = new String("https://graph.facebook.com/" + accessToken.getUserId() + "/picture?type=small");
loadBitmap(imageURL);
if(bitmap!=null) {
globalMap.addMarker(marker
.position(point)
.icon(BitmapDescriptorFactory.fromBitmap(bitmap))
.anchor(0.5f, 1));
}
}
});
}
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(Drawable errorDrawable) {
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
};
Picasso.with(getActivity()).load(url).into(loadtarget);
}
public void handleLoadedBitmap(Bitmap b) {
bitmap = b;
}
You are right, bitmap will be null on the first long click because there's no time to load bitmap. You are calling load() and immediately after that trying to show bitmap.
You can load bitmap not in the long click listener but before, may be in the same place of code where you're setting that listener.
String imageURL = new String("https://graph.facebook.com/" + accessToken.getUserId() + "/picture?type=small");
loadBitmap(imageURL);
globalMap.setOnMapLongClickListener(
// ...
// the same code without imageURL initialization and bitmap loading
);
Follow the Documentation
static Bitmap bitmap = null;
private Target loadtarget;
globalMap.setOnMapLoadedCallback(new OnMapLoadedCallback() {
#Override
public void onMapLoaded() {
globalMap.setOnMapLongClickListener(new GoogleMap.OnMapLongClickListener() {
#Override
public void onMapLongClick(LatLng point) {
vibe.vibrate(100);
AccessToken accessToken = AccessToken.getCurrentAccessToken();
MarkerOptions marker = new MarkerOptions().position(
new LatLng(point.latitude, point.longitude))
.title("New Marker");
Log.e("TAG", accessToken.getUserId());
String imageURL = new String("https://graph.facebook.com/" + accessToken.getUserId() + "/picture?type=small");
loadBitmap(imageURL);
if(bitmap!=null) {
globalMap.addMarker(marker
.position(point)
.icon(BitmapDescriptorFactory.fromBitmap(bitmap))
.anchor(0.5f, 1));
}
}
});
}
});
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(Drawable errorDrawable) {
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
};
Picasso.with(getActivity()).load(url).into(loadtarget);
}
public void handleLoadedBitmap(Bitmap b) {
bitmap = b;
}
I found out why it wasn't posting on the first try. It's because I was calling my method loadBitmap(..) too late, I felt like the process of it retrieving the image and loading it into the target value was too long for the clickListener which instantly places the marker. I just assumed that so I called it earlier, in my method onMapReady(), and that did the job.

Downloading and saving all the images reading the url Array

I am trying to download and save all the images from a url array but the problem is only last image gets saved with the name img7.jpg. I don't understand where the problem is? I want all the images to be downloaded and saved with the name img1.jpg, img2.jpg, img3.jpg and so on. Where in the code do I need to make changes?
public class MainActivity extends ActionBarActivity {
ImageView imageView;
public static String[] stringArray = {"http://upload.wikimedia.org/wikipedia/en/9/90/Broken_Boundaries_of_Rohtas_Fort.jpg",
"http://upload.wikimedia.org/wikipedia/en/a/a0/Rohtas_View_4.jpg",
"http://upload.wikimedia.org/wikipedia/en/0/07/Rohtas_Fort_Gate.jpg",
"http://upload.wikimedia.org/wikipedia/en/7/78/Rohtas_Village_View_2nd.jpg",
"http://www.worldheritagesite.org/picx/w586.jpg",
"http://wpcontent.answcdn.com/wikipedia/en/thumb/6/68/Rani_Mahal_Rohtas_Fort_2.jpg/930px-Rani_Mahal_Rohtas_Fort_2.jpg",
"http://photos.wikimapia.org/p/00/01/75/75/69_big.jpg"};
int i = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = (ImageView)findViewById(R.id.imageView1);
for (String url : stringArray){
Picasso.with(this)
.load(url)
.into(target);
i++;
}
}
private 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() +"/img"+i+".jpg");
try {
file.createNewFile();
FileOutputStream ostream = new FileOutputStream(file);
bitmap.compress(CompressFormat.JPEG, 75, ostream);
ostream.close();
}
catch (Exception e){
e.printStackTrace();
}
}
}).start();
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
if (placeHolderDrawable != null) {
}
}
};
}
In your case Picasso loads your images asynchronously. What means, that
Picasso.with(this)
.load(url)
.into(target);
isn't waiting until your picture is downloaded and stored to disk. So, your counter i is always (stringArray.length - 1), if it comes to saving the image.
Do something like this:
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int index = 0;
for (String url : stringArray) {
Picasso.with(this)
.load(url)
.into(new IndexTarget(index));
index++;
}
}
class IndexTarget implements Target {
private final int mIndex;
public IndexTarget(int index){
this.mIndex = index;
}
#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() +"/img"+mIndex+".jpg");
try {
file.createNewFile();
FileOutputStream ostream = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 75, ostream);
ostream.close();
}
catch (Exception e){
e.printStackTrace();
}
}
}).start();
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
}

Android Expansion File to Custom Picasso Downloader - InputStream

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

Download and Save Images Using Picasso

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.

Categories

Resources