If you’re an experienced Android programmer, you’ve probably hand-written a number of onSaveInstanceState and onRestoreInstanceState methods to keep your Activity in state, Since an Activity becomes invisible, the system can reclaim it at any time to free up memory. Overriding the onSaveInstanceState method in an Activity is a Google recommended way to keep the Activity in state.

Best practices recommended by Google

The onSaveInstanceState method will give us a Bundle object to hold the value we want to hold, but the Bundle store is based on the form key-value, so we need to define some additional String key constants. Eventually our project would be filled with code like this:

static final String STATE_SCORE = "playerScore";
static final String STATE_LEVEL = "playerLevel";
// ...


@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
    // Save the user's current game state
    savedInstanceState.putInt(STATE_SCORE, mCurrentScore);
    savedInstanceState.putInt(STATE_LEVEL, mCurrentLevel);

    // Always call the superclass so it can save the view hierarchy state
    super.onSaveInstanceState(savedInstanceState);
}
Copy the code

After saving the state, in order to restore the state before the system kills the Activity when it is reinstantiated, we retrieve the saved value in the onCreate method:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState); // Always call the superclass first

    // Check whether we're recreating a previously destroyed instance
    if(savedInstanceState ! =null) {
        // Restore value of members from saved state
        mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
        mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
    } else {
        // Probably initialize members with default values for a new instance
    }
    // ...
}
Copy the code

Of course, this can also be done in the onRestoreInstanceState method:

public void onRestoreInstanceState(Bundle savedInstanceState) {
    // Always call the superclass so it can restore the view hierarchy
    super.onRestoreInstanceState(savedInstanceState);

    // Restore state members from saved instance
    mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
    mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
}
Copy the code

Free your hands

The above scheme is certainly correct. But it’s not elegant. In order to preserve the value of the variable, two methods (onSaveInstanceState and onRestoreInstanceState) and two constants (two constants defined to store the two variables, just to put in the Bundle) are introduced.

To better solve this problem, I wrote the SaveState plugin:

After using the SaveState plugin, this is how to maintain the state of an Activity:

public class MyActivity extends Activity {

    @AutoRestore
    int myInt;

    @AutoRestore
    IBinder myRpcCall;

    @AutoRestore
    String result;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // Your code here}}Copy the code

That’s right, you just need to mark the @AutoRestore annotation on the variables you want to hold, and you don’t need to bother with a few Activity callbacks or define redundant String key constants.

So, in addition to activities, can fragments automatically hold state? The answer: Yes!

public class MyFragment extends Fragment {

    @AutoRestore
    User currentLoginUser;

    @AutoRestore
    List<Map<String, Object>> networkResponse;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        // Your code here}}Copy the code

Use the same method as Activity! Not only that, but the usage scenario can also be generalized to views, from which your custom View can also delegate the task of maintaining state to SaveState:

public class MyView extends View {

    @AutoRestore
    String someText;

    @AutoRestore
    Size size;

    @AutoRestore
    float[] myFloatArray;

    public MyView(Context context) {
        super(context);
    }

    public MyView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr); }}Copy the code

Use SaveState now

The way to introduce SaveState is also simple:

First, add the following to the build.gradle file in the project root directory:

buildscript {

    repositories {
        google()
        jcenter()
    }
    dependencies {
        // your other dependencies

        // dependency for save-state
        classpath "io.github.prototypez:save-state:${latest_version}"}}Copy the code

Then, apply the plugin in the build.gradle file of the Application and Library modules:

apply plugin: 'com.android.application'
// apply plugin: 'com.android.library'
apply plugin: 'save.state'
Copy the code

Everything! No more annoying callbacks to write, because your time is worth a lot! Did a little bit of work, if I save you a cup of coffee time, hope you can help me order a Star, thank you 🙂

SaveState Github: github.com/PrototypeZ/…

Kotlin, Happy Hacking!