How best to re-create markers/polylines when phone is rotated (orientation change)
Asked Answered
G

2

14

Background:

  • Developing a native Android App that uses Android Google Map v2, uses android.support.v4.app.FragmentActivity. Runs on Android v2.2.

Objective:

  • Retain Markers/Polylines "drawn" on map before the phone orientation is changed.

Question(s):

  1. Can I "save" the markers/polylines as part of the bundle and simply have them re-displayed by saving them in the onSaveInstanceState using the appropriate savedInstance.put.. methods and then "restore" them in onCreate using the appropriate savedInstanceState.get.. methods.

    In reviewing the description for Marker getID() I'm confused when the Google documentation for Marker.getId() method states the following:

    When a map is restored from a Bundle, markers that were on that map are also restored. However, those markers will then be represented by different Marker objects. A marker's id can be used to retrieve the new instance of a Marker object after such restoration.

    The Google documentation (bold text above) makes it sound like the Marker's are just automagically restored without having to take any action. That isn't my experience...Maybe I'm mis-interpreting what is being stated. Or maybe you have to explicitly save map in the Bundle? Can someone clarify what this means?

  2. Assuming I will have to explicitly save the Marker and Polylines to the bundle via the appropriate savedInstance.put... method should I save the entire Marker or should I save the marker id and retrieve the marker info using the marker id to re-display the marker? I couldn't find a put method that would allow me to save the entire Marker.

    I noticed that the MapFragment section of the Google Maps Android API v2 it states the following:

    Any objects obtained from the GoogleMap is associated with the view. It's important to not hold on to objects (e.g. Marker) beyond the view's life. Otherwise it will cause a memory leak as the view cannot be released.

    This statement leads me to believe that I shouldn't try and save the Marker itself but instead try and save the marker id and then re-generate the marker based on the marker object associated with the marker id. Likewise for the PolyLines. Is my assumption correct?

  3. Also, should I avoid having the Marker as a class variable? My concern is that if the Marker is a class variable and the Activity Map fragment is placed on the back stack that this could cause a memory leak because it will effectively be "holding on to the object" as noted in the aforementioned documentation. Is this something I should be concerned about?

Regards.

Gavrielle answered 3/6, 2013 at 17:34 Comment(0)
A
14

Can I "save" the markers/polylines as part of the bundle and simply have them re-displayed by saving them in the onSaveInstanceState using the appropriate "savedInstance.put.." methods and then "restore" them in onCreate using the appropriate "savedInstanceState.get.." methods.

No.

The Google documentation (bolded text above) makes it sound like the Marker's are just automagically restored without having to take any action. That isn't my experience...Maybe I'm mis-interpreting what is being stated. Or maybe you have to explicitly save map in the Bundle? Can someone clarify what this means?

You are not mis-interpreting anything. The documentation is incorrect.

should I save the Marker ID and retrieve the marker info using the marker ID to re-display the marker

Marker ID is not something permanent across configuration changes - it depends on the sequence of visual objects creation (first call to addMarker returns object with ID "m1", second "m2"). You cannot use this value in any way (as of API version 3.1.36) and IMHO there is really no point in its existance. I have actually sumbited an issue related to ID. There should at least be a function GoogleMap.getMarkerById(String) for Marker.getId() to make little sense.

3) Also, should I avoid having the Marker as a Class variable? My concern is that if the Marker is a class variable and the Activity Map fragment is placed on the back stack that this could cause a memory leak because it will effectively be "holding on to the object" as noted in the aforementioned documentation. Is this something I should be concerned about?

Yes. Keeping a static reference to Marker or any other visual object leads to a leak.


Not that answering all your questions and concerns made it closer to the solution, so here are my propositions.

I assume you have a data set that Markers and Polylines are created from and probably store it in DB after being fetched from webservice. Now if you load it from DB in AsyncTask in your Activity or not even store it in DB, but only fetch in Activity directly - that's bad.

Try to make your data as accessible as possible, so it only has to be reload into memory after process is killed (or after you drop it when low on memory). If you do that - that's good. Of course not all: if you have 20000 markers and each has its image displayed on info window, it can wait...

Now that you have all the data needed to create markers in memory, just create them like you would the first time. No additional code required.

We can argue about this being good or bad idea and I will improve the answer, but this requires more info about the context:

  • how many markers and polylines
  • what additional data you have
  • where do you keep your model
  • etc.

There is another way of course: you can send MarkerOptions in onSaveInstanceState. This might work for you if you keep it up-to-date or if your Markers do not change and if there are not so many of them. I can't make sense of sending thousands of objects via IPC on every rotation or pressing HOME button.

Arresting answered 3/6, 2013 at 18:50 Comment(5)
Thank you Macie...just clarifying what can and can't be done is extremely helpful. Hopefully someone has filed an issue against the Google documentation.Gavrielle
As background...I only have one marker that I'm worried about here and two polylines connected to this one marker. The marker represents the location that the user tapped on the screen (tapMarker). The 1st polyline connects the users current GPS connection to the tapMarker and the second polyline connects the tap marker to a designated LatLng. The data for designated LatLng will be stored in the database.Gavrielle
In that case you can just send LatLng of the tap in onSaveInstanceState. It makes sense, as it is user activity (just like editing a text field). Optionally two ArrayList<LatLng> to recreate polylines. I thought my answer would be of more use ;)Lucchesi
Your answer was very useful! Now I know what is and isn't possible. Thank you!Gavrielle
There is a Marker.getId() instance method which can be useful in situations like onInfoWindowClick(Marker m). For example, you can find corresponding data object with the id.Guide
S
14

It seems to me that calling setRetainInstance(true); on the fragment that holds the map retains everything on the map through pauses, orientation changes, etc, without having to worry about setting and getting. Is there some reason why this isn't the recommended approach?

Scientism answered 27/2, 2014 at 4:19 Comment(5)
And I can confirm it works fine! It's that simple. Why is this post not marked as answer?Stanchion
For one it does not work if your map fragment is nested inside another fragment. You get a runtime exception.Isle
So what happens if you setRetainInstance on both fragments? (sorry, away from testing environment but still curious)Scientism
You savior! I spent a whole weekend researching Activity Lifecycles, Bundles, Parcels, Parcelable interfaces and IPCs within Android ... to no avail. All I needed was a single line of code to save my state.Addlebrained
Is there a way to add a callback function for when there is an orientation change? I tried onConfigurationChanged but that crashes the app because I assume the GoogleMap isn't readyGarett

© 2022 - 2024 — McMap. All rights reserved.