State not restored correctly


#1

If you click the start button, click the Android’s back button, and then go back to the app via the recents button, the background mRunManager keeps going and you get the LocCat messages from LocationReceiver, but the dynamic Broadcast receiver is no longer working. So, the Stop button is enabled but the UI is no longer getting updated.

I fixed it by having onStart() do this:

@Override public void onStart() { super.onStart(); if (mRunManager != null && mRunManager.isTrackingRun()) { if (mRun == null) { mRun = new Run(); } } getActivity().registerReceiver(mLocationReceiver, new IntentFilter(RunManager.ACTION_LOCATION)); Log.d(TAG, "onStart() called"); }

If you leave and return to the app in a way that only causes onStop() and onStart() to get called, the timer is updated as if you never left. If you leave the app with the back-button and return, it goes through onCreate() and onCreateView() before onStart(), and the timer gets reset to 0.

So, using the start button to start the foreground and background receivers is a problem if you wanted the foreground one to stop when you leave the app.


#2

You’re absolutely right. First, though, a minor technical clarification (this is nitpicking): the background receiver is never “started”. It’s always there. It’s the background broadcast that is being started and that continues to go on when the foreground fragment and activity are destroyed in onDestroy().

The weird part is really nothing to do with the broadcasts - it’s in the mismatch in lifecycle management between the location tracking coming from the LocationManager (which persists for a long time) and the location tracking in mRun (which is bounded by the lifecycle of the activity).

This is a minor flaw in the flow of the exercise. Later on, RunTracker’s run management also becomes centralized and persistent - which is what you’d want in a real app.

I’ll put down your fix in the errata.


#3

I finished the next chapter and can’t get it to go through this code anymore. So, it must have just been an issue here.

I was wondering why mRun when be null when we have the [color=#BF0000]setRetainInstance(true);[/color] command. Dunno.

Thanks.


#4

I use the SharedPreferences to save the startDate value to keep the result of clicking “Home” button & “Back” button having the same UI effect.

        mStartButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mRunManager.startLocationUpdates();
                mRun = new Run();
                mLastLocation = null;

                PreferenceManager.getDefaultSharedPreferences(getActivity())
                        .edit()
                        .putLong(START_DATE, mRun.getStartDate().getTime())
                        .commit();

                updateUI();
            }
        });
        mStopButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mRunManager.stopLocationUpdates();

                PreferenceManager.getDefaultSharedPreferences(getActivity())
                        .edit()
                        .putLong(START_DATE, 0)
                        .commit();

                updateUI();
            }
        });
    public void onStart() {
        super.onStart();
        getActivity().registerReceiver(mLocationReceiver, new IntentFilter(RunManager.ACTION_LOCATION));

        mRun = new Run();
        long startDateLong = PreferenceManager.
                getDefaultSharedPreferences(getActivity()).getLong(START_DATE, 0);
        if (startDateLong != 0) {
            mRun.setStartDate(new Date(startDateLong));
        }
    }