Adding markers from url with Picasso
Asked Answered
C

2

8

I'm adding markers to my map from a url using the Picasso library

As a marker isn't an ImageView I tried to use a Target instead

for(int x =0; x < mapIcon_url.length; x++){

    Picasso.with(getActivity()).load(mapIcon_url[x]).resize(marker_size, marker_size+15).into(new Target() {

        @Override
        public void onSuccess(Bitmap b) {
            bitmapMarker = BitmapDescriptorFactory.fromBitmap(b);


            //create marker option
            if(b != null)
                markerOptions = new MarkerOptions().position(marker_position).icon(bitmapMarker));
            else
                markerOptions = new MarkerOptions().position(marker_position).icon(BitmapDescriptorFactory.fromResource(R.drawable.placeholder_pin)).snippet(String.valueOf(x));

            marker = map.addMarker(markerOptions);                              
        }

        @Override
        public void onError() {

            //create marker option                                  
            markerOptions = new MarkerOptions().position(marker_position).icon(BitmapDescriptorFactory.fromResource(R.drawable.placeholder_pin)).snippet(String.valueOf(x));
            marker = map.addMarker(markerOptions);

        }
    }); 
}   

I'm doing this in a loop to add about 20 markers but I find that on first run of the code only 5 or 7 markers are added so I've switched to using the lib and an AsyncTask like this.

for(int x =0; x < mapIcon_url.length; x++){

    new AddMarker().execute(mapIcon_url[x]);
}


public class AddMarker extends AsyncTask<String, Integer, BitmapDescriptor> {

    BitmapDescriptor bitmapMarker1;
    VenueDetails myVenue;

    @Override
    protected BitmapDescriptor doInBackground(String... url) {  
        myUrl = url[0];
        try {
            bitmapMarker1 = BitmapDescriptorFactory.fromBitmap(Picasso.with(getActivity()).load(myUrl).resize(marker_size, marker_size+15).get());
        } catch (IOException e) {
            e.printStackTrace();
        }

        return bitmapMarker1;
    }

    protected void onPostExecute(BitmapDescriptor icon) {

        try {

            map.addMarker(new MarkerOptions().position(marker_position).icon(icon)));

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}   

However I'm a bit worried this method could give me some issues when I have alot of markers say about 100. My question would be is this the best way to do this and if not what other options can I try.

Cletuscleve answered 31/8, 2013 at 13:9 Comment(16)
I would focus on debugging your first approach. Use breakpoints or logging statements to determine where you are failing to get the markers.Lick
I did use logging statements in the onSucess and onError override methods of the target, also just before calling the Picasso class. Logs showed the look was called 20 times, onSucess was called a few times and onError never called.Cletuscleve
Jake just released Picasso 2.0.0 yesterday -- you might try that if you have not done so already. Otherwise, you might try to create a reproducible test case and file an issue with the Picasso project. I don't see a particular problem with what you have.Lick
Your anonymous Target might be coming garbage collected and thats why you lose some of the downloads.Bikol
How do I avoid this? Same thing happens in 2.0Cletuscleve
Create a member array of mapIcon_url.length size. Store your Target instances into the array. Once download is complete, remove from the array (remember to remove also if the download failed). If the user exits your Activity loop through the array and call cancelRequest(array[i]) to cancel all pending/in-progress requests. You must keep a strong reference to your Target, otherwise there is a high chance it will get gc'ed.Bikol
Don't use AsyncTask. Its implementation for execution (parallel vs sequential) has changed over Android versions and I would avoid spawning so many threads. Picasso will do the work for you just fine.Bikol
I'm doing something similar HERE!!!#18808614Supposition
Same problem here. dnkoutso absolutely right, it works! Why not post it as answer?Abyssal
I can load images with Picasso, problem is loading markers using a targetCletuscleve
I also did what @Bikol proposed and it workedCartomancy
Alternatively you could make your view implement Target.Bikol
Hi, indeed having a strong reference (e.g. List<Target>) does solve the problem of loading the images into the marker. However I bumped into another problem: The images are not getting cached in disk. Specifically when there is no internet and I load images from a previously used url, all of the Targets get "onBitmapFailed". Any ideas?Secretarygeneral
@ThomasKaliakos Does the response include the necessary headers to store the bitmap into disk? http layer handles disk caching.Bikol
Yes you are right. Eventually I found out that the response was a 302 redirect and not the image itself, that was preventing the caching to happen. Ευχαριστώ :)Secretarygeneral
I found the answer here. Hope it helps. #27095969Rubescent
D
10

You have to keep a reference for each Target, otherwise the system automatically releases them when the garbage collector is invoked.

So, the better solution is add each Target to a HashSet and then in onBitmapLoaded() and onBitmapFailed methods from Target, remove the Target itself from the set.

Thank you for the suggestion, now my code work perfectly. Below the pieces of code that implement your suggestion.

[...]//Global var
  private Set<PoiTarget> poiTargets = new HashSet<PoiTarget>();
[...]    
 private void somewhere(){
    PoiTarget pt;
    for(Item item: data) {
        m = map.addMarker(new MarkerOptions()
               .position(new LatLng(item.latitude, item.longitude))
               .title(item.title));
        pt = new PoiTarget(m);
        poiTargets.add(pt);
        Picasso.with(context)
           .load(mapImageURLString)
           .into(pt);
    }
}
[...]
//--------------------------------------------------------
// Inner class
//--------------------------------------------------------
    class PoiTarget implements Target{
        private Marker m;

        public PoiTarget(Marker m) { this.m = m; }

        @Override public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
            m.setIcon(BitmapDescriptorFactory.fromBitmap(bitmap));
            poiTargets.remove(this);
            Tools.msg(" @+ Set bitmap for "+m.getTitle()+" PT size: #"+poiTargets.size());
        }

        @Override public void onBitmapFailed(Drawable errorDrawable) {
            Tools.msg(" @+ [ERROR] Don't set bitmap for "+m.getTitle());
            poiTargets.remove(this);
        }

        @Override public void onPrepareLoad(Drawable placeHolderDrawable) {

        }
    }
Dripps answered 23/10, 2014 at 15:10 Comment(2)
With Picasso 2.4.0 the targets are being garbage collected using this technique. Do you have any kind of advice to fix it?Circumpolar
This is workable for Picasso 2.3.2. Didn't known how to use Target like this before!Poisoning
E
2

You have to keep a reference for each Target, otherwise the system automatically releases them when the garbage collector is invoked.

So, the better solution is add each Target to a HashSet and then in onBitmapLoaded() and onBitmapFailed methods from Target, remove the Target itself from the set.

Exhibitioner answered 10/4, 2014 at 11:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.