0
votes

I've created a simple app with a Maps fragment. With a help of onInfoWindowClick listener user could click on the marker's info window and another activity with nested fragment opens. I'm using HashMap to link marker with a certain object to populate 2nd activity's fragment with relevant info.

When user clicks on the marker's info window first time 2nd activity launches OK and shows details screen. But when user hits Back button and clicks on this or other info window - app crashes.

I've tried to debug app and found out that after 2nd click on info window marker couldn't be found in HashMap.

I hit a wall here - couldn't find out why the same marker could be found in map first time but second time is not.

Error:

03-27 12:50:34.637: E/AndroidRuntime(31198): FATAL EXCEPTION: main
03-27 12:50:34.637: E/AndroidRuntime(31198): Process: com.example.testingmap, PID: 31198
03-27 12:50:34.637: E/AndroidRuntime(31198): java.lang.NullPointerException
03-27 12:50:34.637: E/AndroidRuntime(31198):    at com.example.testingmap.MapActivity$1.onInfoWindowClick(MapActivity.java:166)
03-27 12:50:34.637: E/AndroidRuntime(31198):    at com.google.android.gms.maps.GoogleMap$12.zze(Unknown Source)
03-27 12:50:34.637: E/AndroidRuntime(31198):    at com.google.android.gms.maps.internal.zzh$zza.onTransact(Unknown Source)
03-27 12:50:34.637: E/AndroidRuntime(31198):    at android.os.Binder.transact(Binder.java:361)
03-27 12:50:34.637: E/AndroidRuntime(31198):    at com.google.android.gms.maps.internal.al.a(SourceFile:82)

P.S. I've cleaned up the code a bit so the stack trace should point somewhere around line 91 instead of 166.

Activity's code:

public class MapActivity extends FragmentActivity implements ConnectionCallbacks, OnConnectionFailedListener {
private GoogleMap mMap;
private LatLng mDogPosition = new LatLng(0.0, 0.0);

private ArrayList<Dog> mDogList;
private HashMap<Marker, Dog> mHashMap;

private GoogleApiClient mGoogleApiClient;
private Location mLastLocation;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_map);

    mDogList = DogCollection.get(this).getDogs();

    buildGoogleApiClient();
}

@Override
protected void onStart(){
    super.onStart();
    mGoogleApiClient.connect();
}

@Override
protected void onStop(){
    mGoogleApiClient.disconnect();
    super.onStop();
}

@Override
protected void onResume(){
    super.onResume();
    initializeMap();
}

private void addingMyMarker(){
    Marker dog = mMap.addMarker(new MarkerOptions()
    .position(mDogPosition)
    .title("The Dog")
    .snippet("Woof-woof!")
    .icon(BitmapDescriptorFactory
        .fromResource(R.drawable.dog_icon_m)));
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(mDogPosition, 15));
mMap.setMyLocationEnabled(true);
}

private void addingMarkers(){
    mHashMap = new HashMap<Marker, Dog>();
    for (Dog dog : mDogList) {
        Marker m = mMap.addMarker(new MarkerOptions()
        .position(dog.getPosition())
        .title(dog.getName())
        .snippet(dog.getDescription())
        .icon(BitmapDescriptorFactory
            .fromResource(dog.getIcon())));
        mHashMap.put(m, dog);
    }
}

protected synchronized void buildGoogleApiClient(){
    mGoogleApiClient = new GoogleApiClient.Builder(this)
        .addConnectionCallbacks(this)
        .addOnConnectionFailedListener(this)
        .addApi(LocationServices.API)
        .build();
}

@Override
public void onConnected(Bundle connectionHint){
    mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
    if (mLastLocation != null && mMap != null) {
        mDogPosition = new LatLng (mLastLocation.getLatitude(), mLastLocation.getLongitude());

        addingMyMarker();
        addingMarkers();
    }
}

private void initializeMap(){
    if (mMap == null){
        mMap = ((MapFragment) getFragmentManager().findFragmentById(R.id.map)).getMap();
    }

    if (mMap != null){
        mMap.setOnInfoWindowClickListener(new OnInfoWindowClickListener(){
            @Override
            public void onInfoWindowClick(Marker marker){
                Dog dog = mHashMap.get(marker);
                Intent intent = new Intent(MapActivity.this, DetailsActivity.class);

                intent.putExtra(DetailsFragment.EXTRA_DOG_ID, dog.getId());
                startActivity(intent);
            }
        });
    }
}
}

SOLVED The problem was with markers that were recreated by initializeMap method each time onResume was called. Every time new marker was added on exactly the same position as former one. I was clicking on old marker which somehow was in front of the new one. In my HashMap actual markers were stored but when I clicked on old one app crashed. Solved this problem by adding mMap.clear() call to delete all old markers in my initializeMap() method.

1

1 Answers

0
votes

Your MapActivity is destroyed once the new activity has started, so when you go back to the MapActivity all of your arraylist and hashmap are empty you can put the debugger on the OnCreate method and you can see the OnCreate method is being called. To save data for the MapActivity when it is created again, you would need to save the data in a bundle by implementing the OnSaveInstanceState method and save the data then retrieve the data from the bundle in the OnCreate method. It is easy to save arraylists of simple data types such strings, integers, etc. but for an object such as Dog, it need to implement Parcelable to be able save the array of Dog objects then recreate the markers.

first the Dog object need to implement Parcelable,

public class Dog implements Parcelable{

here is a good post that have a good example of implementing Parcelable, How can I make my custom objects Parcelable?

then you need to change your OnCreate method to as follows,

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

if(savedInstanceState!=null)
{
  mDogList = savedInstanceState.getParcelableArrayList("My Dogs");
  addingMarkers();
}

Also you would need to implement onSaveInstanceState to save your list of Dog objects,

@Override
  protected void onSaveInstanceState(Bundle bundle) 
  {
    super.onSaveInstanceState(bundle);
    bundle.putParcelableArrayList(mDogList);
  }

I hope this works for you. I have logic that works with saving several array lists of strings but not objects, but the key here is implementing Parcelable and rebuilding your HashMap in the onCreate method.