Retain data using Fragment API setRetainInstance()

Last post demonstrate how to "Retaining data during orientation change, with deprecated onRetainNonConfigurationInstance() and getLastNonConfigurationInstance()". It's suggested to use the Fragment API setRetainInstance() instead.

In this example, we will implement two almost identical fragments to demonstrate setRetainInstance(). Fragment1 extends android.support.v4.app.Fragment, without calling setRetainInstance(true). It do some long time job, loading Yahoo Weather as do in last exercise, in background. Fragment2 simple extends Fragment1 and override onActivityCreated(), to call setRetainInstance(true).




Create /res/layout/fragmentlayout.xml to define layout of the fragments.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" >

<TextView
android:id="@+id/status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold" />
<ScrollView
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:id="@+id/weather"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</ScrollView>

</LinearLayout>


Create Fragment1 class extends android.support.v4.app.Fragment.
package com.example.androidyahooweatherdom;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;

public class Fragment1 extends Fragment {

TextView weather, status;
MyWeather weatherResult;

Thread myThread;

class MyWeather{
String description;
String city;
String region;
String country;

String windChill;
String windDirection;
String windSpeed;

String sunrise;
String sunset;

String conditiontext;
String conditiondate;

String numberOfForecast;
String forecast;

public String toString(){

return "\n- " + description + " -\n\n"
+ "city: " + city + "\n"
+ "region: " + region + "\n"
+ "country: " + country + "\n\n"

+ "Wind\n"
+ "chill: " + windChill + "\n"
+ "direction: " + windDirection + "\n"
+ "speed: " + windSpeed + "\n\n"

+ "Sunrise: " + sunrise + "\n"
+ "Sunset: " + sunset + "\n\n"

+ "Condition: " + conditiontext + "\n"
+ conditiondate +"\n"

+ "\n"
+ "number of forecast: " + numberOfForecast + "\n"
+ forecast;

}
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View myFragmentView = inflater.inflate(R.layout.fragmentlayout, container, false);
weather = (TextView)myFragmentView.findViewById(R.id.weather);
status = (TextView)myFragmentView.findViewById(R.id.status);
return myFragmentView;
}

@Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);

if(weatherResult != null){
weather.setText(weatherResult.toString());
status.setText("Reloaded previous weatherResult");
}else{
status.setText("loadYahooWeather()");
loadYahooWeather();
}
}

protected void loadYahooWeather(){

myThread = new Thread(new Runnable(){

@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}

String weatherString = QueryYahooWeather();

try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}

Document weatherDoc = convertStringToDocument(weatherString);

try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}

weatherResult = parseWeather(weatherDoc);
FragmentActivity parentActivity = getActivity();

if(parentActivity != null){
parentActivity.runOnUiThread(new Runnable(){

@Override
public void run() {
weather.setText(weatherResult.toString());
status.setText("Finished");
}});
}

}});
myThread.start();
}

private MyWeather parseWeather(Document srcDoc){

MyWeather myWeather = new MyWeather();

//<description>Yahoo! Weather for New York, NY</description>
myWeather.description = srcDoc.getElementsByTagName("description")
.item(0)
.getTextContent();

//<yweather:location.../>
Node locationNode = srcDoc.getElementsByTagName("yweather:location").item(0);
myWeather.city = locationNode.getAttributes()
.getNamedItem("city")
.getNodeValue()
.toString();
myWeather.region = locationNode.getAttributes()
.getNamedItem("region")
.getNodeValue()
.toString();
myWeather.country = locationNode.getAttributes()
.getNamedItem("country")
.getNodeValue()
.toString();

//<yweather:wind.../>
Node windNode = srcDoc.getElementsByTagName("yweather:wind").item(0);
myWeather.windChill = windNode.getAttributes()
.getNamedItem("chill")
.getNodeValue()
.toString();
myWeather.windDirection = windNode.getAttributes()
.getNamedItem("direction")
.getNodeValue()
.toString();
myWeather.windSpeed = windNode.getAttributes()
.getNamedItem("speed")
.getNodeValue()
.toString();

//<yweather:astronomy.../>
Node astronomyNode = srcDoc.getElementsByTagName("yweather:astronomy").item(0);
myWeather.sunrise = astronomyNode.getAttributes()
.getNamedItem("sunrise")
.getNodeValue()
.toString();
myWeather.sunset = astronomyNode.getAttributes()
.getNamedItem("sunset")
.getNodeValue()
.toString();

//<yweather:condition.../>
Node conditionNode = srcDoc.getElementsByTagName("yweather:condition").item(0);
myWeather.conditiontext = conditionNode.getAttributes()
.getNamedItem("text")
.getNodeValue()
.toString();
myWeather.conditiondate = conditionNode.getAttributes()
.getNamedItem("date")
.getNodeValue()
.toString();

//Added to get elements of <yweather:forecast.../>
NodeList forecastList = srcDoc.getElementsByTagName("yweather:forecast");

myWeather.forecast = "";
if(forecastList.getLength() > 0){
myWeather.numberOfForecast = String.valueOf(forecastList.getLength());
for(int i = 0; i < forecastList.getLength(); i++){
Node forecastNode = forecastList.item(i);
myWeather.forecast +=
forecastNode
.getAttributes()
.getNamedItem("date")
.getNodeValue()
.toString() + " " +
forecastNode
.getAttributes()
.getNamedItem("text")
.getNodeValue()
.toString() +
" High: " + forecastNode
.getAttributes()
.getNamedItem("high")
.getNodeValue()
.toString() +
" Low: " + forecastNode
.getAttributes()
.getNamedItem("low")
.getNodeValue()
.toString() + "\n";
}
}else{
myWeather.numberOfForecast = "No forecast";
}

return myWeather;
}

