Method of Saving Android RetainFragment State

  • 2021-08-21 21:28:12
  • OfStack

1. Common state saving and recovery methods

① onSaveInstance + onRestoreInstance

This method is the most common way to realize state saving and recovery, which is widely used in Android ecological species, components and View.

② android: configChanges+onConfigurationChanged

This situation applies to screen rotation and configuration changes, as long as the effect is to prevent Activity from rebuilding, so the adjustment of "language" and "time zone" may require restarting Activity to update.

Note:

Language changes need to be configured as


android:configChanges="locale|layoutDirection"

Screen rotation needs to be configured as


android:configChanges="orientation|keyboard|screenSize"

③ onRetainNonConfigurationInstance

This method is an alternative to Mode ② provided in Android system version 3.0. The usage scenario is to allow screen rotation, time zone and language adjustment to respond in time. However, the current system state or tasks need to be saved.

Such as threaded tasks


public class NetWorkTask extends Thread {
  private volatile ProgressUpdateLinster progressUpdateLinster;
  private Handler handler = new Handler(Looper.getMainLooper());

  public NetWorkTask(ProgressUpdateLinster progressUpdateLinster) {
    this.progressUpdateLinster = progressUpdateLinster;
  }

  private int progress = 0;
  @Override
  public void run() {
    while (progress <= 100) {
      if(progressUpdateLinster != null) {
        handler.post(new Runnable() {
         @Override
          public void run() {
            progressUpdateLinster.updateProgress(progress);
          }
        });
      }
      try {
        Thread.sleep(200);
      } catch (InterruptedException e) {
        return;
      }
      progress += 2;
    }
  }

  public interface ProgressUpdateLinster {
    void updateProgress(int progress);
  }

  public void cacel() {
    interrupt();
  }
  public void setProgressUpdateLinster(ProgressUpdateLinster progressUpdateLinster) {  
    this.progressUpdateLinster = progressUpdateLinster;
  }
}

Save state in Activity


private ProgressBar progressBar;
private TextView textView;
private static final String TAG = "MainActivity";

NetWorkTask netWorkTask = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  progressBar = (ProgressBar) findViewById(R.id.progressbar);
  textView = (TextView) findViewById(R.id.tv_progroess);

  if(getLastCustomNonConfigurationInstance() != null
      && getLastCustomNonConfigurationInstance() instanceof NetWorkTask) {

    this.netWorkTask = (NetWorkTask) getLastCustomNonConfigurationInstance(); // Get saved tasks 
    this.netWorkTask.setProgressUpdateLinster(linster);
  }else {
    this.netWorkTask = new NetWorkTask();
    netWorkTask.setProgressUpdateLinster(linster);
    netWorkTask.start();
  }

}

private NetWorkTask.ProgressUpdateLinster linster = new NetWorkTask.ProgressUpdateLinster() {
  @Override
  public void updateProgress(int progress) {
    progressBar.setProgress(progress);
    textView.setText(progress+"%");
    Log.d(TAG,MainActivity.this.toString());
  }
};

/**
*  Save task 
*/
@Override
public Object onRetainCustomNonConfigurationInstance() {
  return netWorkTask;
}

④ RetainFragment

The so-called RetainFragment is not so tall. Fragment and DialogFragment1 are common in themselves. RetainFragment here pays more attention to "use" than the name of Fragment.

Fragment is also the API of Android version 3.0, but a supplement is also provided in support-v4. The principle of this saved state is to add Fragment to the transaction of FragmentManager, but it is not displayed in the interface (and there is no need to implement view), so it can become the background Fragment.

To realize the background Fragment, it must not be destroyed when Activity is reconstructed. The principle is to realize it by setRetainInstance method.


public class WorkFragment extends Fragment {

NetWorkTask netWorkTask = null;

/**
 *  After reconstruction, here Context Will be automatically replaced with the new Activity
 * @param context
 */
@Override
public void onAttach(Context context) {
  super.onAttach(context);
  // No. 1 1 At the time of starting, here network Has not been initialized yet 
  //Activity After rebuilding, update the callback 
  if(netWorkTask != null) {
    netWorkTask.setProgressUpdateLinster((NetWorkTask.ProgressUpdateLinster) context);
  }
}

@Override
public void onDetach() {
  super.onDetach();
  netWorkTask.setProgressUpdateLinster(null);
}

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
  super.onCreate(savedInstanceState); // This method will no longer be called after rebuilding 
  // Set to retain instance Fragment
  setRetainInstance(true);
  netWorkTask = new NetWorkTask();
  netWorkTask.setProgressUpdateLinster((NetWorkTask.ProgressUpdateLinster) getActivity());
  netWorkTask.start();
}
}

Usage in Activity


public class MainActivity extends AppCompatActivity implements NetWorkTask.ProgressUpdateLinster {

private ProgressBar progressBar;
private TextView textView;
private static final String TAG = "MainActivity";
private static final String TAG_TASK_FRAGMENT = "work";

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  progressBar = (ProgressBar) findViewById(R.id.progressbar);
  textView = (TextView) findViewById(R.id.tv_progroess);
  
  // If there is already one work fragment , then there is no need to build new ones 
  if(getSupportFragmentManager().findFragmentByTag(TAG_TASK_FRAGMENT) == null) {
    getSupportFragmentManager().beginTransaction().add(new WorkFragment(),TAG_TASK_FRAGMENT).commit();
  }
}

@Override
public void updateProgress(int progress) {
  progressBar.setProgress(progress);
  textView.setText(progress+"%");
}
}

Related articles: