I have an Activity
with a MapFragment
that I add to the Activity
programmatically using a FragmentTransaction
:
private static final String MAP_FRAGMENT_TAG = "map";
private MapFragment mapFragment = null;
...
protected void onCreate(Bundle savedInstanceState) {
...
mapFragment = (MapFragment) getFragmentManager().findFragmentByTag(MAP_FRAGMENT_TAG);
if (mapFragment == null) {
mapFragment = MapFragment.newInstance();
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.add(R.id.fragment_wrapper, mapFragment, MAP_FRAGMENT_TAG);
fragmentTransaction.commit();
}
...
}
Standard way. Then I get the GoogleMap
instance from the mapFragment
and set its settings, set the listeners, do stuff with it. Everything works fine.
Then when the user is done with the map, an AsyncTask
gets triggered to show a ProgressDialog
, perform some operation, put a different fragment into the fragment_wrapper
and dismiss the ProgressDialog
again:
private class GetFlightsTask extends AsyncTask<Double, Void, String> {
@Override
protected void onPreExecute() {
super.onPreExecute();
// the activity context has been passed to the AsyncTask through its constructor
loadingFlightsSpinner = new ProgressDialog(context);
// setting the dialog up
loadingFlightsSpinner.show();
}
@Override
protected String doInBackground(Double... params) {
// some pretty long remote API call
// (loading a JSON file from http://some.website.com/...)
}
@Override
protected void onPostExecute(String flightsJSON) {
super.onPostExecute(flightsJSON);
// here I do stuff with the JSON and then I swtich the fragments like this
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
FlightsFragment fragment = new FlightsFragment();
fragmentTransaction.replace(R.id.fragment_wrapper, fragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
loadingFlightsSpinner.dismiss();
}
Everything still works fine. The user does something in the FlightsFragment
and then maybe decides to go back to the map. Presses the back button and the map pops up again. And this is when the map gets laggy. The countries/cities names on it load really slowly, it lags heavily on moving the map... And I have no idea why, I don't do anything on popping the MapFragment
back.
What's interesting is that it gets fixed for example on pressing the home button and then returning to the app again...
What am I doing wrong?
Thank you for any ideas.
I have run a simple test:
public class MapFragmentOnBackStackExample extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.map_fragment_on_back_stack_example);
FragmentManager fm = getSupportFragmentManager();
Fragment f = fm.findFragmentById(R.id.fragment_container);
if (f == null) {
f = SupportMapFragment.newInstance();
FragmentTransaction transaction = fm.beginTransaction();
transaction.add(R.id.fragment_container, f);
transaction.commit();
}
}
public void onAddFragmentClick(View view) {
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction transaction = fm.beginTransaction();
transaction.replace(R.id.fragment_container, new MyFragment());
transaction.addToBackStack(null);
transaction.commit();
}
public static class MyFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
TextView textView = new TextView(getActivity());
textView.setText("MyFragment: " + hashCode());
return textView;
}
}
}
and can't see any problems.
I could see the problem when commented if (f == null) {
leaving it to always create new fragment on rotation, which is obviously wrong, but that brings some suspicions.
Can you see more than 1 MapFragment in memory at the same time? Try using Eclipse Memory Analyzer (MAT).
it only lag if you press the back button?
if thats the problem try to block the back button or make it exit the app try this code:
@Override
public void onBackPressed(){
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("You wanna leave the aplication?").setPositiveButton("Yes", dialogClickListener)
.setNegativeButton("No", dialogClickListener).show();
}
or try this code its a way to put a map fragment inside another fragment (nested Map Fragment) it worked for me a weeks ago:
Java Class:
public class Yourfragment extends Fragment {
private MapView mMapView;
private GoogleMap mMap;
private Bundle mBundle;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View inflatedView = inflater.inflate(R.layout.map_fragment, container, false);
try {
MapsInitializer.initialize(getActivity());
} catch (GooglePlayServicesNotAvailableException e) {
// TODO handle this situation
}
mMapView = (MapView) inflatedView.findViewById(R.id.map);
mMapView.onCreate(mBundle);
setUpMapIfNeeded(inflatedView);
return inflatedView;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBundle = savedInstanceState;
}
private void setUpMapIfNeeded(View inflatedView) {
if (mMap == null) {
mMap = ((MapView) inflatedView.findViewById(R.id.map)).getMap();
if (mMap != null) {
setUpMap();
}
}
}
private void setUpMap() {
mMap.addMarker(new MarkerOptions().position(new LatLng(0, 0)).title("Marker"));
}
@Override
public void onResume() {
super.onResume();
mMapView.onResume();
}
@Override
public void onPause() {
super.onPause();
mMapView.onPause();
}
@Override
public void onDestroy() {
mMapView.onDestroy();
super.onDestroy();
}
}
XML:
<com.google.android.gms.maps.MapView
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent" />
put this code on post execute:
View inflatedView = inflater.inflate(R.layout.map_fragment, container, false);
try {
MapsInitializer.initialize(getActivity());
} catch (GooglePlayServicesNotAvailableException e) {
// TODO handle this situation
}
mMapView = (MapView) inflatedView.findViewById(R.id.map);
mMapView.onCreate(mBundle);
setUpMapIfNeeded(inflatedView);
return inflatedView;
and call the assynctask on oncreateview
Try this:
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//Call assyncTask
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBundle = savedInstanceState;
}
private void setUpMapIfNeeded(View inflatedView) {
if (mMap == null) {
mMap = ((MapView) inflatedView.findViewById(R.id.map)).getMap();
if (mMap != null) {
setUpMap();
}
}
}
private void setUpMap() {
mMap.addMarker(new MarkerOptions().position(new LatLng(0, 0)).title("Marker"));
}
@Override
public void onResume() {
super.onResume();
mMapView.onResume();
}
@Override
public void onPause() {
super.onPause();
mMapView.onPause();
}
@Override
public void onDestroy() {
mMapView.onDestroy();
super.onDestroy();
}
private class GetFlightsTask extends AsyncTask<Double, Void, String> {
@Override
protected void onPreExecute() {
super.onPreExecute();
// if I remove the next line, everything gets fixed
loadingFlightsSpinner.show();
}
@Override
protected String doInBackground(Double... params) {
// some pretty long remote API call
// (loading a JSON file from http://some.website.com/flights?...)
// works fine
String flightsJSON = loadJSON("flights?flyFrom=CZ&to=...");
}
@Override
protected void onPostExecute(String flightsJSON) {
super.onPostExecute(flightsJSON);
loadingFlightsSpinner.dismiss();
// here I do stuff with the JSON and then replace the fragment
dohardwork()
}
public view dohardwork(){
View inflatedView = inflater.inflate(R.layout.map_fragment, container, false);
try {
MapsInitializer.initialize(getActivity());
} catch (GooglePlayServicesNotAvailableException e) {
// TODO handle this situation
}
mMapView = (MapView) inflatedView.findViewById(R.id.map);
mMapView.onCreate(mBundle);
setUpMapIfNeeded(inflatedView);
return inflatedView;
}
I have fixed it by dismissing the ProgressDialog
already at the end of the AsyncTask
's doInBackground()
method rather than at the beginning of the onPostExecute()
method.
Which is a bit weird because I actually thought I shouldn't touch things from the UI in the doInBackground()
method... If someone wants to elaborate on it a bit, I would be glad to learn why it works like this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With