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.

Hugo Boss
  • 213
  • 1
  • 3
  • 8
  • I would focus on debugging your first approach. Use breakpoints or logging statements to determine where you are failing to get the markers. – CommonsWare Aug 31 '13 at 13:16
  • 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. – Hugo Boss Aug 31 '13 at 13:38
  • 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. – CommonsWare Aug 31 '13 at 13:42
  • Your anonymous `Target` might be coming garbage collected and thats why you lose some of the downloads. – dnkoutso Sep 01 '13 at 16:51
  • How do I avoid this? Same thing happens in 2.0 – Hugo Boss Sep 01 '13 at 20:31
  • 4
    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. – dnkoutso Sep 04 '13 at 04:13
  • 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. – dnkoutso Sep 04 '13 at 04:15
  • I'm doing something similar HERE!!!http://stackoverflow.com/questions/18808114/picasso-loading-of-image-spawned-inside-asynctask – Etienne Lawlor Sep 15 '13 at 02:01
  • Same problem here. dnkoutso absolutely right, it works! Why not post it as answer? – Dmitriy Voronin Sep 26 '13 at 02:04
  • I can load images with Picasso, problem is loading markers using a target – Hugo Boss Oct 01 '13 at 20:33
  • I also did what @dnkoutso proposed and it worked – Benoit Oct 08 '13 at 11:54
  • Alternatively you could make your view implement `Target`. – dnkoutso Feb 15 '14 at 07:13
  • Hi, indeed having a strong reference (e.g. List) 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? – Thomas Kaliakos Feb 22 '14 at 01:37
  • @ThomasKaliakos Does the response include the necessary headers to store the bitmap into disk? http layer handles disk caching. – dnkoutso Apr 02 '14 at 03:20
  • 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. Ευχαριστώ :) – Thomas Kaliakos Apr 02 '14 at 08:09
  • I found the answer here. Hope it helps. http://stackoverflow.com/questions/27095469/how-can-i-use-picasso-to-add-icon-to-marker – Matheus Soares Mar 23 '17 at 00:06

2 Answers2

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) {

        }
    }
Francesco Florio
  • 1,184
  • 14
  • 16
  • 1
    With Picasso 2.4.0 the targets are being garbage collected using this technique. Do you have any kind of advice to fix it? – Imanol Nov 13 '14 at 09:51
  • This is workable for Picasso 2.3.2. Didn't known how to use Target like this before! – Robert Dec 03 '14 at 06:23
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.

icastell
  • 3,495
  • 1
  • 19
  • 27