Search code examples
javaandroidandroid-fragmentsandroid-lifecycle

Why does an Android app go through the activity and fragment lifecycle methods when airplane mode is turned off


I have an android application I am building and one surprising thing that happens is that it crashes when I turn off airplane mode while the application is in the foreground. I tried to include logs in the various lifecycle methods for the activity (onPause, onCreate, onResume) and for the fragment that's displayed the activity's onCreate. It so happens that all these call back methods are called in the following order:

onPause in the Displayed Fragment

onPause in the MainActivity

onCreate in the MainActivity

onViewCreated in the fragment that gets displayed in the MainActivity's onCreate

onStart in the Displayed Fragment

onStart in the MainActivity

onResume in the Displayed Fragment

and the app finally crashes in onPause in the displayed Fragment due to a NullPointerException.

I tried to trace all these callbacks and they are due to the fact that in the displayed fragment's onPause, there are some states that I save in SharedPreferences to restore them when the fragment is resumed and apparently they raise the exception.

May you please help me understand why an application goes through all these lifecycle methods when airplane mode is turned off while the app is in the foreground and the best way to prevent the app from crashing when this happens? I have gone through several posts concerning this issue and I have not found any answer.

Here is the code

public class MainActivity extends AppCompatActivity{

    public static FragmentManager fragmentManager;
    public static final String LOG_TAG = "LOG_TAG";


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Log.i(LOG_TAG, "Main activity created");
        super.onCreate(savedInstanceState);

        // init airplane mode receiver
        IntentFilter intentFilter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
        BroadcastReceiver receiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {

                boolean isAirplaneModeOn = intent.getBooleanExtra("state", false);
                if(isAirplaneModeOn){
                    Log.i(LOG_TAG, "Airplane mode turned on");// handle Airplane Mode on
                } else {
                    Log.i(LOG_TAG, "Airplane mode turned off");
                }
            }
        };
        this.registerReceiver(receiver, intentFilter);


        setContentView(R.layout.activity_main);
        fragmentManager = getSupportFragmentManager();
        displayFragment(new MapFragment());
    }


    @Override
    protected void onPause() {
        super.onPause();
        Log.i(LOG_TAG, "Main activity paused");
    }


    @Override
    protected void onResume() {
        super.onResume();
        Log.i(LOG_TAG, "Main activity resumed");
    }


    public void displayFragment(Fragment fragmentActivity){

        // start the transaction
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        fragmentTransaction.replace(R.id.fragment_container, fragmentActivity).
                    addToBackStack(fragmentActivity.getClass().getSimpleName()).commit();
    }
}

And the fragment

public class MapsFragment extends Fragment implements
        OnMapReadyCallback {


    public MapsFragment() {
        // Required empty public constructor
        super();
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_map, container, false);
    }


    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        FrameLayout map_frame = view.findViewById(R.id.map_frame);

        // configure map
        MapView mapFragment = view.findViewById(R.id.map_view);
        mapFragment.onCreate(savedInstanceState);
        mapFragment.onResume();
        mapFragment.getMapAsync(this);
        View mapView = mapFragment.getRootView();
        super.onViewCreated(mapView, savedInstanceState);
    }

    @Override
    public void onResume() {
        super.onResume();
        Log.i(LOG_TAG, "MapFragment resumed");
    }


    @Override
    public void onPause() {
        super.onPause();
        Log.i(LOG_TAG, "Map fragment paused");
    }

    @Override
    public void onMapReady(GoogleMap googleMap) {
        Log.i(LOG_TAG, "onMapReadyCalled");
        MapsInitializer.initialize(getActivity());
    }
}

Thank you so much in advance.


Solution

  • I just realized that in my code, I was displaying the MapFragment in my MainActivity's onCreate which caused the app to crush. There is no way I could make the app not go through the lifecycle methods when turning off airplane mode while the app was in the foreground. However displaying the MapFragment in my MainActivity's onStart solved the problem.

    @Override
        protected void onStart() {
            super.onStart();
            if (getCurrentFragment() == null) {
                displayFragment(new MapFragment(), null, false, true);
            }
        }
    

    where getCurrentFragment() is a method I defined to return the fragment that is current displayed.