Search code examples
androidgoogle-mapsandroid-asynctaskgoogle-maps-markerspicasso

Picasso not loading images on first app start


I have Activity with GoogleMap and I send request to server, parse JSON response and add Markers, using Picasso for loading icons.

PROBLEM

The problem is next: when I close app and open It again, data loads as well (urls also loading well), Markers are added on GoogleMap, but they have default icons, Picasso doesn't load new icons (onBitmapLoaded() not called). When I finish Activity and start it again (without app closing), new markers' icons loads as well.

My implementation of Picasso Target:

public class PicassoMarker implements Target {
    private static final String TAG = "PicassoMarker";
    private Marker marker;
    private LoadingCallBacks loadingCallBacks;

    public PicassoMarker(Marker marker) {
        this.marker = marker;
    }

    public PicassoMarker(Marker marker, LoadingCallBacks loadingCallBacks) {
        this.marker = marker;
        this.loadingCallBacks = loadingCallBacks;
    }

    @Override
    public int hashCode() {
        return marker.hashCode();
    }

    @Override
    public boolean equals(Object o) {
        if (o instanceof PicassoMarker) {
            Marker marker = ((PicassoMarker) o).marker;
            return this.marker.equals(marker);
        } else {
            return false;
        }
    }

    @Override
    public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
        marker.setIcon(BitmapDescriptorFactory.fromBitmap(bitmap));
        Log.d(TAG, "onBitmapLoaded() -> " +
                "\nBitmap:" +
                "\n\twidth: " + bitmap.getWidth() +
                "\n\theight: " + bitmap.getHeight() /*+
                "\nCanvas:" +
                "\n\twidth: " + resultBitmap.getWidth() +
                "\n\theight: " + resultBitmap.getHeight()*/
        );

        if (loadingCallBacks != null) {
            loadingCallBacks.onLoaded(bitmap);
        }
    }

    @Override
    public void onBitmapFailed(Drawable errorDrawable) {
        Log.e(TAG, "onBitmapFailed()");
    }

    @Override
    public void onPrepareLoad(Drawable placeHolderDrawable) {
        Log.d(TAG, "onPrepareLoad()");
    }

    public void setLoadingCallBacks(LoadingCallBacks loadingCallBacks) {
        this.loadingCallBacks = loadingCallBacks;
    }

    public interface LoadingCallBacks {
        void onLoaded(Bitmap bitmap);
    }
}

And my AsyncTask for sending request and adding Markers:

new AsyncTask<String, Void, ArrayList<MarkerDataModel>>() {
            private ArrayList<MarkerDataModel> markerDataModels = new ArrayList<>();

            @Override
            protected void onPreExecute() {
            }

            @Override
            protected ArrayList<MarkerDataModel> doInBackground(String... params) {
                // Parsing JSON ...

                return markerDataModels;
            }

            @Override
            protected void onPostExecute(ArrayList<MarkerDataModel> markerDataModels) {
                if (markerDataModels != null && markerDataModels.size() != 0) {
                    MarkerDataModel markerDataModel;

                    for (int i = 0; i < markerDataModels.size(); i++) {
                        markerDataModel = markerDataModels.get(i);

                        Marker marker = googleMap.addMarker(new MarkerOptions()
                                .position(new LatLng(markerDataModel.getLat(), markerDataModel.getLng()))
                                .title(markerDataModel.getLabel()));

                        PicassoMarker picassoMarker picassoMarker = new PicassoMarker(marker);
                        Picasso.
                                with(activityContext)
                                .load(markerDataModel.getIcon())
                                //.memoryPolicy(MemoryPolicy.NO_CACHE, MemoryPolicy.NO_STORE)
                                //.networkPolicy(NetworkPolicy.NO_CACHE)
                                .into(picassoMarker);
                    }
                }
            }
        }.execute("http://example.com");

