UNIT 4 Android Developer Fundamentals (V2)
UNIT 4 Android Developer Fundamentals (V2)
Fundamentals
(Version 2) course
Last updated Tue Sep 11 2018
developer.android.com/courses/adfv2
Note: This course uses the terms "codelab" and "practical" interchangeably.
We advise you to use the online version of this course rather than
this static PDF to ensure you are using the latest content.
See developer.android.com/courses/adf-v2.
1
Android Developer Fundamentals Course (V2) – Unit 4
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 2
Android Developer Fundamentals Course (V2) – Unit 4
The data you save to shared preferences is different from the data in the saved activity state, which
you learned about in an earlier chapter:
● Data in a saved activity instance state is retained across activity instances in the same user
session.
● Shared preferences persist across user sessions. Shared preferences persist even if your app
stops and restarts, or if the device reboots.
Use shared preferences only when you need to save a small amount data as simple key/value pairs.
To manage larger amounts of persistent app data, use a storage method such as the Room library
or an SQL database.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 3
Android Developer Fundamentals Course (V2) – Unit 4
What you'll do
● Update an app so it can save, retrieve, and reset shared preferences.
App overview
The HelloSharedPrefs app is another variation of the HelloToast app you created in Lesson 1. It
includes buttons to increment the number, to change the background color, and to reset both the
number and color to their defaults. The app also uses themes and styles to define the buttons.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 4
Android Developer Fundamentals Course (V2) – Unit 4
You start with the starter app and add shared preferences to the main activity code. You also add a
reset button that sets both the count and the background color to the default, and clears the
preferences file.
○ In Android Studio, select Run > Stop 'app' or click the Stop Icon in the toolbar.
○ On the device, press the Recents button (the square button in the lower right
corner). Swipe the HelloSharedPrefs app card to quit the app, or click the X in the
right corner of the card. If you quit the app in this manner, wait a few seconds before
starting it again so the system can clean up.
4. Re-run the app. The app restarts with the default appearance—the count is 0, and the
background color is grey.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 5
Android Developer Fundamentals Course (V2) – Unit 4
You can name your shared preferences file anything you want to, but conventionally it has
the same name as the package name of your app.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 6
Android Developer Fundamentals Course (V2) – Unit 4
2. In the onCreate() method, initialize the shared preferences. Insert this code before the if
statement:
mPreferences = getSharedPreferences(sharedPrefFile, MODE_PRIVATE);
The getSharedPreferences() method (from the activity Context) opens the file at the
given filename (sharedPrefFile) with the mode MODE_PRIVATE.
Note: Older versions of Android had other modes that allowed you to create a world-readable or
world-writable shared preferences file. These modes were deprecated in API 17, and are now
strongly discouraged for security reasons. If you need to share data with other apps, consider
using content URIs provided by a FileProvider.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// …
}
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 7
Android Developer Fundamentals Course (V2) – Unit 4
// ...
}
A shared preferences editor is required to write to the shared preferences object. Add this
line to onPause() after the call to super.onPause().
3. Use the putInt() method to put both the mCount and mColor integers into the shared
preferences with the appropriate keys:
preferencesEditor.putInt(COUNT_KEY, mCount);
preferencesEditor.putInt(COLOR_KEY, mColor);
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 8
Android Developer Fundamentals Course (V2) – Unit 4
The SharedPreferences.Editor class includes multiple "put" methods for different data
types, including putInt() and putString().
The apply() method saves the preferences asynchronously, off of the UI thread. The
shared preferences editor also has a commit() method to synchronously save the
preferences. The commit() method is discouraged as it can block other operations.
1. Delete the entire onSaveInstanceState() method. Because the activity instance state
contains the same data as the shared preferences, you can replace the instance state
altogether.
@Override
protected void onPause(){
super.onPause();
1. Locate the part of the onCreate() method that tests if the savedInstanceState
argument is null and restores the instance state:
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 9
Android Developer Fundamentals Course (V2) – Unit 4
if (savedInstanceState != null) {
mCount = savedInstanceState.getInt(COUNT_KEY);
if (mCount != 0) {
mShowCountTextView.setText(String.format("%s", mCount));
}
mColor = savedInstanceState.getInt(COLOR_KEY);
mShowCountTextView.setBackgroundColor(mColor);
}
When you read data from the preferences you don't need to get a shared preferences
editor. Use any of the "get" methods on a shared preferences object (such as getInt() or
getString() to retrieve preference data.
Note that the getInt() method takes two arguments: one for the key, and the other for
the default value if the key cannot be found. In this case the default value is 0, which is the
same as the initial value of mCount.
4. Update the value of the main TextView with the new count.
mShowCountTextView.setText(String.format("%s", mCount));
5. Get the color from the preferences with the COLOR_KEY key and assign it to the mColor
variable.
mColor = mPreferences.getInt(COLOR_KEY, mColor);
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 10
Android Developer Fundamentals Course (V2) – Unit 4
As before, the second argument to getInt() is the default value to use in case the key
doesn't exist in the shared preferences. In this case you can just reuse the value of mColor,
which was just initialized to the default background further up in the method.
7. Run the app. Click the Count button and change the background color to update the
instance state and the preferences.
8. Rotate the device or emulator to verify that the count and color are saved across
configuration changes.
9. Force-quit the app using one of these methods:
○ In Android Studio, select Run > Stop 'app.'
○ On the device, press the Recents button (the square button in the lower right
corner). Swipe the HelloSharedPrefs app card to quit the app, or click the X in the
right corner of the card.
10. Re-run the app. The app restarts and loads the preferences, maintaining the state.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Restore preferences
mCount = mPreferences.getInt(COUNT_KEY, 0);
mShowCountTextView.setText(String.format("%s", mCount));
mColor = mPreferences.getInt(COLOR_KEY, mColor);
mShowCountTextView.setBackgroundColor(mColor);
}
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 11
Android Developer Fundamentals Course (V2) – Unit 4
1. In the reset() onClick method, after the color and count are reset, get an editor for the
SharedPreferences object:
// Reset color
mColor = ContextCompat.getColor(this, R.color.default_background);
mShowCountTextView.setBackgroundColor(mColor);
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 12
Android Developer Fundamentals Course (V2) – Unit 4
// Clear preferences
SharedPreferences.Editor preferencesEditor = mPreferences.edit();
preferencesEditor.clear();
preferencesEditor.apply();
Solution code
Android Studio project: HelloSharedPrefs
Coding challenge
Note: All coding challenges are optional and are not prerequisites for later lessons.
Challenge: Modify the HelloSharedPrefs app so that instead of automatically saving the state to the
preferences file, add a second activity to change, reset, and save those preferences. Add a button to
the app named Settings to launch that activity. Include toggle buttons and spinners to modify the
preferences, and Save and Reset buttons for saving and clearing the preferences.
Summary
● The SharedPreferences class allows an app to store small amounts of primitive data as
key-value pairs.
● Shared preferences persist across different user sessions of the same app.
● To write to the shared preferences, get a SharedPreferences.Editor object.
● Use the various "put" methods in a SharedPreferences.Editor object, such as putInt()
or putString(), to put data into the shared preferences with a key and a value.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 13
Android Developer Fundamentals Course (V2) – Unit 4
Related concept
The related concept documentation is in 9.0: Data storage and 9.1: Shared preferences.
Learn more
Android developer documentation:
● SharedPreferences.Editor
Stack Overflow:
Homework
Build and run an app
Open the ScoreKeeper app that you created in the Android fundamentals 5.1: Drawables, styles, and
themes lesson.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 14
Android Developer Fundamentals Course (V2) – Unit 4
1. Replace the saved instance state with shared preferences for each of the scores.
2. To test the app, rotate the device to ensure that configuration changes read the saved
preferences and update the UI.
3. Stop the app and restart it to ensure that the preferences are saved.
4. Add a Reset button that resets the score values to 0 and clears the shared preferences.
Question 1
In which lifecycle method do you save the app state to shared preferences?
Question 2
Question 3
Can you think of a case where it makes sense to have both shared preferences and instance state?
● The app retains the current scores after the app is stopped and restarted.
● The app saves the current scores to the shared preferences in the onPause() method.
Make sure that the implementation of the on-click handler method for the Reset button does the
following things:
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 15
Android Developer Fundamentals Course (V2) – Unit 4
Users expect to navigate to app settings by tapping Settings in side navigation, such as a navigation
drawer as shown on the left side of the figure below, or in the options menu in the app bar, shown
on the right side of the figure below.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 16
Android Developer Fundamentals Course (V2) – Unit 4
In this practical you add a settings activity to an app. Users will be able to navigate to the app
settings by tapping Settings, which will be located in the options menu in the app bar.
● Create an Android Studio project from a template and generate the main layout.
● Run apps on the emulator or a connected device.
● Create and edit UI elements using the layout editor and XML code.
● Extract string resources and edit string values.
● Access UI elements from your code using findViewById().
● Handle a Button click.
● Display a Toast message.
● Add an Activity to an app.
● Create an options menu in the app bar.
● Add and edit the menu items in the options menu.
● Use styles and themes in a project.
● Use SharedPreferences.
What you'll do
● Create an app that includes Settings in the options menu.
● Add a Settings option toggle switch.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 17
Android Developer Fundamentals Course (V2) – Unit 4
● Add code to set the default value for the setting, and access the setting value after it has
changed.
● Use and customize the Android Studio Settings Activity template.
App overview
Android Studio provides a shortcut for setting up an options menu with Settings. If you start an
Android Studio project for a phone or tablet using the Basic Activity template, the new app includes
Settings as shown below:
The template also includes a floating action button in the lower right corner of the screen with an
envelope icon. You can ignore this button for this practical, as you won't be using it.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 18
Android Developer Fundamentals Course (V2) – Unit 4
You'll start by creating an app named AppWithSettings using the Basic Activity template, and you'll
add a settings Activity that provides one toggle switch setting that the user can turn on or off:
You will add code to read the setting and perform an action based on its value. For the sake of
simplicity, the action will be to display a Toast message with the value of the setting.
In the second task, you will add the standard Settings Activity template provided by Android Studio
to the DroidCafeOptionsUp app you created in a previous lesson.
The Settings Activity template is pre-populated with settings you can customize for an app, and
provides a different layout for phones and tablets:
● Phones: A main Settings screen with a header link for each group of settings, such as
General for general settings, as shown below.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 19
Android Developer Fundamentals Course (V2) – Unit 4
● Tablets: A master/detail screen layout with a header link for each group on the left (master)
side, and the group of settings on the right (detail) side, as shown in the figure below.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 20
Android Developer Fundamentals Course (V2) – Unit 4
To customize the template, you'll change the headers, setting titles, setting descriptions, and values
for the settings.
The DroidCafeOptionsUp app was created in a previous lesson from the Basic Activity template,
which provides an options menu in the app bar for placing the Settings option. You will customize
the supplied Settings Activity template by changing a single setting's title, description, values, and
default values. You will add code to read the setting's value after the user changes it, and display
that value.
● Create a new project based on the Basic Activity template, which provides an options menu.
● Add a toggle switch (SwitchPreference) with attributes in a preference XML file.
● Add an activity for settings and a fragment for a specific setting. To maintain compatibility
with AppCompatActivity, you use PreferenceFragmentCompat rather than
PreferenceFragment. You also add the android.support.v7.preference library.
● Connect the Settings item in the options menu to the settings activity.
1.1 Create the project and add the xml directory and resource file
1. In Android Studio, create a new project with the following parameters:
Attribute Value
Company Name
android.example.com (or your own domain)
Project location
Path to your directory of projects
Template
Basic Activity
Activity Name
MainActivity
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 21
Android Developer Fundamentals Course (V2) – Unit 4
Layout Name
activity_main
Title
MainActivity
2. Run the app, and tap the overflow icon in the app bar to see the options menu, as shown in
the figure below. The only item in the options menu is Settings.
3. You need to create a new resource directory to hold the XML file containing the settings.
Select the res directory in the Project > Android pane, and choose File > New > Android
Resource Directory. The New Resource Directory dialog appears.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 22
Android Developer Fundamentals Course (V2) – Unit 4
4. In the Resource type drop-down menu, choose xml. The Directory name automatically
changes to xml. Click OK.
5. The xml folder appears in the Project > Android pane inside the res folder. Select xml and
choose File > New > XML resource file (or right-click xml and choose New > XML resource
file).
6. Enter the name of the XML file, preferences, in the File name field, and click OK. The
preferences.xml file appears inside the xml folder, and the layout editor appears, as shown
in the figure below.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 23
Android Developer Fundamentals Course (V2) – Unit 4
1.2 Add the XML preference and attributes for the setting
1. Drag a SwitchPreference from the Palette pane on the left side to the top of the layout, as
shown in the figure below.
2. Change the values in the Attributes pane on the right side of the layout editor as follows,
and as shown in the figure below:
○ defaultValue: true
○ key: example_switch
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 24
Android Developer Fundamentals Course (V2) – Unit 4
3. Click the Text tab at the bottom of the layout editor to see the XML code:
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
<SwitchPreference
android:defaultValue="true"
android:key="example_switch"
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 25
Android Developer Fundamentals Course (V2) – Unit 4
4. Extract the string resources for the android:title and android:summary attribute values to
@string/switch_title and @string/switch_summary.
● android:defaultValue: The default value of the setting when the app starts for the first
time.
● android:title: The title of the setting. For a SwitchPreference, the title appears to the left
of the toggle switch.
● android:key: The key to use for storing the setting value. Each setting has a corresponding
key-value pair that the system uses to save the setting in a default SharedPreferences file
for your app's settings.
● android:summary: The text summary appears underneath the setting.
1. In the Project > Android pane, open the build.gradle (Module: app) file in the Gradle
Scripts folder, and add the following to the dependencies section:
implementation 'com.android.support:preferencev7:26.1.0'
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 26
Android Developer Fundamentals Course (V2) – Unit 4
2. In the preferences.xml file in the xml folder, change <SwitchPreference in the code to
<android.support.v7.preference.SwitchPreferenceCompat:
<android.support.v7.preference.SwitchPreferenceCompat
android:defaultValue="true"
android:key="example_switch"
android:summary="@string/switch_summary"
android:title="@string/switch_title" />
The SwitchPreferenceCompat line above may show a yellow light bulb icon with a warning,
but you can ignore it for now.
3. Open the styles.xml file in the values folder, and add the following preferenceTheme item
to the AppTheme declaration:
<item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
1. Select app at the top of the Project > Android pane, and choose New > Activity > Empty
Activity.
2. Name the Activity SettingsActivity. Uncheck the Generate Layout File option (you don't
need one), and leave unchecked the Launcher Activity option.
3. Leave the Backwards Compatibility (AppCompat) option checked. The Package name
should already be set to com.example.android.projectname.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 27
Android Developer Fundamentals Course (V2) – Unit 4
4. Click Finish.
In this step you will add a blank Fragment for a group of similar settings (without a layout, factory
methods, or interface callbacks) to the app, and extend PreferenceFragmentCompat.
1. Select app again, and choose New > Fragment > Fragment (Blank).
2. Name the fragment SettingsFragment. Uncheck the Create layout XML? option (you don't
need one).
3. Uncheck the options to include fragment factory methods and interface callbacks.
4. The Target Source Set should be set to main.
5. Click Finish. The result is the following class definition in SettingsFragment:
public SettingsFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
TextView textView = new TextView(getActivity());
textView.setText(R.string.hello_blank_fragment);
return textView;
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 28
Android Developer Fundamentals Course (V2) – Unit 4
}
}
As you change the class definition so it matches the definition shown above, a red bulb
appears in the left margin. Click the red bulb and choose Implement methods, and then
choose onCreatePreferences. Android Studio creates the following
onCreatePreferences() stub:
@Override
public void onCreatePreferences(Bundle
savedInstanceState, String rootKey) {
}
In order to extend the Fragment, Android Studio adds the following import statement:
import android.support.v7.preference.PreferenceFragmentCompat;
The reason why you are essentially replacing onCreateView() with onCreatePreferences()
is because you will be adding this SettingsFragment to the existing SettingsActivity to
display preferences, rather than showing a separate Fragment screen. Adding it to the
existing Activity makes it easy to add or remove a Fragment while the Activity is running.
The preference Fragment is rooted at the PreferenceScreen using rootKey.
You can safely delete the empty constructor from SettingsFragment as well, because the
Fragment is not displayed by itself:
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 29
Android Developer Fundamentals Course (V2) – Unit 4
public SettingsFragment() {
// Required empty public constructor
}
8. You need to associate with this Fragment the preferences.xml settings resource you
created in a previous step. Add to the newly created onCreatePreferences() stub a call to
setPreferencesFromResource() passing the id of the XML file (R.xml.preferences) and
the rootKey to identify the preference root in PreferenceScreen:
setPreferencesFromResource(R.xml.preferences, rootKey);
@Override
public void onCreatePreferences(Bundle
savedInstanceState, String rootKey) {
setPreferencesFromResource(R.xml.preferences, rootKey);
}
1. Open SettingsActivity.
2. Add the following code to the end of the onCreate() method so that the Fragment is
displayed as the main content:
getSupportFragmentManager().beginTransaction()
.replace(android.R.id.content, new SettingsFragment())
.commit();
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 30
Android Developer Fundamentals Course (V2) – Unit 4
The code above uses the typical pattern for adding a fragment to an activity so that the fragment
appears as the main content of the activity:
● Use getFragmentManager() if the class extends Activity and the Fragment extends
PreferenceFragment.
● Use getSupportFragmentManager() if the class extends AppCompatActivity and the
Fragment extends PreferenceFragmentCompat.
The entire onCreate() method in SettingsActivity should now look like the following:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportFragmentManager().beginTransaction()
.replace(android.R.id.content, new SettingsFragment())
.commit();
}
1. Open MainActivity and find the if block in the onOptionsItemSelected() method, which
handles the tap on Settings in the options menu:
if (id == R.id.action_settings) {
return true;
}
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 31
Android Developer Fundamentals Course (V2) – Unit 4
if (id == R.id.action_settings) {
Intent intent = new Intent(this, SettingsActivity.class);
startActivity(intent);
return true;
}
3. To add the app bar Up navigation button to SettingsActivity, you need to edit its
declaration in the AndroidManifest.xml file to define the SettingsActivity parent as
MainActivity. Open AndroidManifest.xml and find the SettingsActivity declaration:
<activity android:name=".SettingsActivity"></activity>
<activity android:name=".SettingsActivity"
android:label="Settings"
android:parentActivityName=".MainActivity">
<metadata
android:name="android.support.PARENT_ACTIVITY"
android:value=".MainActivity"/>
</activity>
4. Run the app. Tap the overflow icon for the options menu, as shown on the left side of the
figure below. Tap Settings to see the settings activity, as shown in the center of the figure
below. Tap the Up button in the app bar of the settings activity, shown on the right side of
the figure below, to return to the main activity.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 32
Android Developer Fundamentals Course (V2) – Unit 4
1. Open MainActivity.
2. Add the following to the end of the onCreate() method after the FloatingActionButton
code:
android.support.v7.preference.PreferenceManager
.setDefaultValues(this, R.xml.preferences, false);
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 33
Android Developer Fundamentals Course (V2) – Unit 4
The code above ensures that the settings are properly initialized with their default values. The
PreferenceManager.setDefaultValues() method takes three arguments:
Each setting is identified using a key-value pair. The Android system uses this key-value pair when
saving or retrieving settings from a SharedPreferences file for your app. When the user changes a
setting, the system updates the corresponding value in the SharedPreferences file. To use the value
of the setting, the app can use the key to get the setting from the SharedPreferences file using
PreferenceManager.getDefaultSharedPreferences().
1. Open SettingsActivity and create a static String variable to hold the key for the value:
2. Open MainActivity and add the following at end of the onCreate() method:
SharedPreferences sharedPref =
android.support.v7.preference.PreferenceManager
.getDefaultSharedPreferences(this);
Boolean switchPref = sharedPref.getBoolean
(SettingsActivity.KEY_PREF_EXAMPLE_SWITCH, false);
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 34
Android Developer Fundamentals Course (V2) – Unit 4
Toast.makeText(this, switchPref.toString(),
Toast.LENGTH_SHORT).show();
3. Run the app and Tap Settings to see the Settings Activity.
4. Tap the setting to change the toggle from on to off, as shown on the left side of the figure
below.
5. Tap the Up button in the Settings Activity to return to MainActivity. The Toast message
should appear in MainActivity with the value of the setting, as shown on the right side of
the figure below.
6. Repeat these steps to see the Toast message change as you change the setting.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 35
Android Developer Fundamentals Course (V2) – Unit 4
● android.support.v7.preference.PreferenceManager.getDefaultSharedPreferences(th
is) to get the setting as a SharedPreferences object (sharedPref).
● getBoolean() to get the Boolean value of the setting that uses the key
(KEY_PREF_EXAMPLE_SWITCH defined in SettingsActivity) and assign it to switchPref. If
there is no value for the key, the getBoolean() method sets the setting value (switchPref)
to false. For other values such as strings, integers, or floating point numbers, you can use
the getString(), getInt(), or getFloat() methods respectively.
Whenever the MainActivity starts or restarts, the onCreate() method should read the setting
values in order to use them in the app. The Toast.makeText() method would be replaced with a
method that initializes the settings.
In the previous task you learned how to use an empty settings Activity and a blank Fragment in
order to add a setting to an app. Task 2 will now show you how to use the Settings Activity template
supplied with Android Studio to:
● Display a main Settings screen with a header link for each group of settings, such as General
for general settings, as shown in the figure below.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 36
Android Developer Fundamentals Course (V2) – Unit 4
● Display a master/detail screen layout with a header link for each group on the left (master)
side, and the group of settings on the right (detail) side, as shown in the figure below.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 37
Android Developer Fundamentals Course (V2) – Unit 4
In a previous practical you created an app called DroidCafeOptionsUp using the Basic Activity
template, which provides an options menu in the app bar as shown below.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 38
Android Developer Fundamentals Course (V2) – Unit 4
1. App bar
2. Options menu action icons
3. Overflow button
4. Options overflow menu
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 39
Android Developer Fundamentals Course (V2) – Unit 4
You choose MainActivity as the parent so that the Up app bar button in the Settings
Activity returns the user to the MainActivity. Choosing the parent Activity automatically
updates the AndroidManifest.xml file to support Up button navigation.
6. Click Finish.
7. In the Project > Android pane, expand the app > res > xml folder to see the XML files
created by the Settings Activity template.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 40
Android Developer Fundamentals Course (V2) – Unit 4
You can open and then add to or customize the XML files for the settings you want:
The above XML layouts use various subclasses of the Preference class rather than View, and direct
subclasses provide containers for layouts involving multiple settings. For example,
PreferenceScreen represents a top-level Preference that is the root of a Preference hierarchy. The
above files use PreferenceScreen at the top of each screen of settings. Other Preference
subclasses for settings provide the appropriate UI for users to change the setting. For example:
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 41
Android Developer Fundamentals Course (V2) – Unit 4
Tip: You can edit the XML files to change the default settings and customize the setting text.
All strings used in the settings activity, such as setting titles, string arrays for lists, and setting
descriptions, are defined as string resources in a strings.xml file. They are marked by comments
such as the following:
<!‑‑ Strings related to Settings ‑‑>
<!‑‑ Example General settings ‑‑>
2.2 Add the Settings menu item and connect it to the activity
As you learned in another practical, you can edit the menu_main.xml file for the options menu to add
or remove menu items.
1. Expand the res folder in the Project > Android pane, and open menu_main.xml file. Click
the Text tab to show the XML code.
2. Add another menu item called Settings with the new resource id action_settings:
<item
android:id="@+id/action_settings"
android:orderInCategory="50"
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 42
Android Developer Fundamentals Course (V2) – Unit 4
android:title="Settings"
app:showAsAction="never" />
You specify "never" for the app:showAsAction attribute so that Settings appears only in
the overflow options menu and not in the app bar itself, because it should not be used often.
You specify "50" for the android:orderInCategory attribute so that Settings appears
below Favorites (set to "30") but above Contact (set to "100").
3. Extract the string resource for "Settings" in the android:title attribute to the resource
name settings.
4. Open MainActivity, and find the switch case block in the onOptionsItemSelected()
method which handles the tap on items in the options menu. Shown below is a snippet of
that method showing the first case (for action_order):
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_order:
Intent intent = new Intent(MainActivity.this,
OrderActivity.class);
intent.putExtra(EXTRA_MESSAGE, mOrderMessage);
startActivity(intent);
return true;
case R.id.action_status:
// Code for action_status and other cases...
}
return super.onOptionsItemSelected(item);
}
5. Note in the above code that the first case uses an Intent to launch OrderActivity. Add a
new case for action_settings to the switch case block with similar Intent code to launch
SettingsActivity (but without the intent.putExtra):
case R.id.action_settings:
Intent settingsIntent = new Intent(this,
SettingsActivity.class);
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 43
Android Developer Fundamentals Course (V2) – Unit 4
startActivity(settingsIntent);
return true;
5. Run the app using a phone or emulator so that you can see how the Settings Activity
template handles the phone screen size.
6. Tap the overflow icon for the options menu, and tap Settings to see the Settings Activity,
as shown on the left side of the figure below.
7. Tap each setting header (General, Notifications, and Data & sync), as shown in the center
of the figure below, to see the group of settings on each child screen of the Settings screen,
shown on the right side of the figure below.
8. Tap the Up button in the Settings Activity to return to MainActivity.
You use the Settings Activity template code as-is. It not only provides layouts for phone-sized and
tablet-sized screens, but also provides the function of listening to a settings change, and changing
the summary to reflect the settings change. For example, if you change the "Add friends to
messages" setting (the choices are Always, When possible, or Never), the choice you make appears
in the summary underneath the setting:
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 44
Android Developer Fundamentals Course (V2) – Unit 4
In general, you need not change the Settings Activity template code in order to customize the
Activity for the settings you want in your app. You can customize the settings titles, summaries,
possible values, and default values without changing the template code, and even add more settings
to the groups that are provided.
In this step you will change the "Data & sync" settings.
1. Expand the res > values folder and open the strings.xml file. Scroll the contents to the <!‑‑
Example settings for Data & Sync ‑‑> comment:
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 45
Android Developer Fundamentals Course (V2) – Unit 4
<stringarray name="list_preference_entries">
<item>Entry 1</item>
<item>Entry 2</item>
<item>Entry 3</item>
</stringarray>
<stringarray name="list_preference_entry_values">
<item>1</item>
<item>2</item>
<item>3</item>
</stringarray>
2. Edit the pref_header_data_sync string resource, which is set to Data & sync (the
& is HTML code for an ampersand). Change the value to Account (without quotation
marks).
3. You should now refactor the resource name (the app will still work without refactoring the
name, but refactoring makes the code easier to understand). Right-click (or Control-click)
the pref_header_data_sync resource name choose Refactor > Rename. Change the name
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 46
Android Developer Fundamentals Course (V2) – Unit 4
to pref_header_account, click the option to search in comments and strings, and click
Refactor.
4. You should also refactor the XML file name (the app will still work without refactoring the
name, but refactoring makes the code easier to understand). Right-click (or Control-click)
the pref_data_sync resource name in the Project > Android pane, and choose Refactor >
Rename. Change the name to pref_account, click the option to search in comments and
strings, and click Refactor.
8. Change each value in the pref_market_titles string array (15 minutes, 30 minutes, 1
hour, etc.) to be the titles of markets, such as United States, Canada, etc., rather than
frequencies:
<stringarray name="pref_market_titles">
<item>United States</item>
<item>Canada</item>
<item>United Kingdom</item>
<item>India</item>
<item>Japan</item>
<item>Other</item>
</stringarray>
10. Change each value in the pref_market_values string array (15, 30, 60, etc.) to be values for
the markets—abbreviations that correspond to the countries above, such as US, CA, etc.:
<stringarray name="pref_market_values">
<item>US</item>
<item>CA</item>
<item>UK</item>
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 47
Android Developer Fundamentals Course (V2) – Unit 4
<item>IN</item>
<item>JA0</item>
<item>1</item>
</stringarray>
11. Scroll down to the pref_title_system_sync_settings string resource, and edit the
resource (which is set to System sync settings) to Account settings.
12. Refactor > Rename the string array resource name pref_title_system_sync_settings to
pref_title_account_settings.
13. Open the pref_account.xml file. The ListPreference in this layout defines the setting you
just changed. Note that the string resources for the android:entries,
android:entryValues and android:title attributes are now changed to the values you
supplied in the previous steps:
<ListPreference
android:defaultValue="180"
android:entries="@array/pref_market_titles"
android:entryValues="@array/pref_market_values"
android:key="sync_frequency"
android:negativeButtonText="@null"
android:positiveButtonText="@null"
android:title="@string/pref_title_account" />
android:defaultValue="US"
Because the key for this setting preference ("sync_frequency") is hard-coded elsewhere in the Java
code, don't change the android:key attribute. Instead, keep using "sync_frequency" as the key for
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 48
Android Developer Fundamentals Course (V2) – Unit 4
this setting in this example. If you were thoroughly customizing the settings for a real-world app, you
would take the time to change the hard-coded keys throughout the code.
Note: Why not use a string resource for the key? Because string resources can be localized for
different languages using multiple-language XML files, and the key string might be inadvertently
translated along with the other strings, which would cause the app to crash.
2.4 Add code to set the default values for the settings
To add code to set the default values for the settings, follow these steps:
PreferenceManager.setDefaultValues(this,
R.xml.pref_general, false);
PreferenceManager.setDefaultValues(this,
R.xml.pref_notification, false);
PreferenceManager.setDefaultValues(this,
R.xml.pref_account, false);
The default values are already specified in the XML file with the android:defaultValue attribute,
but the above statements ensure that the SharedPreferences file is properly initialized with the
default values. The setDefaultValues() method takes three arguments:
● The resource ID for the settings layout XML file which includes the default values set by the
android:defaultValue attribute.
● A boolean indicating whether the default values should be set more than once. When false,
the system sets the default values only when this method is called for the first time. As long
as you set this third argument to false, you can safely call this method every time your
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 49
Android Developer Fundamentals Course (V2) – Unit 4
Activity starts without overriding the user's saved settings values by resetting them to the
default values. However, if you set it to true, the method will override any previous values
with the defaults.
2. Run the app. When the app's main screen first appears, you see a Toast message at the
bottom of the screen. The first time you run the app, you should see "-1" displayed in the
Toast because you haven't changed the setting yet.
3. Tap Settings in the options menu, and tap Account in the Settings screen. Tap Market, and
choose Canada as shown below:
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 50
Android Developer Fundamentals Course (V2) – Unit 4
4. Tap the Up button in the app bar to return to the Settings screen, and tap it again to return
to the main screen.
5. Run the app again from Android Studio. You should see a Toast message with "CA" (for
Canada), and the Market setting is now set to Canada.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 51
Android Developer Fundamentals Course (V2) – Unit 4
You have successfully integrated the Settings Activity with the app.
6. Now run the app on a tablet or tablet emulator. Because a tablet has a physically larger
screen, the Android runtime takes advantage of the extra space. On a tablet, the settings and
details are displayed on the same screen making it easier for users to manage their settings.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 52
Android Developer Fundamentals Course (V2) – Unit 4
Coding challenge
Note: All coding challenges are optional and are not prerequisites for later lessons.
Challenge: The DroidCafeWithSettings app displays the settings on a tablet-sized screen properly,
but the Up button in the app bar doesn't return the user to the MainActivity as it does on a
phone-sized screen. This is due to the three onOptionsItemSelected() methods—one for each
Fragment—in SettingsActivity. It uses the following to restart the SettingsActivity when the
user taps the Up button:
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 53
Android Developer Fundamentals Course (V2) – Unit 4
The above is the appropriate action on phone screens in which Settings headers (General,
Notifications, and Account) appear in a separate screen (SettingsActivity). After changing a
setting, you want the user's tap on the Up button to take the user back to the Settings headers in
SettingsActivity. A further tap on Up takes the user back to MainActivity.
However, on a tablet, the headers are always visible in the left pane (while the settings are in the
right pane). As a result, tapping the Up button doesn't take the user to MainActivity.
Find a way to make the Up button work properly in SettingsActivity on tablet-sized screens.
Hint: While there are several ways to fix this problem, consider the following steps:
1. Add another dimens.xml file to specifically accommodate screen sizes larger than 600dp.
When the app runs on a specific device, the appropriate dimens.xml file is chosen based on
the qualifiers for the dimens.xml files. You can add another dimens.xml file with the
Smallest Screen Width qualifier set to 600 dp (sw600dp), as you learned in the practical on
supporting landscape and screen sizes, to specify any device with a large screen, such as a
tablet.
2. Add the following bool resource between the <resources> and </resources> tags in the
dimens.xml (sw600dp) file, which is automatically chosen for tablets:
<bool name="isTablet">true</bool>
3. Add the following bool resource to the standard dimens.xml file, which is chosen when the
app runs on any device that is not large:
<bool name="isTablet">false</bool>
4. In SettingsActivity, you can add to all three onOptionsItemSelected() methods (one for
each Fragment) an if else block that checks to see if isTablet is true. If it is, your code can
redirect the Up button action to MainActivity.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 54
Android Developer Fundamentals Course (V2) – Unit 4
Summary
Users expect to navigate to app settings by tapping Settings in side navigation, such as a navigation
drawer, or in the options menu in the app bar.
To provide user settings for your app, provide an Activity for settings:
● To connect the Settings menu item to the settings activity, use an Intent.
1. Create a new resource directory (File > New > Android Resource Directory).
2. In the Resource type drop-down menu, select xml. The xml folder appears inside the res
folder.
3. Click on xml and select File > New > XML resource file.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 55
Android Developer Fundamentals Course (V2) – Unit 4
4. Enter preferences as the name of the XML file. The preferences.xml file appears inside the
xml folder.
Add UI controls such as toggle switches, with attributes in a preferences XML file:
● android:defaultValue is the value of the setting when the app starts for the first time.
● android:title is the user-visible setting title.
● android:key is the key used for storing the setting value.
● android:summary is the user-visible text that appears under the setting.
● When the app starts, the MainActivity onCreate() method can read the setting values that
have changed, and use the changed values rather than the default values.
● Each setting is identified using a key-value pair. The Android system uses this key-value pair
when saving or retrieving settings from a SharedPreferences file for your app. When the
user changes a setting, the system updates the corresponding value in the
SharedPreferences file.
● To use the value of the setting, your app can use the key to get the setting from the
SharedPreferences file.
● Your app reads settings values from SharedPreferences using
PreferenceManager.getDefaultSharedPreferences(), and obtains each setting value
using .getString, .getBoolean, etc.
Related concepts
The related concept documentation is in 9.2: App settings.
Learn more
Android Studio documentation: Android Studio User Guide
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 56
Android Developer Fundamentals Course (V2) – Unit 4
● Settings (overview)
● Preference
● PreferenceFragment
● PreferenceFragmentCompat
● Fragment
● SharedPreferences
● Save key-value data
● Support different screen sizes
Stack Overflow:
Homework
Build and run an app
Open the DroidCafeWithSettings app project.
1. Add a ListPreference (a dialog with radio buttons) to the general settings. Put the dialog in
the General settings screen, below the "Add friends to order messages" ListPreference.
2. Edit the string arrays used for the ListPreference to include the title "Choose a delivery
method." Use the same delivery choices that are used in the radio buttons in the
OrderActivity.
3. Make the user's chosen delivery method appear in the same Toast message as the chosen
Market setting.
4. Extra credit: Show the selected delivery method as the setting summary text that appears
underneath the ListPreference title. Enable this text to change with each update.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 57
Android Developer Fundamentals Course (V2) – Unit 4
Question 1
In which file of the DroidCafeWithSettings project do you define the array of entries and the array of
values for the ListPreference? Choose one:
● pref_general.xml
● strings.xml
● menu_main.xml
● activity_main.xml
● content_main.xml
Question 2
In which file of the DroidCafeWithSettings project do you use the array of entries and the array of
values in setting up the ListPreference, and also set the ListPreference key and default value?
Choose one:
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 58
Android Developer Fundamentals Course (V2) – Unit 4
● pref_general.xml
● strings.xml
● menu_main.xml
● content_main.xml
● SettingsActivity.java
Question 3
How do you set up a settings Activity and a Fragment with a SwitchPreference for the UI, and still
remain compatible with the v7 appcompat library for backward compatibility with older versions
of Android?
● You can't use a fragment with a SwitchPreference and remain compatible with the
v7 appcompat library.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 59
Android Developer Fundamentals Course (V2) – Unit 4
The Android operating system provides a strong foundation for building apps that run well on a
wide range of devices and form factors. However, issues like complex lifecycles and the lack of a
recommended app architecture make it challenging to write robust apps. The Android Architecture
Components provide libraries for common tasks such as lifecycle management and data
persistence to make it easier to implement the recommended architecture.
Architecture Components help you structure your app in a way that is robust, testable, and
maintainable with less boilerplate code.
When it comes to architecture, it helps to see the big picture first. To introduce the terminology,
here's a short overview of the Architecture Components and how they work together. Each
component is explained more as you use it in this practical.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 60
Android Developer Fundamentals Course (V2) – Unit 4
The diagram below shows a basic form of the recommended architecture for apps that use
Architecture Components. The architecture consists of a UI controller, a ViewModel that serves
LiveData, a Repository, and a Room database. The Room database is backed by an SQLite database
and accessible through a data access object (DAO). Each component is described briefly below, and in
detail in the Architecture Components concepts chapter, 10.1: Storing data with Room. You implement the
components in this practical.
Because all the components interact, you will encounter references to these components
throughout this practical, so here is a short explanation of each.
Entity: In the context of Architecture Components, the entity is an annotated class that describes a
database table.
SQLite database: On the device, data is stored in an SQLite database. The Room persistence library
creates and maintains this database for you.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 61
Android Developer Fundamentals Course (V2) – Unit 4
DAO: Short for data access object. A mapping of SQL queries to functions. You used to have to define
these queries in a helper class. When you use a DAO, your code calls the functions, and the
components take care of the rest.
Room database: Database layer on top of an SQLite database that takes care of mundane tasks that
you used to handle with a helper class. The Room database uses the DAO to issue queries to the
SQLite database based on functions called.
Repository: A class that you create for managing multiple data sources. In addition to a Room
database, the Repository could manage remote data sources such as a web server.
ViewModel: Provides data to the UI and acts as a communication center between the Repository and the
UI. Hides the backend from the UI. ViewModel instances survive device configuration changes.
LiveData: A data holder class that follows the observer pattern, which means that it can be
observed. Always holds/caches latest version of data. Notifies its observers when the data has
changed. Generally, UI components observe relevant data. LiveData is lifecycle-aware, so it
automatically manages stopping and resuming observation based on the state of its observing
activity or fragment.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 62
Android Developer Fundamentals Course (V2) – Unit 4
● The observer pattern. In summary, the observer pattern defines a one-to-many dependency
between objects. Whenever an object changes its state, all the object's dependents are
notified and updated automatically. The main object is called the “subject” and its
dependents are called the "observers." Usually, the subject notifies the observers by calling
one of the observers' methods. The subject knows what methods to call, because the
observers are "registered" with the subject and specify the methods to call.
Important: This practical implements the architecture defined in the Guide to App Architecture
and explained in the Architecture Components concepts chapter, 10.1: Storing data with Room. It is
highly recommended that you read the concepts chapter.
What you'll do
● Create an app with an Activity that displays words in a RecyclerView.
● Create an Entity that represents word objects.
● Define the mapping of SQL queries to Java methods in a DAO (data access object).
● Use LiveData to make changes to the data visible to the UI, by way of observers.
● Add a Room database to the app for persisting data locally, and initialize the database.
● Abstract the data backend as a Repository class with an API that is agnostic to how the data is
stored or acquired.
● Use a ViewModel to separate all data operations from the UI.
● Add a second Activity that allows the user to add new words.
App overview
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 63
Android Developer Fundamentals Course (V2) – Unit 4
In this practical you build an app that uses the Android Architecture Components. The app, called
RoomWordsSample, stores a list of words in a Room database and displays the list in a
RecyclerView. The RoomWordsSample app is basic, but sufficiently complete that you can use it as
a template to build on.
● Works with a database to get and save words, and pre-populates the database with some
words.
● Displays all the words in a RecyclerView in MainActivity.
● Opens a second Activity when the user taps the + FAB button. When the user enters a word,
the app adds the word to the database and then the list updates automatically.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 64
Android Developer Fundamentals Course (V2) – Unit 4
The following diagram mirrors the overview diagram from the introduction and shows all the pieces
of the RoomWordsSample app. Each of the enclosing boxes (except for the SQLite database)
represents a class that you create.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 65
Android Developer Fundamentals Course (V2) – Unit 4
Tip: Print or open this diagram in a separate tab so you can refer to it as you build the code.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 66
Android Developer Fundamentals Course (V2) – Unit 4
Note: In this practical, you are expected to create member variables, import classes, and extract values
as needed. Code that you are expected to be familiar with is provided but not explained.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 67
Android Developer Fundamentals Course (V2) – Unit 4
In Android Studio, manually add the Architecture Component libraries to your Gradle files.
1. Add the following code to your build.gradle (Module: app) file, to the bottom of the
dependencies block (but still inside it).
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 68
Android Developer Fundamentals Course (V2) – Unit 4
// Room components
implementation
"android.arch.persistence.room:runtime:$rootProject.roomVersion"
annotationProcessor
"android.arch.persistence.room:compiler:$rootProject.roomVersion"
androidTestImplementation
"android.arch.persistence.room:testing:$rootProject.roomVersion"
// Lifecycle components
implementation
"android.arch.lifecycle:extensions:$rootProject.archLifecycleVersion"
annotationProcessor
"android.arch.lifecycle:compiler:$rootProject.archLifecycleVersion"
2. In your build.gradle (Project: RoomWordsSample) file, add the version numbers at the
end of the file.
ext {
roomVersion = '1.0.0'
archLifecycleVersion = '1.1.0'
Important: Use the latest version numbers for the Room and lifecycle libraries. To find the latest
version numbers:
On the Adding Components to your Project page, find the entry for the component, for
example Room.
The version number is defined at the start of the component's dependencies definition.
For example, the Room version number in the definition below is 1.1.1:
def room_version = "1.1.1"
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 69
Android Developer Fundamentals Course (V2) – Unit 4
The diagram below is the complete architecture diagram with the component that you are going to
implement in this task highlighted. Every task will have such a diagram to help you understand
where the current component fits into the overall structure of the app, and to see how the
components are connected.
The data for this app is words, and each word is represented by an entity in the database. In this
task you create the Word class and annotate it so Room can create a database table from it. The
diagram below shows a word_table database table. The table has one word column, which also acts
as the primary key, and two rows, one each for "Hello" and "World."
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 70
Android Developer Fundamentals Course (V2) – Unit 4
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 71
Android Developer Fundamentals Course (V2) – Unit 4
To make the Word class meaningful to a Room database, you must annotate it. Annotations identify
how each part of the Word class relates to an entry in the database. Room uses this information to
generate code.
● @Entity(tableName = "word_table")
Each @Entity class represents an entity in a table. Annotate your class declaration to
indicate that the class is an entity. Specify the name of the table if you want it to be different
from the name of the class.
● @PrimaryKey
Every entity needs a primary key. To keep things simple, each word in the
RoomWordsSample app acts as its own primary key. To learn how to auto-generate unique
keys, see the tip below.
● @NonNull
Denotes that a parameter, field, or method return value can never be null. The primary
key should always use this annotation. Use this annotation for any mandatory fields in
your rows.
● @ColumnInfo(name = "word")
Specify the name of a column in the table, if you want the column name to be different from
the name of the member variable.
● Every field that's stored in the database must either be public or have a "getter" method.
This app provides a getWord() "getter" method rather than exposing member variables
directly.
For a complete list of annotations, see the Room package summary reference.
Update your Word class with annotations, as shown in the code below.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 72
Android Developer Fundamentals Course (V2) – Unit 4
1. Add the @Entity notation to the class declaration and set the tableName to
"word_table".
2. Annotate the mWord member variable as the @PrimaryKey. Require mWord to be @NonNull,
and name the column "word".
Note: If you type in the annotations, Android Studio auto-imports everything you need.
@Entity(tableName = "word_table")
@PrimaryKey
@NonNull
@ColumnInfo(name = "word")
If you get errors for the annotations, you can import them manually, as follows:
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 73
Android Developer Fundamentals Course (V2) – Unit 4
import android.arch.persistence.room.ColumnInfo;
import android.arch.persistence.room.Entity;
import android.arch.persistence.room.PrimaryKey;
import android.support.annotation.NonNull;
Tip on auto-generating keys: To auto-generate a unique key for each entity, you would add and
annotate a primary integer key with autoGenerate=true. See Defining data using Room entities.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 74
Android Developer Fundamentals Course (V2) – Unit 4
The data access object, or Dao, is an annotated class where you specify SQL queries and associate
them with method calls. The compiler checks the SQL for errors, then generates queries from the
annotations. For common queries, the libraries provide convenience annotations such as @Insert.
Note that:
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 75
Android Developer Fundamentals Course (V2) – Unit 4
The DAO for this practical is basic and only provides queries for getting all the words, inserting
words, and deleting all the words.
4. Annotate the insert() method with @Insert. You don't have to provide any SQL! (There
are also @Delete and @Update annotations for deleting and updating a row, but you do not
use these operations in the initial version of this app.)
5. Declare a method to delete all the words:
void deleteAll();
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 76
Android Developer Fundamentals Course (V2) – Unit 4
List<Word> getAllWords();
8. Annotate the getAllWords() method with an SQL query that gets all the words from the
word_table, sorted alphabetically for convenience:
@Dao
@Insert
void deleteAll();
List<Word> getAllWords();
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 77
Android Developer Fundamentals Course (V2) – Unit 4
Tip: For this app, ordering the words is not strictly necessary. However, by default, return order is
not guaranteed, and ordering makes testing straightforward.
To learn more about DAOs, see Accessing data using Room DAOs.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 78
Android Developer Fundamentals Course (V2) – Unit 4
When you display data or use data in other ways, you usually want to take some action when the
data changes. This means you have to observe the data so that when it changes, you can react.
LiveData, which is a lifecycle library class for data observation, can help your app respond to data
changes. If you use a return value of type LiveData in your method description, Room generates
all necessary code to update the LiveData when the database is updated.
● In the WordDao interface, change the getAllWords() method signature so that the
returned List<Word> is wrapped with LiveData<>.
LiveData<List<Word>> getAllWords();
See the LiveData documentation to learn more about other ways to use LiveData, or watch this
Architecture Components: LiveData and Lifecycle video.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 79
Android Developer Fundamentals Course (V2) – Unit 4
Room is a database layer on top of an SQLite database. Room takes care of mundane tasks that you
used to handle with a database helper class such as SQLiteOpenHelper.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 80
Android Developer Fundamentals Course (V2) – Unit 4
2. Annotate the class to be a Room database. Declare the entities that belong in the
database—in this case there is only one entity, Word. (Listing the entities class or classes
creates corresponding tables in the database.) Set the version number.
3. Define the DAOs that work with the database. Provide an abstract "getter" method for each
@Dao.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 81
Android Developer Fundamentals Course (V2) – Unit 4
if (INSTANCE == null) {
synchronized (WordRoomDatabase.class) {
if (INSTANCE == null) {
return INSTANCE;
5. Add code to create a database where indicated by the Create database here comment in
the code above.
The following code uses Room's database builder to create a RoomDatabase object named
"word_database" in the application context from the WordRoomDatabase class.
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
WordRoomDatabase.class, "word_database")
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 82
Android Developer Fundamentals Course (V2) – Unit 4
.build();
In this practical you don't update the entities and the version numbers. However, if you modify the
database schema, you need to update the version number and define how to handle migrations. For
a sample app such as the one you're creating, destroying and re-creating the database is a fine
migration strategy. For a real app, you must implement a non-destructive migration strategy. See
Understanding migrations with Room.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 83
Android Developer Fundamentals Course (V2) – Unit 4
.build();
}
}
}
return INSTANCE;
}
}
Important: In Android Studio, if you get errors when you paste code or during the build process,
make sure you are using the full package name for imports. See Adding Components to your
Project. Then select Build > Clean Project. Then select Build > Rebuild Project, and build again.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 84
Android Developer Fundamentals Course (V2) – Unit 4
A Repository is a class that abstracts access to multiple data sources. The Repository is not part of the
Architecture Components libraries, but is a suggested best practice for code separation and
architecture. A Repository class handles data operations. It provides a clean API to the rest of the
app for app data.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 85
Android Developer Fundamentals Course (V2) – Unit 4
A Repository manages query threads and allows you to use multiple backends. In the most common
example, the Repository implements the logic for deciding whether to fetch data from a network or
use results cached in the local database.
3. Add a constructor that gets a handle to the database and initializes the member variables.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 86
Android Developer Fundamentals Course (V2) – Unit 4
WordRepository(Application application) {
WordRoomDatabase db = WordRoomDatabase.getDatabase(application);
mWordDao = db.wordDao();
mAllWords = mWordDao.getAllWords();
4. Add a wrapper method called getAllWords() that returns the cached words as LiveData.
Room executes all queries on a separate thread. Observed LiveData notifies the observer
when the data changes.
LiveData<List<Word>> getAllWords() {
return mAllWords;
5. Add a wrapper for the insert() method. Use an AsyncTask to call insert() on a non-UI
thread, or your app will crash. Room ensures that you don't do any long-running operations
on the main thread, which would block the UI.
new insertAsyncTask(mWordDao).execute(word);
6. Create the insertAsyncTask as an inner class. You should be familiar with AsyncTask, so
here is the insertAsyncTask code for you to copy:
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 87
Android Developer Fundamentals Course (V2) – Unit 4
insertAsyncTask(WordDao dao) {
mAsyncTaskDao = dao;
@Override
mAsyncTaskDao.insert(params[0]);
return null;
WordRepository(Application application) {
WordRoomDatabase db = WordRoomDatabase.getDatabase(application);
mWordDao = db.wordDao();
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 88
Android Developer Fundamentals Course (V2) – Unit 4
mAllWords = mWordDao.getAllWords();
LiveData<List<Word>> getAllWords() {
return mAllWords;
new insertAsyncTask(mWordDao).execute(word);
insertAsyncTask(WordDao dao) {
mAsyncTaskDao = dao;
@Override
mAsyncTaskDao.insert(params[0]);
return null;
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 89
Android Developer Fundamentals Course (V2) – Unit 4
Note: For this simple example, the Repository doesn't do much. For a more complex
implementation, see the BasicSample code on GitHub.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 90
Android Developer Fundamentals Course (V2) – Unit 4
The ViewModel is a class whose role is to provide data to the UI and survive configuration changes. A
ViewModel acts as a communication center between the Repository and the UI. The ViewModel is part
of the lifecycle library. For an introductory guide to this topic, see ViewModel.
A ViewModel holds your app's UI data in a way that survives configuration changes. Separating your
app's UI data from your Activity and Fragment classes lets you better follow the single
responsibility principle: Your activities and fragments are responsible for drawing data to the screen,
while your ViewModel is responsible for holding and processing all the data needed for the UI.
In the ViewModel, use LiveData for changeable data that the UI will use or display.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 91
Android Developer Fundamentals Course (V2) – Unit 4
Warning:
Never pass context into ViewModel instances.
Do not store Activity, Fragment, or View instances or their Context in the ViewModel.
An Activity can be destroyed and created many times during the lifecycle of a ViewModel,
such as when the device is rotated. If you store a reference to the Activity in the
ViewModel, you end up with references that point to the destroyed Activity. This is a
memory leak. If you need the application context, use AndroidViewModel, as shown in this
practical. </div>
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 92
Android Developer Fundamentals Course (V2) – Unit 4
4. Add a constructor that gets a reference to the WordRepository and gets the list of all words
from the WordRepository.
super(application);
mAllWords = mRepository.getAllWords();
5. Add a "getter" method that gets all the words. This completely hides the implementation from the
UI.
6. Create a wrapper insert() method that calls the Repository's insert() method. In this way,
the implementation of insert() is completely hidden from the UI.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 93
Android Developer Fundamentals Course (V2) – Unit 4
super(application);
mAllWords = mRepository.getAllWords();
Next, add the XML layout for the list and items to be displayed in the RecyclerView.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 94
Android Developer Fundamentals Course (V2) – Unit 4
This practical assumes that you are familiar with creating layouts in XML, so the code is just
provided.
1. Change the colors in colors.xml to the following: (to use Material Design colors):
<resources>
<color name="colorPrimary">#2196F3</color>
<color name="colorPrimaryLight">#64b5f6</color>
<color name="colorPrimaryDark">#1976D2</color>
<color name="colorAccent">#FFFF9800</color>
<color name="colorTextPrimary">@android:color/white</color>
<color name="colorScreenBackground">#fff3e0</color>
<color name="colorTextHint">#E0E0E0</color>
</resources>
<style name="text_view_style">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:textAppearance">
@android:style/TextAppearance.Large</item>
<item name="android:background">@color/colorPrimaryLight</item>
<item name="android:layout_marginTop">8dp</item>
<item name="android:layout_gravity">center</item>
<item name="android:padding">16dp</item>
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 95
Android Developer Fundamentals Course (V2) – Unit 4
<item name="android:textColor">@color/colorTextPrimary</item>
</style>
<LinearLayout xmlns:android=
"http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
xmlns:tools="http://schemas.android.com/tools"
android:layout_height="wrap_content">
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/text_view_style"
</LinearLayout>
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 96
Android Developer Fundamentals Course (V2) – Unit 4
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
tools:listitem="@layout/recyclerview_item"
/>
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 97
Android Developer Fundamentals Course (V2) – Unit 4
android:src="@drawable/ic_add_black_24dp"
You are going to display the data in a RecyclerView, which is a little nicer than just throwing the
data in a TextView. This practical assumes that you know how RecyclerView,
RecyclerView.LayoutManager, RecyclerView.ViewHolder, and RecyclerView.Adapter
work.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 98
Android Developer Fundamentals Course (V2) – Unit 4
@Override
@Override
if (mWords != null) {
holder.wordItemView.setText(current.getWord());
} else {
holder.wordItemView.setText("No Word");
mWords = words;
notifyDataSetChanged();
// mWords has not been updated ﴾means initially, it's null, and we can't return
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 99
Android Developer Fundamentals Course (V2) – Unit 4
null﴿.
@Override
if (mWords != null)
return mWords.size();
else return 0;
super(itemView);
wordItemView = itemView.findViewById(R.id.textView);
Note: The mWords variable in the adapter caches the data. In the next task, you add the code that
updates the data automatically.
Note: The getItemCount() method needs to account gracefully for the possibility that the data
is not yet ready and mWords is still null. In a more sophisticated app, you could display
placeholder data or something else that would be meaningful to the user.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 100
Android Developer Fundamentals Course (V2) – Unit 4
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 101
Android Developer Fundamentals Course (V2) – Unit 4
1. Run your app to make sure the app compiles and runs. There are no items, because you
have not hooked up the data yet. The app should display the empty recycler view.
There is no data in the database yet. You will add data in two ways: Add some data when the
database is opened, and add an Activity for adding words. Every time the database is opened, all
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 102
Android Developer Fundamentals Course (V2) – Unit 4
content is deleted and repopulated. This is a reasonable solution for a sample app, where you
usually want to restart on a clean slate.
To delete all content and repopulate the database whenever the app is started, you create a
RoomDatabase.Callback and override the onOpen() method. Because you cannot do Room
database operations on the UI thread, onOpen() creates and executes an AsyncTask to add
content to the database.
new RoomDatabase.Callback(){
@Override
super.onOpen(db);
new PopulateDbAsync(INSTANCE).execute();
};
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 103
Android Developer Fundamentals Course (V2) – Unit 4
*/
private static class PopulateDbAsync extends AsyncTask<Void, Void,
Void> {
PopulateDbAsync(WordRoomDatabase db) {
mDao = db.wordDao();
}
@Override
protected Void doInBackground(final Void... params) {
// Start the app with a clean database every time.
// Not needed if you only populate the database
// when it is first created
mDao.deleteAll();
1. Add the callback to the database build sequence in WordRoomDatabase, right before you call
.build():
.addCallback(sRoomDatabaseCallback)
Now that you have created the method to populate the database with the initial set of words, the
next step is to add the code to display those words in the RecyclerView.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 104
Android Developer Fundamentals Course (V2) – Unit 4
To display the current contents of the database, you add an observer that observes the LiveData in
the ViewModel. Whenever the data changes (including when it is initialized), the onChanged()
callback is invoked. In this case, the onChanged() callback calls the adapter's setWord() method to
update the adapter's cached data and refresh the displayed list.
1. In MainActivity, create a member variable for the ViewModel, because all the activity's
interactions are with the WordViewModel only.
mWordViewModel = ViewModelProviders.of(this).get(WordViewModel.class);
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 105
Android Developer Fundamentals Course (V2) – Unit 4
@Override
adapter.setWords(words);
});
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 106
Android Developer Fundamentals Course (V2) – Unit 4
2. Run the app. The initial set of words appears in the RecyclerView.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 107
Android Developer Fundamentals Course (V2) – Unit 4
Now you will add an Activity that lets the user use the FAB to enter new words. This is what the
interface for the new activity will look like:
<string name="hint_word">Word…</string>
<string name="button_save">Save</string>
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 108
Android Developer Fundamentals Course (V2) – Unit 4
1. Use the Empty Activity template to create a new activity, NewWordActivity. Verify that the
activity has been added to the Android Manifest.
<activity android:name=".NewWordActivity"></activity>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorScreenBackground"
android:orientation="vertical"
android:padding="24dp">
<EditText
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 109
Android Developer Fundamentals Course (V2) – Unit 4
android:id="@+id/edit_word"
style="@style/text_view_style"
android:hint="@string/hint_word"
android:inputType="textAutoComplete" />
<Button
android:id="@+id/button_save"
style="@style/button_style"
android:text="@string/button_save" />
</LinearLayout>
3. Implement the NewWordActivity class. The goal is that when the user presses the Save
button, the new word is put in an Intent to be sent back to the parent Activity.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 110
Android Developer Fundamentals Course (V2) – Unit 4
@Override
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_new_word);
mEditWordView = findViewById(R.id.edit_word);
button.setOnClickListener(new View.OnClickListener() {
if (TextUtils.isEmpty(mEditWordView.getText())) {
setResult(RESULT_CANCELED, replyIntent);
} else {
replyIntent.putExtra(EXTRA_REPLY, word);
setResult(RESULT_OK, replyIntent);
finish();
});
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 111
Android Developer Fundamentals Course (V2) – Unit 4
mWordViewModel.insert(word);
} else {
Toast.makeText(
getApplicationContext(),
R.string.empty_not_saved,
Toast.LENGTH_LONG).show();
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 112
Android Developer Fundamentals Course (V2) – Unit 4
1. In MainActivity,start NewWordActivity when the user taps the FAB. Replace the code in
the FAB's onClick() click handler with the following code:
startActivityForResult(intent, NEW_WORD_ACTIVITY_REQUEST_CODE);
1. Run your app. When you add a word to the database in NewWordActivity, the UI
automatically updates.
2. Add a word that already exists in the list. What happens? Does your app crash?
Your app uses the word itself as the primary key, and each primary key must be unique.
You can specify a conflict strategy to tell your app what to do when the user tries to add an
existing word.
3. In the WordDao interface, change the annotation for the insert() method to:
@Insert(onConflict = OnConflictStrategy.IGNORE)
4. Run your app again and try adding a word that already exists. What happens now?
Solution code
Android Studio project: RoomWordsSample
Summary
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 113
Android Developer Fundamentals Course (V2) – Unit 4
Now that you have a working app, let's recap what you've built. Here is the app structure again,
from the beginning:
● The words are cached in the RecyclerViewAdapter as a List of words (mWords). The list is
automatically updated and redisplayed when the data changes.
● The automatic update happens because in the MainActivity, there is an Observer that
observes the words and is notified when the words change. When there is a change, the
observer's onChange() method is executed and updates mWords in the WordListAdapter.
● The data can be observed because it is LiveData. And what is observed is the
LiveData<List<Word>> that is returned by the WordViewModel object.
● The WordViewModel hides everything about the backend from the user interface. It provides
methods for accessing the UI data, and it returns LiveData so that MainActivity can set
up the observer relationship. Views, activities, and fragments only interact with the data
through the ViewModel. As such, it doesn't matter where the data comes from.
● In this case, the data comes from a Repository. The ViewModel does not need to know what
that Repository interacts with. It just needs to know how to interact with the Repository,
which is through the methods exposed by the Repository.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 114
Android Developer Fundamentals Course (V2) – Unit 4
● The Repository manages one or more data sources. In the RoomWordsSample app, that
backend is a Room database. Room is a wrapper around and implements an SQLite
database. Room does a lot of work for you that you used to have to do yourself. For
example, Room does everything that you used to use an SQLiteOpenHelper class to do.
● The DAO maps method calls to database queries, so that when the Repository calls a
method such as getAllWords(), Room can execute SELECT * from word_table ORDER BY
word ASC.
● The result returned from the query is observed LiveData. Therefore, every time the data in
Room changes, the Observer interface's onChanged() method is executed and the UI is
updated.
Related concept
The related concept documentation is in 10.1: Room, LiveData, and ViewModel.
Learn more
To continue working with the RoomWordsSample app and learn more ways to use a Room database,
see the 10.1 Part B: Room, LiveData, and ViewModel codelab, which takes up where this codelab
leaves off.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 115
Android Developer Fundamentals Course (V2) – Unit 4
● DAO
● Room DAOs
● LiveData
● MutableLiveData
● ViewModel
● ViewModelProviders
Codelabs:
Videos:
Code samples:
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 116
Android Developer Fundamentals Course (V2) – Unit 4
Homework
● Create a simple app that stores one text document and displays the contents of the document in
a TextView. When the user edits the document, changes appear in the TextView.
● Create a questionanswer app. Start with only questions and let users add new questions and
answers.
● As a challenge, add a button to each answer in the questionanswer app that displays additional
information that's stored in a different repository. The information could come from a file on the
device, or from a page on the internet.
Question 1
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 117
Android Developer Fundamentals Course (V2) – Unit 4
Question 2
● Often used with LiveData for changeable data that the UI will use or display.
● Prevents your data from being lost when the app crashes.
Question 3
● A class whose methods run always in the background, not on the main thread.
● A class that the compiler checks for SQL errors, then uses to generate queries from
the annotations.
Question 4
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 118
Android Developer Fundamentals Course (V2) – Unit 4
This codelab (practical) follows on from 10.1 Part A: Room, LiveData, and ViewModel. This codelab
gives you more practice at using the API provided by the Room library to implement database
functionality. You will add the ability to delete specific items from the database. This codelab also
includes a coding challenge, in which you update the app so the user can edit existing data.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 119
Android Developer Fundamentals Course (V2) – Unit 4
● Using entity classes, data access objects (DAOs), and the RoomDatabase to store and
retrieve data in Android's built-in SQLite database. You learned these topics in 10.1 Part A:
Room, LiveData, and ViewModel.
● How to update existing data (if you build the challenge app).
What you'll do
● Update the RoomWordsSample app to keep data when the app closes.
● Allow users to delete all words by selecting an Options menu item.
● Allow users to delete a specific word by swiping an item in the list.
● Optionally, in a coding challenge, extend the app to allow the user to update existing words.
App overview
You will extend the RoomWordsSample app that you created in the previous codelab. So far, that
app displays a list of words, and users can add words. When the app closes and re-opens, the app
re-initializes the database. Words that the user has added are lost.
In this practical, you extend the app so that it only initializes the data in the database if there is no
existing data.
Then you add a menu item that allows the user to delete all the data.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 120
Android Developer Fundamentals Course (V2) – Unit 4
You also enable the user to swipe a word to delete it from the database.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 121
Android Developer Fundamentals Course (V2) – Unit 4
In this task you update the app so that when it opens, the initial data set is only added if the
database has no data.
To detect whether the database contains data already, you can run a query to get one data item. If
the query returns nothing, then the database is empty.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 122
Android Developer Fundamentals Course (V2) – Unit 4
Note: In a production app, you might want to allow users to delete all data without re-initializing
the data when the app restarts. But for testing purposes, it's useful to be able to delete all data,
then re-initialize the data when the app starts.
Room issues the database query when the getAnyWord() method is called and returns an array
containing one word. You don't need to write any additional code to implement it.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 123
Android Developer Fundamentals Course (V2) – Unit 4
2. Run your app and add several new words. Close the app and restart it. You should see the
new words that you added, as the words should now persist when the app is closed and
opened again.
Here are the general steps for implementing a method to use the Room library to interact with the
database:
● Add the method to the DAO, and annotate it with the relevant database operation. For the
deleteAll() method, you already did this step in the previous practical.
● Add the method to the WordRepository class. Write the code to run the method in the
background.
● To call the method in the WordRepository class, add the method to the WordViewModel.
The rest of the app can then access the method through the WordViewModel.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 124
Android Developer Fundamentals Course (V2) – Unit 4
void deleteAll();
deleteAllWordsAsyncTask(WordDao dao) {
mAsyncTaskDao = dao;
}
@Override
protected Void doInBackground(Void... voids) {
mAsyncTaskDao.deleteAll();
return null;
}
}
2. In the WordRepository class, add the deleteAll() method to invoke the AsyncTask that
you defined.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 125
Android Developer Fundamentals Course (V2) – Unit 4
Note: The production version of your app must provide safeguards so that users do not
accidentally wipe out their entire database. However, while you develop your app, it's helpful to be
able to clear out test data quickly. This is especially true now that your app does not clear out the
data when the app opens.
<item
android:id="@+id/clear_data"
android:orderInCategory="100"
android:title="@string/clear_all_data"
app:showAsAction="never" />
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 126
Android Developer Fundamentals Course (V2) – Unit 4
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.clear_data) {
// Add a toast just for confirmation
Toast.makeText(this, "Clearing the data...",
Toast.LENGTH_SHORT).show();
return super.onOptionsItemSelected(item);
}
3. Run your app. In the Options menu, select Clear all data. All words should disappear.
4. Restart the app. (Restart it from your device or the emulator; don't run it again from Android
Studio) You should see the initial set of words.
Note: After you clear the data, re-deploying the app from Android Studio shows the initial data set
again. Opening the app shows the empty data set.
Again, here are the general steps to implement a method to use the Room library to interact with the
database:
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 127
Android Developer Fundamentals Course (V2) – Unit 4
● Add the method to the DAO, and annotate it with the relevant database operation.
● Add the method to the WordRepository class. Write the code to run the method in the
background.
● To call the method in the WordRepository class, add the method to the WordViewModel.
The rest of the app can then access the method through the WordViewModel.
@Delete
void deleteWord(Word word);
Because this operation deletes a single row, the @Delete annotation is all that is needed to delete
the word from the database.
deleteWordAsyncTask(WordDao dao) {
mAsyncTaskDao = dao;
}
@Override
protected Void doInBackground(final Word... params) {
mAsyncTaskDao.deleteWord(params[0]);
return null;
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 128
Android Developer Fundamentals Course (V2) – Unit 4
}
}
2. In WordRepository, add the deleteWord() method to invoke the AsyncTask you defined.
You have now implemented the logic to delete a word. As yet, there is no way to invoke the
delete-word operation from the app's UI. You fix that next.
Use the ItemTouchHelper class provided by the Android Support Library (version 7 and higher) to
implement swipe functionality in your RecyclerView. The ItemTouchHelper class has the
following methods:
● onMove() is called when the user moves the item. You will not implement any move
functionality in this app.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 129
Android Developer Fundamentals Course (V2) – Unit 4
● onSwipe() is called when the user swipes the item. You implement this method to delete
the word that was swiped.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 130
Android Developer Fundamentals Course (V2) – Unit 4
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder,
int direction) {
int position = viewHolder.getAdapterPosition();
Word myWord = adapter.getWordAtPosition(position);
Toast.makeText(MainActivity.this, "Deleting " +
myWord.getWord(), Toast.LENGTH_LONG).show();
helper.attachToRecyclerView(recyclerView);
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 131
Android Developer Fundamentals Course (V2) – Unit 4
Given the position, you can get the word displayed by the ViewHolder by calling the
getWordAtPosition() method that you defined in the adapter:
mWordViewModel.deleteWord(myWord);
1. Run your app. You should be able to delete words by swiping them left or right.
Solution code
Android Studio project: RoomWordsWithDelete
Coding challenge
Note: All coding challenges are optional and are not prerequisites for later lessons.
Challenge: Update your app to allow users to edit a word by tapping the word and then saving
their changes.
Hints
Make changes in NewWordActivity
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 132
Android Developer Fundamentals Course (V2) – Unit 4
You can add functionality to NewWordActivity, so that it can be used either to add a new word or
edit an existing word.
The Word entity class uses the word field as the database key. However, when you update a row in
the database, the item being updated cannot be the primary key, because the primary key is unique
to each row and never changes. So you need to add an auto-generated id to use as the primary key
@PrimaryKey(autoGenerate = true)
private int id;
@NonNull
@ColumnInfo(name = "word")
private String mWord;
Add a constructor to the Word entity class that takes id and word as parameters. Make sure this
additional constructor is annotated using @Ignore, because Room expects only one constructor by
default in an entity class.
@Ignore
public Word(int id, @NonNull String word) {
this.id = id;
this.mWord = word;
}
To update an existing Word, create the Word using this constructor. Room will use the primary key (in
this case the id) to find the existing entry in the database so it can be updated.
@Update
void update(Word... word);
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 133
Android Developer Fundamentals Course (V2) – Unit 4
Summary
● Annotate methods in the data access object (DAO) as @insert, @delete, @update, @query.
● For simple insertions, updates and deletions, it is enough to add the relevant annotation to
the method in the DAO.
For example:
@Delete
void deleteWord(Word word);
@Update
void update(Word... word);
● For queries or more complex database interactions, such as deleting all words, use the
@query annotation and provide the SQL for the operation.
For example:
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 134
Android Developer Fundamentals Course (V2) – Unit 4
ItemTouchHelper
● To enable users to swipe or move items in a RecyclerView, you can use the
ItemTouchHelper class.
● To identify which item the user moved or swiped, you can add a method to the adapter for
the RecylerView. The method takes a position and returns the relevant item. Call the
method inside onMove() or onSwipe().
Related concept
The related concept documentation is in 10.1: Room, LiveData, and ViewModel.
Learn more
Entities, data access objects (DAOs), and ViewModel:
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 135
Android Developer Fundamentals Course (V2) – Unit 4
ItemTouchHelper:
● ItemTouchHelper reference
Homework
Create an app that demonstrates how data that is stored in at least two different locations survives
configuration changes and the destruction of the app. You can do this by storing small pieces of
data, such as strings, in different data stores.
● The app should demonstrate what happens to data that is not saved.
● The app could demonstrate what happens to data that is preserved with
savedInstanceState, data that uses LiveData with a ViewModel, and data that is
stored in a file or database.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 136
Android Developer Fundamentals Course (V2) – Unit 4
Question 1
Android Architecture Components provide some convenience annotations for DAOs. Which of the
following are available? Select as many as apply.
● @Dao
● @Insert
● @Delete
● @Update
● @Query
● @Select
Question 2
What are the benefits of using Architecture Components?
● Architecture Components help you structure your app in a way that is robust and
testable.
● Architecture Components help you create better UIs.
● Architecture Components provide a simple, flexible, and practical approach to
structuring your app.
● If you use the provided libraries and architecture, your app is more maintainable
with less boilerplate code.
This work is licensed under a Creative Commons Attribution 4.0 International License.
This PDF is a one-time snapshot. See developer.android.com/courses/fundamentals-training/toc-v2
for the latest updates.
Page 137