Some menu in the drawer will open Tab + ViewPager content. Each page (fragment) is list that its data is requested from server.
Every time I click that menu, I want the content will show the tab immediately even the data are still requested instead empty screen. I try to add progress bar in the TabFragment so the content will show a loading when preparing the ViewPager and the pagers' data. But, the content still show an empty screen without loading indicator. I found the problem is because the method to request data from server is called from each pager.
Should I move the method for requesting data to TabFragment?
My TabFragment class looks like:
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
.....
content = view.findViewById(R.id.content);
content.setVisibility(View.GONE);
tabLayout = (TabLayout) view.findViewById(R.id.tabs);
viewPager = (ViewPager) view.findViewById(R.id.pager);
progressBar = (ProgressBar) view.findViewById(R.id.progressBar1);
farmerViewPagerAdapter = new FarmerViewPagerAdapter(getChildFragmentManager(), titles);
viewPager.setOffscreenPageLimit(2);
viewPager.setAdapter(farmerViewPagerAdapter);
tabLayout.post(()->{
tabLayout.setupWithViewPager(viewPager);
for (int i = 0; i < titles.length; ++i){
tabLayout.getTabAt(i).setIcon(icons[i]);
}
});
}
And here is fragment for each page (each page requests different data):
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
this.view = view;
emptyView = view.findViewById(R.id.emptyView);
emptyText = (TextView) view.findViewById(R.id.emptyTextView);
recyclerView = (RecyclerView) view.findViewById(R.id.list);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setItemAnimator(new DefaultItemAnimator());
adapter = new FarmerAdapter(data, getContext());
recyclerView.setAdapter(adapter);
recyclerView.addItemDecoration(
new HorizontalDividerItemDecoration.Builder(getContext())
.showLastDivider()
.marginResId(R.dimen.divider_margin_left, R.dimen.divider_margin_right)
.build());
swipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swipeRefreshLayout);
swipeRefreshLayout.setOnRefreshListener(() -> {
if (!Util.isNetworkAvailable(getContext())) {
if (swipeRefreshLayout.isRefreshing()) swipeRefreshLayout.setRefreshing(false);
} else {
currentPage = 1;
loadData(); //method to request data from server
}
}
);
if (user != null) {
getDataFromLocal();
addToAdapter();
loadData();
}
}
(Ed)loadData method :
Observable<Response<List<Data>>> dataApi = request.getServerData(currentPage,
NUMBER_DATA_PER_PAGE,
token);
dataApi.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(responseData -> {
if (swipeRefreshLayout.isRefreshing()) swipeRefreshLayout.setRefreshing(false);
if (responseData.isSuccessful() && responseData.code() == 200) {
currentPage++;
adapter.add(responseData.body());
if (adapter.getItemCount() < 1) {
emptyText.setText("Empty");
emptyView.setVisibility(View.VISIBLE);
}
} else {
try {
JSONObject json = new JSONObject(responseFarmer.errorBody().string());
Toast.makeText(getContext(), json.getString("message"), Toast.LENGTH_LONG).show();
} catch (Exception e) {
e.printStackTrace();
}
}
}, error -> {
if (swipeRefreshLayout != null && swipeRefreshLayout.isRefreshing())
swipeRefreshLayout.setRefreshing(false);
if (error != null && error.getLocalizedMessage() != null)
Toast.makeText(getContext(), error.getLocalizedMessage(), Toast.LENGTH_LONG).show();
});
This method is called from pager fragment.
Your AsyncTask has an empty doInBackground() body. That essentially makes it synchronous. Say you have this AsyncTask:
private class SetAdapterTask extends AsyncTask<Void,Void,Void> {
protected Void doInBackground(Void... params) {
return null;
}
#Override
protected void onPostExecute(Void result) {
doPostExecute();
}
#Override
protected void onPreExecute() {
doPreExecute();
}
}
and you call this in your code like this:
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
.....
new SetAdapterTask().execute();
}
but since your AsyncTask doesn't do anything in background, the postExecute fires off right after the preExecute, making the whole thing equivalent to this:
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
.....
doPreExecute();
doPostExecute();
}
In other words, you make your ProgressBar visible in preExecute and immediatelly after that you make it disappear in postExecute.
The right way to approach this would be moving the ProgressBar visibility settings to the AsyncTask you use to load your data, which is located somewhere in the loadData() I presume. As for the data loading itself, it's hard to say what is wrong without seeing the actual methods which load the data.
Related
I've tried a few different ways of fixing this, but it just doesn't seem to want to work. I have attached the fragment involved. The recyclerview works when I use the search function; However, when I first load the page, I get the error that
E/RecyclerView: No adapter attached; skipping layout
I think it might be an issue with the onCreate vs onCreateView, but I'm not exactly sure what to put where.
NewsFragment.java
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
v = inflater.inflate(R.layout.fragment_news, container, false);
swipeRefreshLayout = v.findViewById(R.id.swipeRefresh);
recyclerView = v.findViewById(R.id.news_recyclerView);
etQuery = v.findViewById(R.id.etQuery);
btnSearch = v.findViewById(R.id.btnSearch);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
final String country = getCountry();
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
retrieveJson("", country, API_KEY);
}
});
retrieveJson("", country, API_KEY);
btnSearch.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (!etQuery.getText().toString().equals("")) {
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
retrieveJson(etQuery.getText().toString(), country, API_KEY);
}
});
retrieveJson(etQuery.getText().toString(), country, API_KEY);
} else {
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
retrieveJson("", country, API_KEY);
}
});
retrieveJson("", country, API_KEY);
}
}
});
return v;
}
public void retrieveJson(String query ,String country, String apiKey){
swipeRefreshLayout.setRefreshing(true);
Call<Headlines> call;
if (!etQuery.getText().toString().equals("")){
call=NewsApiClient.getInstance().getApi().getSpecificData(query,apiKey);
}else{
call=NewsApiClient.getInstance().getApi().getHeadlines(country,apiKey);
}
call.enqueue(new Callback<Headlines>() {
#Override
public void onResponse(Call<Headlines> call, Response<Headlines> response) {
if (response.isSuccessful() && response.body().getArticles() != null ){
swipeRefreshLayout.setRefreshing(false);
articles.clear();
articles = response.body().getArticles();
newsAdapter = new NewsAdapter(getContext(), articles);
recyclerView.setAdapter(newsAdapter);
}
}
#Override
public void onFailure(Call<Headlines> call, Throwable t) {
swipeRefreshLayout.setRefreshing(false);
Toast.makeText(getContext(), t.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
}
});
}
public String getCountry(){
Locale locale = Locale.getDefault();
String country = locale.getCountry();
return country.toLowerCase();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
On the onCreateView you are in fact not setting any adapter. You instead set it when retrieving the json data inside the call.enqueue callbacks, this callbacks are called asynchronously and thus the first time the onCreateView is called no adapter is set to the RecyclerView. To avoid that, set an adapter with empty data empty adapter right at the start of the onCreateView function like this (make newsAdapter a variable of the current fragment)
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
newsAdapter = new NewsAdapter(getContext(), articles);
recyclerView.setAdapter(newsAdapter);
make articles be a variable of the current fragment
and insted of setting and adapter on the callbacks of you retriveJson function simply update the articles array with the data and call the notifydatasetchanged function of the RecycleView like this
public void onResponse(Call<Headlines> call, Response<Headlines> response) {
if (response.isSuccessful() && response.body().getArticles() != null ){
swipeRefreshLayout.setRefreshing(false);
articles.clear();
articles = response.body().getArticles();
newsAdapter.setData(articles);
newsAdapter.notifyDataSetChanged()
}
}
setData is a function that you have to create in your NewsAdapter class.
Hope it helped!
And take a look a this brief guide on how to use RecycleViews
I have list items of custom object and I am using recyclerview to show them and using loaders for internet connection. My problem is when you click an item Google Chrome Custom Tabs intent is triggered (you are redirected to related website) but when you press back button from the website to return the fragment, the recylerview items disappears (interestingly it disappears a few seconds later). I want to return back to the previous screen without refreshing. There is no error. It happened after I added the SwipeRefreshlayout.
I am sharing the related code parts:
public class FragmentTopNews extends Fragment
implements LoaderManager.LoaderCallbacks<List<NewsItem>>, NewsAdapter.OnItemClickListener{
...
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
....
// SwipeRefreshlayout for refreshing the data
mSwipeRefreshLayout = rootView.findViewById(R.id.srl_refresher);
mSwipeRefreshLayout.setColorSchemeResources(R.color.colorPrimary, R.color.colorPrimaryDark);
mSwipeRefreshLayout.canChildScrollUp();
mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
mNewsAdapter.clearData();
mNewsAdapter.notifyDataSetChanged();
mSwipeRefreshLayout.setRefreshing(true);
// Restart the loader
LoaderManager.getInstance(FragmentTopNews.this).restartLoader
(NEWS_LOADER_ID, null, FragmentTopNews.this);
}
});
return rootView;
}
#Override
public void onItemClick(String newsUrl) {
CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
CustomTabsIntent customTabsIntent = builder.build();
customTabsIntent.launchUrl(getActivity(), Uri.parse(newsUrl));
}
#NonNull
#Override
public android.support.v4.content.Loader<List<NewsItem>> onCreateLoader(int i, #Nullable Bundle bundle) {
return new NewsLoaderTopNews(getContext(), NEWS_URLS);
}
#Override
public void onLoadFinished(#NonNull android.support.v4.content.Loader<List<NewsItem>> loader, List<NewsItem> newsItems) {
// Hide loading indicator because the data has been loaded
mLoadingIndicator.setVisibility(View.INVISIBLE);
// Hide refreshing indicator
mSwipeRefreshLayout.setRefreshing(false);
if (newsItems != null && !newsItems.isEmpty()) {
// Clear the adapter of previous data
mNewsAdapter.clearData();
// pass in the news data to the adapter
mNewsAdapter.setNewsData(newsItems);
mNewsAdapter.notifyDataSetChanged();
} else { // tell the user that the news can't be loaded
mRecyclerView.setVisibility(View.INVISIBLE);
mErrorView.setVisibility(View.VISIBLE);
mErrorView.setText(R.string.no_data);
}
}
#Override
public void onLoaderReset(#NonNull android.support.v4.content.Loader<List<NewsItem>> loader) {
Log.i(LOG_TAG, "onLoaderReset is called");
// Clear the adapter of previous data
mNewsAdapter.clearData();
}
}
And this is the onstartLoading override method:
//this is required to trigger the loader to start doing background work
#Override
protected void onStartLoading() {
forceLoad();
}
}
Ok, I solved the issue.
I noticed that when i click back button, the loader is started again (which I don't want) and in my AsynctaskLoader's OnStart method I have "forceload()" command which is executed everytime. So I changed it to this:
#Override
protected void onStartLoading() {
// Added this check to not load data when back button is triggered.
if (news == null || news.isEmpty()) {
forceLoad();
}
}
I have MainActivity which add two tabs namely "A","B" both tabs send some request to server tab "A" is a fragment and Tab "B" is also a fragment ,problem is that when MainActivity start both tab send server request,by default tab1 is visible to user but what I want when tab 2 is visible then tab2 send request to server.pls help me .
mainactivity code:-
m_TabLayout = (TabLayout) findViewById(R.id.tab_layout);// finding Id of tablayout
m_ViewPager = (ViewPager) findViewById(R.id.pager);//finding Id of ViewPager
m_TabLayout.addTab(m_TabLayout.newTab().setText("Deals"));// add deal listin tab
m_TabLayout.addTab(m_TabLayout.newTab().setText("Stories"));
m_TabLayout.setTabGravity(TabLayout.GRAVITY_FILL);// setting Gravity of Tab
CDealMainListingPager m_oDealMainScreenPager = new CDealMainListingPager(getSupportFragmentManager(), m_TabLayout.getTabCount());
m_ViewPager.setAdapter(m_oDealMainScreenPager);// adiing adapter to ViewPager
m_ViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(m_TabLayout));// performing action of page changing
m_TabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
m_ViewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
tab2 code:-
private View m_Main;
private String m_szMobileNumber, m_szEncryptedPassword;
private CAdscenMediaDealStorage m_Item;
private static ListView m_ListView;// declaring Listview variable..
private String TAG = CAdscendMediaDeal.class.getSimpleName();
Integer pubId = 105639;
Integer aswallId = 6453;
private ArrayList<CAdscenMediaDealStorage> s_oDataset;// declaring Arraylist variable
private CADscendDealAdapter m_oAdapter;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
m_Main = inflater.inflate(R.layout.adscend_deal_list, container, false);
getDetail();
init();
return m_Main;
}
private void getDetail() {
CLoginSessionManagement m_oSessionManagement = new CLoginSessionManagement(getActivity());// crating object of Login Session
HashMap<String, String> user = m_oSessionManagement.getLoginDetails();// get String from Login Session
m_szMobileNumber = user.get(CLoginSessionManagement.s_szKEY_MOBILE).trim();// getting password from saved preferences..........
}
public void init() {
m_ListView = (ListView) m_Main.findViewById(R.id.dealList);
s_oDataset = new ArrayList<>();
m_ListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
try {
String url = s_oDataset.get(position).getM_Link();
if (!url.startsWith("http://") && !url.startsWith("https://"))
url = "http://" + url;
Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(myIntent);
} catch (ActivityNotFoundException e) {
Toast.makeText(getActivity(), "No application can handle this request."
+ " Please install a webbrowser", Toast.LENGTH_LONG).show();
e.printStackTrace();
}
}
});
}
#Override
public void onResume() {
super.onResume();
getAdscendDeal();
}
public void getAdscendDeal() {
String url = "http://adsc.com/adwall/api/publisher/" + pubId + "/profile/" + aswallId + "/offers.json?subid1=" + m_szMobileNumber;
JSONObject jsonObject = new JSONObject();
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.GET, url, jsonObject, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
Log.e(TAG, "Server Response::" + response);
try {
JSONArray post = response.optJSONArray("offers");
for (int i = 0; i < post.length(); i++) {
JSONObject obj = post.getJSONObject(i);
m_Item = new CAdscenMediaDealStorage();
m_Item.setM_szHeaderText(obj.getString("name"));
m_Item.setM_szsubHeaderText(obj.getString("description"));
m_Item.setM_szDealValue(obj.getString("currency_count"));
m_Item.setM_szImageView(obj.getString("creative_url"));
m_Item.setM_Link(obj.getString("click_url"));
s_oDataset.add(m_Item);
}
if (!s_oDataset.isEmpty()) {
m_oAdapter = new CADscendDealAdapter(getActivity(), s_oDataset);// create adapter object and add arraylist to adapter
m_ListView.setAdapter(m_oAdapter);//adding adapter to recyclerview
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e(TAG, "Server Error::" + error);
}
});
RequestQueue requestQueue = Volley.newRequestQueue(getActivity());
requestQueue.add(jsonObjectRequest);
}
When you set the viewpager adapter, onCreateView of Tab2 will also be called. Hence it sends the request.
Override the Fragment method : setUserVisibleHint
public class MyFragment extends Fragment {
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
// Fragment is visible
// make request here
}
else {
// Fragment is not visible
}
}
}
View pager retain some pages either side so that on sliding page shows without loading. The offset limit of ViewPager is by default 0.
setOffscreenPageLimit(int limit) :
Set the number of pages that should be retained to either side of the current page in the view hierarchy in an idle state.
But setting viewpager.setOffscreenPageLimit(0) will not work
Warning : Requested offscreen page limit 0 too small; defaulting to 1
try to user this
So user this
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
// load data here
}else{
// fragment is no longer visible
}
}
I'm trying to initialize my views with some details grabbed from my JSON Api.
I have a tablayout with multiple tabs(fragments) and in each fragment, I have placed an AsyncTask at the end of OnViewCreated ...
This works usually, but sometimes it errors out and says it cannot find elements of the view (NullPointerException)
I also notice that sometimes my AsyncTasks lag behind, swiping through the tabs sometimes gets ahead of the AsyncTasks and the tab content doesn't load for a couple of seconds after I've swiped over to them.
Is there a more correct way to do this?
Here's the code I'm working with right now (Same across each fragment of the tabview):
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mSwipeRefreshLayout = (SwipeRefreshLayout) getView().findViewById(R.id.swipe_refresh);
mSwipeRefreshLayout.setOnRefreshListener(this);
// Construct the data source
mSwipeRefreshLayout.setRefreshing(true);
mLoadTask = new LoadTask(this);
mLoadTask.execute((Void) null);
}
And here is an example of a load task:
public class LoadTask extends AsyncTask<Void, Void, Boolean> {
private String mResponse;
private Fragment mFrag;
public LoadTask(Fragment frag){
mFrag = frag;
}
#Override
protected Boolean doInBackground(Void... params) {
// Calls to functions for making an API request here
// Uses HttpUrlConnection
// Response is stored in mResponse
int responseCode = fullResponse.getStatus();
mResponse = fullResponse.getMessage();
Log.w("Response", mResponse);
if(responseCode == 200)
return true;
else
return false;
}
#Override
protected void onPostExecute(final Boolean success) {
mLoadTask = null;
if (success) {
// Initialize adapter and set it to the recyclerview
// Or call initializeView function which sets the view
// elements according the details of the response
} else {
// Error handling code
}
mSwipeRefreshLayout.setRefreshing(false);
}
#Override
protected void onCancelled() {
mLoadTask = null;
}
}
I have the following piece of code which retrieve some weather data from the openweathermap api. The AsyncTask class is used for that purpose.
public class ForecastFragment extends Fragment {
String imageUrl;
ListView listView;
List<WeatherForecastData> WeatherForecastDataList;
String IMG_URL = "http://api.openweathermap.org/img/w/";
Fragment fragment;
public ForecastFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//Inflate xml view and convert it to a View object
View rootView = inflater.inflate(R.layout.fragment_forecast, container, false);
//Initialise ListView.
listView = (ListView) rootView.findViewById(R.id.listView);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
String temp = WeatherForecastDataList.get(position).getWeatherTemperature();
Toast.makeText(getActivity(), temp + "° C"+" Have a nice day", Toast.LENGTH_SHORT).show();
}
});
return rootView;
}
//Now we are ready for further processing
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setRetainInstance(true);
if (savedInstanceState == null) {
if(isOnline()) {
requestData("http://api.openweathermap.org/data/2.5/forecast/daily?lat=50.09&lon=14.42&cnt=9&&units=metric&mode=json");
}else{
Toast.makeText(getActivity(),"There is no internet connection",Toast.LENGTH_SHORT).show();
}
}
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putString("ImageURL", imageUrl);
super.onSaveInstanceState(savedInstanceState);
}
//We create a MyTask object,and execute the async. thread with the specified url which is shown just above.
private void requestData(String uri) {
MyTask task = new MyTask();
task.execute(uri);
}
//AsyncTask that will do the asynchronous threading. It displays the weather's icon,description
//and temperature in the main thread via the OnPostExecute(...) method.
private class MyTask extends AsyncTask<String, String, List<WeatherForecastData>> {
#Override
protected void onPreExecute() {
//Used to initialise Views such as Progress Bars which are not needed for this
//project.
}
#Override
protected List<WeatherForecastData> doInBackground(String... params) {
//Read the url,specify the METHOD GET, and store it in content.
String content = HttpManager.getData(params[0]);
//JSON parsing of the openweather api's response. It is not hard,but I had to use the
//debugger quite a lot to make sure that I deserialise the correct JSON values into Strings.
WeatherForecastDataList = WeatherJSONParser.parseFeed(content);
//Fetching the url image
for (WeatherForecastData d : WeatherForecastDataList) {
try {
imageUrl = IMG_URL + d.getPhoto();
InputStream in = (InputStream) new URL(imageUrl).getContent();
Bitmap bitmap = BitmapFactory.decodeStream(in);
//Is it deprecated?
d.setBitmap(bitmap);
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return WeatherForecastDataList;
}
//WeatherForecastData is the Object that contains all that instances we want to display.
#Override
protected void onPostExecute(List<WeatherForecastData> result) {
if (result == null) {
Toast.makeText(getActivity(), "There is some wrong,and data can not be displayed", Toast.LENGTH_LONG).show();
return;
}
WeatherForecastDataList = result;
//Display the ListView.
WeatherAdapter adapter = new WeatherAdapter(getActivity(), R.layout.weather_row, WeatherForecastDataList);
listView.setAdapter(adapter);
}
}
protected boolean isOnline() {
ConnectivityManager cm = (ConnectivityManager) getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo netInfo = cm.getActiveNetworkInfo();
if (netInfo != null && netInfo.isConnectedOrConnecting()) {
return true;
} else {
return false;
}
}
}
My question is how to make my async task class to work when phone rotates.In other words,I don't want my Fragment to be killed,but storing the weather get I get. I saw other questions here too,but I am confused in this part. Thank you.
Making config changes in the manifest is not the recommended way to save the instance of the fragment.
Instead, you should save the instance of the fragment in container activity's onSaveInstanceState() overriden method.
Below is a small snippet that will help you:
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
getSupportFragmentManager().putFragment(outState,"fragmentInstanceSaved",getSupportFragmentManager().findFragmentById(R.id.fragment_container));
}
Now, in your container activity's onCreate method check if bundle is null or not:
if(savedInstanceState!=null){
Fragment fragment = getSupportFragmentManager().getFragment(savedInstanceState,"fragmentInstanceSaved");
//recreate your preserved fragment here
}else{
//goto ur default activity or fragment....
}