Solution

  • SOLUTION

    I've solved problem, using Glide.

    Here is example code for loading Markers' data from network and setting new icons using Glide:

    new AsyncTask<String, Void, ArrayList<MarkerDataModel>>() {
    
                @Override
                protected void onPreExecute() {
                    ...
                }
    
                @Override
                protected ArrayList<MarkerDataModel> doInBackground(String... params) {
                    ArrayList<MarkerDataModel> markerDataModels = new ArrayList<>();
    
                    Send request and parse response...
    
                    return markerDataModels;
                }
    
                @Override
                protected void onPostExecute(ArrayList<MarkerDataModel> markerDataModels) {
                    if (markerDataModels != null && markerDataModels.size() != 0) {
    
                        for (int i = 0; i < markerDataModels.size(); i++) {
                            MarkerDataModel markerDataModel = markerDataModels.get(i);
    
                            final Marker marker = googleMap.addMarker(new MarkerOptions()
                                    .position(new LatLng(markerDataModel.getLat(), markerDataModel.getLng()))
                                    .title(markerDataModel.getLabel()));
    
                // Getting icon's URL from DataModel and loading icon
                Glide
                    .with(activityContext)
                    .load(markerDataModel.getIcon())
                    .asBitmap()
                    .into(new SimpleTarget<Bitmap>() {
                    @Override
                    public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
                        // Setting new icon for Marker
                        marker.setIcon(BitmapDescriptorFactory.fromBitmap(resource));
                    }
                    });
                        }
                    }
                }
            }.execute(...);
    

    And MarkerDataModel is simple class for storing Marker's data:

    public class MarkerDataModel {
    
        public MarkerDataModel() {
        }
    
        private final String TAG = "MarkerDataModel";
    
        public static final int TYPE_PRIMARY = 1;
        public static final int TYPE_PEOPLE = 2;
    
        private int type;
        private int id;
        private double lat;
        private double lng;
        private Marker marker;
    
        /**
         * TYPE_PRIMARY
         **/
        private String icon;
        private String label;
        private String nameShort;
        private String nameFull;
        private String phone;
        private String information;
        private String website;
        private String sliderImages;
    
        /**
         * TYPE_PEOPLE
         **/
        private String photoUrl;
        private String uniqueId;
        private String registerTime;
    
        public MarkerDataModel(int type,
                               int id,
                               double lat,
                               double lng,
                               String icon,
                               String label,
                               String nameShort,
                               String nameFull,
                               String phone,
                               String information,
                               String website,
                               String sliderImages) {
    
            this.type = type;
            this.id = id;
            this.lat = lat;
            this.lng = lng;
            this.icon = icon;
            this.label = label;
            this.nameShort = nameShort;
            this.nameFull = nameFull;
            this.phone = phone;
            this.information = information;
            this.website = website;
            this.sliderImages = sliderImages;
        }
    
        public MarkerDataModel(int type,
                               int id,
                               double lat,
                               double lng,
                               String photoUrl,
                               String uniqueId,
                               String registerTime) {
            this.type = type;
            this.lat = lat;
            this.lng = lng;
            this.photoUrl = photoUrl;
            this.uniqueId = uniqueId;
            this.registerTime = registerTime;
        }
    
        /**
         * Getters
         */
    
        public int getType() {
            return type;
        }
    
        @Nullable
        public Marker getMarker() {
            return marker;
        }
    
        public int getId() {
            return id;
        }
    
        public double getLat() {
            return lat;
        }
    
        public double getLng() {
            return lng;
        }
    
        /**
         * TYPE_PRIMARY
         **/
    
        public String getIcon() {
            return icon;
        }
    
        public String getInformation() {
            return information;
        }
    
        public String getLabel() {
            return label;
        }
    
        public String getNameFull() {
            return nameFull;
        }
    
        public String getNameShort() {
            return nameShort;
        }
    
        public String getPhone() {
            return phone;
        }
    
        public String getSliderImages() {
            return sliderImages;
        }
    
        public String getWebsite() {
            return website;
        }
    
    
        /**
         * TYPE_PEOPLE
         **/
        public String getPhotoUrl() {
            return photoUrl;
        }
    
        public String getUniqueId() {
            return uniqueId;
        }
    
        public String getRegisterTime() {
            return registerTime;
        }
    
    
        @Nullable
        public LatLng getLatLng() {
            try {
                return new LatLng(lat, lng);
            } catch (Exception ex) {
                Log.e(TAG, "getLatLng() -> ", ex);
                return null;
            }
        }
    
        /**
         * Setters
         */
    
        public void setMarker(Marker marker) {
            this.marker = marker;
        }
    }
    

    BENEFITS

    Switching between TYPE_PRIMARY and TYPE_PEOPLE you can catch type of clicked Marker like this:

    googleMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
        @Override
        public boolean onMarkerClick(Marker marker) {
            MarkerDataModel markerDataModel = markerAllHashMap.get(marker);
            int type;
    
            try {
                Log.d(TAG, "onMarkerClick() -> " + markerDataModel.getType());
            } catch (Exception ex) {
                Log.e(TAG, "onMarkerClick() -> ", ex);
            }
    
            try {
                type = markerDataModel.getType();
            } catch (Exception ex) {
                type = -1;
            }
    
            switch (type) {
                case MarkerDataModel.TYPE_PRIMARY: {
                    // Do something for TYPE_PRIMARY Marker
                    break;
                }
                case MarkerDataModel.TYPE_PEOPLE: {
                    // Do something for TYPE_PEOPLE Marker (e.g. show user's info)
                    break;
                }
                default: {
                    // Type == -1
                    marker.showInfoWindow();
                    break;
                }
            }
    
            // Animate to center
            googleMap.animateCamera(CameraUpdateFactory.newCameraPosition(new CameraPosition.Builder()
                    .target(marker.getPosition())
                    .zoom(18)
                    .build()));
    
            return true;
        }
    });