private Document convertStringToDocument(String src){

Document dest = null;
DocumentBuilderFactory dbFactory =
DocumentBuilderFactory.newInstance();
DocumentBuilder parser;

try {
parser = dbFactory.newDocumentBuilder();
dest = parser.parse(new ByteArrayInputStream(src.getBytes()));
} catch (ParserConfigurationException e1) {
e1.printStackTrace();
Toast.makeText(getActivity().getApplicationContext(),
e1.toString(), Toast.LENGTH_LONG).show();
} catch (SAXException e) {
e.printStackTrace();
Toast.makeText(getActivity().getApplicationContext(),
e.toString(), Toast.LENGTH_LONG).show();
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(getActivity().getApplicationContext(),
e.toString(), Toast.LENGTH_LONG).show();
}

return dest;
}

private String QueryYahooWeather(){

String qResult = "";
String queryString = "http://weather.yahooapis.com/forecastrss?w=2459115";

HttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(queryString);

try {
HttpEntity httpEntity = httpClient.execute(httpGet).getEntity();

if (httpEntity != null){
InputStream inputStream = httpEntity.getContent();
Reader in = new InputStreamReader(inputStream);
BufferedReader bufferedreader = new BufferedReader(in);
StringBuilder stringBuilder = new StringBuilder();

String stringReadLine = null;

while ((stringReadLine = bufferedreader.readLine()) != null) {
stringBuilder.append(stringReadLine + "\n");
}

qResult = stringBuilder.toString();
}
} catch (ClientProtocolException e) {
e.printStackTrace();
Toast.makeText(getActivity().getApplicationContext(),
e.toString(), Toast.LENGTH_LONG).show();
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(getActivity().getApplicationContext(),
e.toString(), Toast.LENGTH_LONG).show();
}

return qResult;
}

}


Create Fragment2 extends Fragment1, it have only one method, override onActivityCreated() to call setRetainInstance(true).
package com.example.androidyahooweatherdom;

import android.os.Bundle;

public class Fragment2 extends Fragment1 {

@Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
setRetainInstance(true);
}

}


Edit /res/layout/activity_main.xml and /res/layout-land/activity_main.xml to define the layout in normal and landscape orientation.

Remark: In order to retain the instance of the fragments, both fragments have to be assigned with ID, even your will not access it.

/res/layout/activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" >

<fragment
class="com.example.androidyahooweatherdom.Fragment1"
android:id="@+id/fragment1"
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="1" />
<fragment
class="com.example.androidyahooweatherdom.Fragment2"
android:id="@+id/fragment2"
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="1" />

</LinearLayout>


/res/layout-land/activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
tools:context=".MainActivity" >

<fragment
class="com.example.androidyahooweatherdom.Fragment1"
android:id="@+id/fragment1"
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="1" />
<fragment
class="com.example.androidyahooweatherdom.Fragment2"
android:id="@+id/fragment2"
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="1" />

</LinearLayout>


MainActivity.java
package com.example.androidyahooweatherdom;

import android.os.Bundle;
import android.support.v4.app.FragmentActivity;

public class MainActivity extends FragmentActivity {

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

}


download filesDownload the files.


Related:
- Share IntentService among Fragments