There is long time I'm trying to solve this issue. I've read so much questions about similiar issues on stackoverflow and other sources that I'm unable to list all of them.
My problem is different from the others I've read since the first time I try load the fragment with the map all works fine... Is on the second time I try to access the fragment with the map without restarting the app that it crashes with a null pointer on:
googleMap = map.getMap();
because map is null:
map = (SupportMapFragment) myContext.getSupportFragmentManager().
findFragmentById(R.id.map);
The key differences are then:
- The first time it works fine
- The second time findFragmentById returns null, but no error
I can't debug it because AVD emulator has no Google Play services installed.
MainActivity has a NavigationDrawer, and depending on the option selected, a Fragment or another is selected. Here some of the code related to this issue:
fragment_map.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
MyFragment.java:
...
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
...
public class MyFragment extends Fragment implements
GooglePlayServicesClient.ConnectionCallbacks,
GooglePlayServicesClient.OnConnectionFailedListener {
private GoogleMap googleMap;
private View fragmentView;
private static WorldFragment fragment;
private FragmentActivity myContext;
...
public static MyFragment getInstance() {
if (fragment == null) {
fragment = new MyFragment();
}
return fragment;
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
myContext = (FragmentActivity) activity;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
try {
GlobalState gs = (GlobalState) getActivity().getApplication();
// recover View from previous use
fragmentView = gs.getWorldFragmentView();
if (fragmentView == null) {
fragmentView = inflater.inflate(R.layout.fragment_map,
container, false);
// save View for next use
gs.setFragmentView(fragmentView);
} else {
ViewGroup parent = (ViewGroup) fragmentView.getParent();
parent.removeView(fragmentView);
}
} catch (InflateException e) {
// map is already there, just return view as it is
}
return fragmentView;
}
...
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Set up the map fragment
SupportMapFragment map = (SupportMapFragment) myContext.
getSupportFragmentManager().findFragmentById(R.id.map);
/* // If I active this code, no crash, but fragment remains
// in gray background without map.
// At least, if the app crashes, it restarts and the map
// gets loaded the first time
if (map == null) {
Toast.makeText(getActivity(), getString(R.string.maps_not_supported),
Toast.LENGTH_LONG).show();
return;
} */
googleMap = map.getMap();
if (googleMap != null) {
// Enable the current location "blue dot"
googleMap.setMyLocationEnabled(true);
...
} else {
Toast.makeText(getActivity(), getString(R.string.maps_not_supported),
Toast.LENGTH_LONG).show();
}
}
...
}
MainActivity.java:
public class MainActivity extends FragmentActivity implements
NavigationDrawerFragment.NavigationDrawerCallbacks {
@Override
public void onNavigationDrawerItemSelected(int position) {
Fragment contentFragment;
switch (selectedOption) {
case MAP:
contentFragment = MyFragment.getInstance();
break;
case XXX:
...
break;
default:
contentFragment = new OtherFragment();
}
// update the main content by replacing fragments
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.container, contentFragment)
.commit();
}
}
Stacktrace:
java.lang.NullPointerException
at xxx.xxxxx.xxx.fragments.MyFragment.onActivityCreated(MyFragment.java:193)
at android.support.v4.app.Fragment.performActivityCreated(Fragment.java:1508)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:958)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1115)
at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:682)
at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1478)
at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:446)
at android.os.Handler.handleCallback(Handler.java:725)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:153)
at android.app.ActivityThread.main(ActivityThread.java:5297)
at java.lang.reflect.Method.invokeNative(Method.java)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:833)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600)
at dalvik.system.NativeStart.main(NativeStart.java)
Please, let me know if you need some more information.
Thank you very much for your time and attention.
30/12/2014: source code of MainActivity's onCreate() method
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GlobalState applicationContext = (GlobalState) getApplicationContext();
applicationContext.updateLocale();
NavigationDrawerFragment.setItems(applicationContext.getMenuItems());
setContentView(R.layout.activity_main);
mNavigationDrawerFragment = (NavigationDrawerFragment)
getFragmentManager().findFragmentById(R.id.navigation_drawer);
mTitle = getTitle();
// Set up the drawer.
mNavigationDrawerFragment.setUp(
R.id.navigation_drawer,
(DrawerLayout) findViewById(R.id.drawer_layout));
// Treating incoming notifications
Intent intent = getIntent();
String action = intent.getAction();
if (action != null && !action.isEmpty()) {
ItemType itemType = ItemType.valueOf(action);
String title = applicationContext.getMenuItemByType(itemType).getTitle();
String itemId = null;
try {
JSONObject json = new JSONObject(intent.getExtras().getString("com.parse.Data"));
itemId = json.getString(EXTRA_ITEM_ID);
} catch (JSONException e) {
Crashlytics.logException(e);
}
launchFragment(itemType, null, title, itemId);
}
}
You have to remove the fragment in the onDestroyView() method of your MyFragment class.
Sample code to remove: