进一步理解 setRetainInstance (true)

当你在 Fragment上调用 setRetainInstance(true)时,没错会发生什么?文档实际上是不存在的,这似乎是一个非常重要的功能。具体来说,我想知道这个序列(我编造的)有多少是真的:

  1. 用户旋转设备。
  2. 该片段与 Activity分离,并调用 Fragment.onDetach()
  3. 活动被销毁; 调用 Activity.onDestroy()
  4. 删除 Activity java 对象(如果可能,通过 GC)。
  5. 创建一个新的 Activity java 对象; 它的构造函数和 onCreate()被调用。
  6. Activity.onCreate()中,我们要么使用 setContentView(...)来设置包含片段的布局,要么使用 FragmentTransaction 添加一个片段。
  7. 我真的不知道这一点,但我认为,机器人足够聪明,找到旧的片段,并调用 Fragment.onAttach() 把它重新接到新的 Activity
  8. 下一个(或之前? 谁知道呢?) Activity.onResume()被调用。

是这样吗?Android 是否足够聪明,能够找到旧的片段,即使我第一次明确使用 FragmentTransaction.add(new MyFragment(), ...)?如果是这样,如何避免在 onCreate()中添加 另一个片段?我需要这么做吗?:

    if (getSupportFragmentManager().findFragmentByTag("foo") == null)
{
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(new FooFragment(), "foo").commit();
}
55844 次浏览

setRetainInstance() in Fragment class is a clever replacement for onRetainCustomNonConfigurationInstance() of Activity class, and more.

Clearly stated in the documentation.

Here's Log of what happens (A UI fragment add on demand and then a config change):

Default setRetainInstance(false)

09-29 13:23:04.771: DEBUG/szipinf(4790): Initializing inflate state
09-29 13:23:04.801: INFO/TESTING - MYACTIVITY(4790): Instantiated - com.example.MyActivity@405196b0
09-29 13:23:04.851: DEBUG/dalvikvm(4790): GC_EXTERNAL_ALLOC freed 49K, 51% free 2686K/5379K, external 0K/0K, paused 45ms
09-29 13:23:04.881: INFO/TESTING - MYACTIVITY(4790): onCreate - com.example.MyActivity@405196b0
09-29 13:23:04.881: INFO/TESTING - MYACTIVITY(4790): onStart - com.example.MyActivity@405196b0
09-29 13:23:04.881: INFO/TESTING - MYACTIVITY(4790): onResume - com.example.MyActivity@405196b0
09-29 13:23:04.891: INFO/TESTING - MYACTIVITY(4790): onAttachedToWindow - com.example.MyActivity@405196b0
09-29 13:23:10.381: DEBUG/dalvikvm(4457): GC_EXPLICIT freed 8K, 51% free 2681K/5379K, external 0K/0K, paused 38ms
09-29 13:23:11.901: INFO/TESTING - MYFRAGMENT(4790): Instantiated - MyFragment{40530610}
09-29 13:23:11.911: INFO/TESTING - MYFRAGMENT(4790): onAttach - MyFragment{40530610 #0 MyFragment}
09-29 13:23:11.911: INFO/TESTING - MYACTIVITY(4790): onAttachFragment - com.example.MyActivity@405196b0
09-29 13:23:11.911: INFO/TESTING - MYFRAGMENT(4790): onCreate - MyFragment{40530610 #0 MyFragment}
09-29 13:23:11.911: INFO/TESTING - MYFRAGMENT(4790): onCreateView - MyFragment{40530610 #0 MyFragment}
09-29 13:23:11.921: INFO/TESTING - MYFRAGMENT(4790): onViewCreated - MyFragment{40530610 #0 MyFragment}
09-29 13:23:11.921: INFO/TESTING - MYFRAGMENT(4790): onActivityCreated - MyFragment{40530610 #0 MyFragment}
09-29 13:23:11.931: INFO/TESTING - MYFRAGMENT(4790): onStart - MyFragment{40530610 #0 MyFragment}
09-29 13:23:11.931: INFO/TESTING - MYFRAGMENT(4790): onResume - MyFragment{40530610 #0 MyFragment}
09-29 13:23:15.081: INFO/ActivityManager(1268): Config changed: { scale=1.0 imsi=404/45 loc=en_US touch=3 keys=1/1/2 nav=1/1 orien=2 layout=34 uiMode=17 seq=105 themeResource=null}
09-29 13:23:15.111: INFO/TESTING - MYACTIVITY(4790): onSaveInstanceState - com.example.MyActivity@405196b0
09-29 13:23:15.111: INFO/TESTING - MYFRAGMENT(4790): onPause - MyFragment{40530610 #0 MyFragment}
09-29 13:23:15.121: INFO/TESTING - MYACTIVITY(4790): onPause - com.example.MyActivity@405196b0
09-29 13:23:15.121: INFO/TESTING - MYFRAGMENT(4790): onStop - MyFragment{40530610 #0 MyFragment}
09-29 13:23:15.121: INFO/TESTING - MYACTIVITY(4790): onStop - com.example.MyActivity@405196b0
09-29 13:23:15.121: INFO/TESTING - MYACTIVITY(4790): onRetainCustomNonConfigurationInstance - com.example.MyActivity@405196b0
09-29 13:23:15.121: INFO/TESTING - MYFRAGMENT(4790): onDestroyView - MyFragment{40530610 #0 MyFragment}
09-29 13:23:15.121: INFO/TESTING - MYFRAGMENT(4790): onDestroy - MyFragment{40530610 #0 MyFragment}
09-29 13:23:15.121: INFO/TESTING - MYFRAGMENT(4790): onDetach - MyFragment{40530610 #0 MyFragment}
09-29 13:23:15.121: INFO/TESTING - MYACTIVITY(4790): onDestroy - com.example.MyActivity@405196b0
09-29 13:23:15.191: INFO/TESTING - MYACTIVITY(4790): onDetachedFromWindow - com.example.MyActivity@405196b0
09-29 13:23:15.201: INFO/TESTING - MYACTIVITY(4790): Instantiated - com.example.MyActivity@4053c438
09-29 13:23:15.201: INFO/TESTING - MYFRAGMENT(4790): Instantiated - MyFragment{4053cde0}
09-29 13:23:15.201: INFO/TESTING - MYFRAGMENT(4790): onAttach - MyFragment{4053cde0 #0 MyFragment}
09-29 13:23:15.201: INFO/TESTING - MYACTIVITY(4790): onAttachFragment - com.example.MyActivity@4053c438
09-29 13:23:15.201: INFO/TESTING - MYFRAGMENT(4790): onCreate - MyFragment{4053cde0 #0 MyFragment}
09-29 13:23:15.251: INFO/TESTING - MYACTIVITY(4790): onCreate - com.example.MyActivity@4053c438
09-29 13:23:15.251: INFO/TESTING - MYFRAGMENT(4790): onCreateView - MyFragment{4053cde0 #0 MyFragment}
09-29 13:23:15.261: INFO/TESTING - MYFRAGMENT(4790): onViewCreated - MyFragment{4053cde0 #0 MyFragment}
09-29 13:23:15.261: INFO/TESTING - MYFRAGMENT(4790): onActivityCreated - MyFragment{4053cde0 #0 MyFragment}
09-29 13:23:15.291: INFO/TESTING - MYFRAGMENT(4790): onStart - MyFragment{4053cde0 #0 MyFragment}
09-29 13:23:15.291: INFO/TESTING - MYACTIVITY(4790): onStart - com.example.MyActivity@4053c438
09-29 13:23:15.291: INFO/TESTING - MYACTIVITY(4790): onResume - com.example.MyActivity@4053c438
09-29 13:23:15.291: INFO/TESTING - MYFRAGMENT(4790): onResume - MyFragment{4053cde0 #0 MyFragment}
09-29 13:23:15.321: INFO/TESTING - MYACTIVITY(4790): onAttachedToWindow - com.example.MyActivity@4053c438

So, Fragment is recreated whole new, and shown again, all this while setRetainInstance(false)

And now with setRetainInstance(true)

09-29 13:18:46.121: INFO/ActivityManager(1268): Starting: Intent { flg=0x10100000 cmp=com.example/.MyActivity } from pid 1268
09-29 13:18:46.141: INFO/TESTING - MYACTIVITY(4726): Instantiated - com.example.MyActivity@4056f2e0
09-29 13:18:46.161: INFO/TESTING - MYACTIVITY(4726): onCreate - com.example.MyActivity@4056f2e0
09-29 13:18:46.161: INFO/TESTING - MYACTIVITY(4726): onStart - com.example.MyActivity@4056f2e0
09-29 13:18:46.161: INFO/TESTING - MYACTIVITY(4726): onResume - com.example.MyActivity@4056f2e0
09-29 13:18:46.191: INFO/TESTING - MYACTIVITY(4726): onAttachedToWindow - com.example.MyActivity@4056f2e0
09-29 13:19:10.431: DEBUG/SntpClient(1268): request time failed: java.net.UnknownHostException: europe.pool.ntp.org
09-29 13:19:14.251: INFO/TESTING - MYFRAGMENT(4726): Instantiated - MyFragment{405288c0}
09-29 13:19:14.271: INFO/TESTING - MYFRAGMENT(4726): onAttach - MyFragment{405288c0 #0 MyFragment}
09-29 13:19:14.271: INFO/TESTING - MYACTIVITY(4726): onAttachFragment - com.example.MyActivity@4056f2e0
09-29 13:19:14.271: INFO/TESTING - MYFRAGMENT(4726): onCreate - MyFragment{405288c0 #0 MyFragment}
09-29 13:19:14.281: INFO/TESTING - MYFRAGMENT(4726): onCreateView - MyFragment{405288c0 #0 MyFragment}
09-29 13:19:14.281: INFO/TESTING - MYFRAGMENT(4726): onViewCreated - MyFragment{405288c0 #0 MyFragment}
09-29 13:19:14.281: INFO/TESTING - MYFRAGMENT(4726): onActivityCreated - MyFragment{405288c0 #0 MyFragment}
09-29 13:19:14.291: INFO/TESTING - MYFRAGMENT(4726): onStart - MyFragment{405288c0 #0 MyFragment}
09-29 13:19:14.291: INFO/TESTING - MYFRAGMENT(4726): onResume - MyFragment{405288c0 #0 MyFragment}
09-29 13:19:21.921: INFO/ActivityManager(1268): Config changed: { scale=1.0 imsi=404/45 loc=en_US touch=3 keys=1/1/2 nav=1/1 orien=2 layout=34 uiMode=17 seq=103 themeResource=null}
09-29 13:19:21.961: INFO/TESTING - MYACTIVITY(4726): onSaveInstanceState - com.example.MyActivity@4056f2e0
09-29 13:19:21.961: INFO/TESTING - MYFRAGMENT(4726): onPause - MyFragment{405288c0 #0 MyFragment}
09-29 13:19:21.961: INFO/TESTING - MYACTIVITY(4726): onPause - com.example.MyActivity@4056f2e0
09-29 13:19:21.961: INFO/TESTING - MYFRAGMENT(4726): onStop - MyFragment{405288c0 #0 MyFragment}
09-29 13:19:21.961: INFO/TESTING - MYACTIVITY(4726): onStop - com.example.MyActivity@4056f2e0
09-29 13:19:21.961: INFO/TESTING - MYACTIVITY(4726): onRetainCustomNonConfigurationInstance - com.example.MyActivity@4056f2e0
09-29 13:19:21.961: INFO/TESTING - MYFRAGMENT(4726): onDestroyView - MyFragment{405288c0 #0 MyFragment}
09-29 13:19:21.961: INFO/TESTING - MYFRAGMENT(4726): onDetach - MyFragment{405288c0 #0 MyFragment}
09-29 13:19:21.961: INFO/TESTING - MYACTIVITY(4726): onDestroy - com.example.MyActivity@4056f2e0
09-29 13:19:22.111: INFO/TESTING - MYACTIVITY(4726): onDetachedFromWindow - com.example.MyActivity@4056f2e0
09-29 13:19:22.111: INFO/TESTING - MYACTIVITY(4726): Instantiated - com.example.MyActivity@4054a0e8
09-29 13:19:22.111: INFO/TESTING - MYFRAGMENT(4726): onAttach - MyFragment{405288c0 #0 MyFragment}
09-29 13:19:22.111: INFO/TESTING - MYACTIVITY(4726): onAttachFragment - com.example.MyActivity@4054a0e8
09-29 13:19:22.131: INFO/TESTING - MYACTIVITY(4726): onCreate - com.example.MyActivity@4054a0e8
09-29 13:19:22.131: INFO/TESTING - MYFRAGMENT(4726): onCreateView - MyFragment{405288c0 #0 MyFragment}
09-29 13:19:22.131: INFO/TESTING - MYFRAGMENT(4726): onViewCreated - MyFragment{405288c0 #0 MyFragment}
09-29 13:19:22.131: INFO/TESTING - MYFRAGMENT(4726): onActivityCreated - MyFragment{405288c0 #0 MyFragment}
09-29 13:19:22.141: INFO/TESTING - MYFRAGMENT(4726): onStart - MyFragment{405288c0 #0 MyFragment}
09-29 13:19:22.141: INFO/TESTING - MYACTIVITY(4726): onStart - com.example.MyActivity@4054a0e8
09-29 13:19:22.141: INFO/TESTING - MYACTIVITY(4726): onResume - com.example.MyActivity@4054a0e8
09-29 13:19:22.141: INFO/TESTING - MYFRAGMENT(4726): onResume - MyFragment{405288c0 #0 MyFragment}
09-29 13:19:22.171: INFO/TESTING - MYACTIVITY(4726): onAttachedToWindow - com.example.MyActivity@4054a0e8
09-29 13:19:22.181: INFO/TESTING - MYFRAGMENT(4726): onPause - MyFragment{405288c0 #0 MyFragment}
09-29 13:19:22.181: INFO/TESTING - MYFRAGMENT(4726): onStop - MyFragment{405288c0 #0 MyFragment}
09-29 13:19:22.181: INFO/TESTING - MYFRAGMENT(4726): onDestroyView - MyFragment{405288c0 #0 MyFragment}
09-29 13:19:22.181: INFO/TESTING - MYFRAGMENT(4726): onDestroy - MyFragment{405288c0 #0 MyFragment}
09-29 13:19:22.181: INFO/TESTING - MYFRAGMENT(4726): onDetach - MyFragment{405288c0 #0 MyFragment}

Noticed the effect ? Fragment instance (object 405288c0) was retained which is good. But the retained instance is very likely to hold resources and views and objects that belonged to previous orientations, which might lead to memory leaks.

Further care must be taken where you write the code to initiate this fragment: you must always check for pre-existing instance.

Moral of the story: setRetainInstance() is best used for non visual fragments.

Ok, perhaps I was slightly too harsh on the Android documentation, because it does have some useful information, but sadly none of it is linked from setRetainInstance(). From the page about fragments

Note: Each fragment requires a unique identifier that the system can use to restore the fragment if the activity is restarted (and which you can use to capture the fragment to perform transactions, such as remove it). There are three ways to provide an ID for a fragment:

  • Supply the android:id attribute with a unique ID.
  • Supply the android:tag attribute with a unique string.
  • If you provide neither of the previous two, the system uses the ID of the container view.

This strongly implies that if you do setContentView(R.layout.whatever) in Activity.onCreated() and that layout contains a fragment with setRetainInstance(true), then when the activity is recreated it will be searched for again using its id or tag.

Secondly, for UI-less fragments, it states

To add a fragment without a UI, add the fragment from the activity using add(Fragment, String) (supplying a unique string "tag" for the fragment, rather than a view ID). This adds the fragment, but, because it's not associated with a view in the activity layout, it does not receive a call to onCreateView(). So you don't need to implement that method.

And the docs link to a very good example - FragmentRetainInstance.java which I have reproduced below for your convenience. It does exactly what I speculated was the answer in my question (if (...findFragmentByTag() == null) { ...).

Finally, I created my own test activity to see exactly what functions are called. It outputs this, when you start in portrait and rotate to landscape. The code is below.

(This is edited a bit to make it easier to read.)

TestActivity@415a4a30: this()
TestActivity@415a4a30: onCreate()
TestActivity@415a4a30: Existing fragment not found.
TestFragment{41583008}: this() TestFragment{41583008}
TestFragment{41583008}: onAttach(TestActivity@415a4a30)
TestFragment{41583008}: onCreate()
TestFragment{41583008}: onCreateView()
TestFragment{41583008}: onActivityCreated()
TestActivity@415a4a30: onStart()
TestFragment{41583008}: onStart()
TestActivity@415a4a30: onResume()
TestFragment{41583008}: onResume()


<rotate device>


TestFragment{41583008}: onPause()
TestActivity@415a4a30: onPause()
TestFragment{41583008}: onStop()
TestActivity@415a4a30: onStop()
TestFragment{41583008}: onDestroyView()
TestFragment{41583008}: onDetach()
TestActivity@415a4a30: onDestroy()
TestActivity@415a3380: this()
TestFragment{41583008}: onAttach(TestActivity@415a3380)
TestActivity@415a3380: onCreate()
TestActivity@415a3380: Existing fragment found.
TestFragment{41583008}: onCreateView()
TestFragment{41583008}: onActivityCreated()
TestActivity@415a3380: onStart()
TestFragment{41583008}: onStart()
TestActivity@415a3380: onResume()
TestFragment{41583008}: onResume()

Note that the Android documentation is wrong: the UI-less fragment does receive a call to onCreateView() but it is free to return null.

Source code for TestActivity/TestFragment

import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;


import com.concentriclivers.ss.R;


// An activity for understanding Android lifecycle events.
public class TestActivity extends Activity
{
private static final String TAG = TestActivity.class.getSimpleName();


public TestActivity()
{
super();
Log.d(TAG, this + ": this()");
}


protected void finalize() throws Throwable
{
super.finalize();
Log.d(TAG, this + ": finalize()");
}


@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Log.d(TAG, this + ": onCreate()");




TextView tv = new TextView(this);
tv.setText("Hello world");
setContentView(tv);


if (getFragmentManager().findFragmentByTag("test_fragment") == null)
{
Log.d(TAG, this + ": Existing fragment not found.");
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.add(new TestFragment(), "test_fragment").commit();
}
else
{
Log.d(TAG, this + ": Existing fragment found.");
}
}


@Override
public void onStart()
{
super.onStart();
Log.d(TAG, this + ": onStart()");
}


@Override
public void onResume()
{
super.onResume();
Log.d(TAG, this + ": onResume()");
}


@Override
public void onPause()
{
super.onPause();
Log.d(TAG, this + ": onPause()");
}


@Override
public void onStop()
{
super.onStop();
Log.d(TAG, this + ": onStop()");
}


@Override
public void onDestroy()
{
super.onDestroy();
Log.d(TAG, this + ": onDestroy()");
}








public static class TestFragment extends Fragment
{
private static final String TAG = TestFragment.class.getSimpleName();


public TestFragment()
{
super();
Log.d(TAG,  this + ": this() " + this);
}


@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Log.d(TAG, this + ": onCreate()");
setRetainInstance(true);
}


@Override
public void onAttach(final Activity activity)
{
super.onAttach(activity);
Log.d(TAG, this + ": onAttach(" + activity + ")");
}


@Override
public void onActivityCreated(Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
Log.d(TAG, this + ": onActivityCreated()");
}


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
Log.d(TAG, this + ": onCreateView()");
return null;
}


@Override
public void onViewCreated(View view, Bundle savedInstanceState)
{
super.onViewCreated(view, savedInstanceState);
Log.d(TAG, this + ": onViewCreated()");
}


@Override
public void onDestroyView()
{
super.onDestroyView();
Log.d(TAG, this + ": onDestroyView()");
}


@Override
public void onDetach()
{
super.onDetach();
Log.d(TAG, this + ": onDetach()");
}


@Override
public void onStart()
{
super.onStart();
Log.d(TAG, this + ": onStart()");
}


@Override
public void onResume()
{
super.onResume();
Log.d(TAG, this + ": onResume()");
}


@Override
public void onPause()
{
super.onPause();
Log.d(TAG, this + ": onPause()");
}


@Override
public void onStop()
{
super.onStop();
Log.d(TAG, this + ": onStop()");
}


@Override
public void onDestroy()
{
super.onDestroy();
Log.d(TAG, this + ": onDestroy()");
}
}


}

Source code for FragmentRetainInstance.java (as of API 16):

/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/


package com.example.android.apis.app;


import com.example.android.apis.R;


import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;


/**
* This example shows how you can use a Fragment to easily propagate state
* (such as threads) across activity instances when an activity needs to be
* restarted due to, for example, a configuration change.  This is a lot
* easier than using the raw Activity.onRetainNonConfiguratinInstance() API.
*/
public class FragmentRetainInstance extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);


// First time init, create the UI.
if (savedInstanceState == null) {
getFragmentManager().beginTransaction().add(android.R.id.content,
new UiFragment()).commit();
}
}


/**
* This is a fragment showing UI that will be updated from work done
* in the retained fragment.
*/
public static class UiFragment extends Fragment {
RetainedFragment mWorkFragment;


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_retain_instance, container, false);


// Watch for button clicks.
Button button = (Button)v.findViewById(R.id.restart);
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
mWorkFragment.restart();
}
});


return v;
}


@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);


FragmentManager fm = getFragmentManager();


// Check to see if we have retained the worker fragment.
mWorkFragment = (RetainedFragment)fm.findFragmentByTag("work");


// If not retained (or first time running), we need to create it.
if (mWorkFragment == null) {
mWorkFragment = new RetainedFragment();
// Tell it who it is working with.
mWorkFragment.setTargetFragment(this, 0);
fm.beginTransaction().add(mWorkFragment, "work").commit();
}
}


}


/**
* This is the Fragment implementation that will be retained across
* activity instances.  It represents some ongoing work, here a thread
* we have that sits around incrementing a progress indicator.
*/
public static class RetainedFragment extends Fragment {
ProgressBar mProgressBar;
int mPosition;
boolean mReady = false;
boolean mQuiting = false;


/**
* This is the thread that will do our work.  It sits in a loop running
* the progress up until it has reached the top, then stops and waits.
*/
final Thread mThread = new Thread() {
@Override
public void run() {
// We'll figure the real value out later.
int max = 10000;


// This thread runs almost forever.
while (true) {


// Update our shared state with the UI.
synchronized (this) {
// Our thread is stopped if the UI is not ready
// or it has completed its work.
while (!mReady || mPosition >= max) {
if (mQuiting) {
return;
}
try {
wait();
} catch (InterruptedException e) {
}
}


// Now update the progress.  Note it is important that
// we touch the progress bar with the lock held, so it
// doesn't disappear on us.
mPosition++;
max = mProgressBar.getMax();
mProgressBar.setProgress(mPosition);
}


// Normally we would be doing some work, but put a kludge
// here to pretend like we are.
synchronized (this) {
try {
wait(50);
} catch (InterruptedException e) {
}
}
}
}
};


/**
* Fragment initialization.  We way we want to be retained and
* start our thread.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);


// Tell the framework to try to keep this fragment around
// during a configuration change.
setRetainInstance(true);


// Start up the worker thread.
mThread.start();
}


/**
* This is called when the Fragment's Activity is ready to go, after
* its content view has been installed; it is called both after
* the initial fragment creation and after the fragment is re-attached
* to a new activity.
*/
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);


// Retrieve the progress bar from the target's view hierarchy.
mProgressBar = (ProgressBar)getTargetFragment().getView().findViewById(
R.id.progress_horizontal);


// We are ready for our thread to go.
synchronized (mThread) {
mReady = true;
mThread.notify();
}
}


/**
* This is called when the fragment is going away.  It is NOT called
* when the fragment is being propagated between activity instances.
*/
@Override
public void onDestroy() {
// Make the thread go away.
synchronized (mThread) {
mReady = false;
mQuiting = true;
mThread.notify();
}


super.onDestroy();
}


/**
* This is called right before the fragment is detached from its
* current activity instance.
*/
@Override
public void onDetach() {
// This fragment is being detached from its activity.  We need
// to make sure its thread is not going to touch any activity
// state after returning from this function.
synchronized (mThread) {
mProgressBar = null;
mReady = false;
mThread.notify();
}


super.onDetach();
}


/**
* API for our UI to restart the progress thread.
*/
public void restart() {
synchronized (mThread) {
mPosition = 0;
mThread.notify();
}
}
}
}

So you can code in protected void onCreate(Bundle savedInstanceState) at activity class

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);


// add code findViewById...etc
.....
// add your fragment in code, IT WILL BE NOT CHANGE
if(savedInstanceState==null){
FragmentA a_fragment = new FragmentA();
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.your_layout_id, a_fragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}


}

Instead of

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);


// add code findViewById...etc
.....
// add your fragment and not check savedInstanceState IT WILL BE CHANGE
FragmentA a_fragment = new FragmentA();
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.your_layout_id, a_fragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();




}

Remember

public View onCreateView(...) {


// Remember add this line
setRetainInstance(true);


return root;
}