The purpose of Architecture Components is to provide guidance on app architecture, with libraries for common tasks like lifecycle management and data persistence.

Architecture components help you structure your app in a way that is robust, testable, and maintainable with less boilerplate code.

Architecture Components provide a simple, flexible, and practical approach that frees you from some common problems so you can focus on building great experiences.

What are the recommended Architecture Components?

To introduce the terminology, here is a short introduction to the Architecture Components and how they work together. Note that this codelab focuses on a subset of the components, namely LiveData, ViewModel and Room. Each component is explained more as you use it. This diagram shows a basic form of this architecture.

Entity: When working with Architecture Components, this is an annotated class that describes a database table.

SQLite database: On the device, data is stored in an SQLite database. For simplicity, additional storage options, such as a web server, are omitted. The Room persistence library creates and maintains this database for you.

DAO: Data access object. A mapping of SQL queries to functions. You used to have to define these painstakingly in your SQLiteOpenHelper class. When you use a DAO, you call the methods, and Room takes care of the rest.

Room database: Database layer on top of SQLite database that takes care of mundane tasks that you used to handle with an SQLiteOpenHelper. Database holder that serves as an access point to the underlying SQLite database. The Room database uses the DAO to issue queries to the SQLite database.

Repository: A class that you create, for example using the WordRepository class. You use the Repository for managing multiple data sources.

ViewModel: Provides data to the UI. Acts as a communication center between the Repository and the UI. Hides where the data originates from the UI. ViewModel instances survive configuration changes.

LiveData: A data holder class that can be observed. Always holds/caches latest version of data. Notifies its observers when the data has changed. LiveData is lifecycle aware. UI components just observe relevant data and don't stop or resume observation. LiveData automatically manages all of this since it's aware of the relevant lifecycle status changes while observing.

What you will build

You will build an app that uses Android Architecture Components and implements the architecture from Guide to App Architecture for these components. The sample app stores a list of words in a Room database and displays it in a RecyclerView. The app is bare bones but sufficiently complex that you can use it as a template to build upon.

In this codelab you build an app that does the following:

RoomWordSample architecture overview

The following diagram shows all the pieces of the app. Each of the enclosing boxes (except for the SQLite database) represents a class that you will create.

What you'll learn

There are a lot of steps to using the Architecture Components and implementing the recommended architecture. The most important thing is to create a mental model of what is going on, and understand how the pieces fit together and how the data flows. As you work through this codelab, don't just copy and paste the code, but try to start building that inner understanding.

What you'll need

You need to be solidly familiar with the Java programming language, object-oriented design concepts, and Android Development Fundamentals. In particular:

This codelab is focused on Android Architecture Components. Off-topic concepts and code are provided for you to simply copy and paste.

This codelab provides all the code you need to build the complete app.

Open Android Studio and create an app as follows:

You have to add the component libraries to your gradle files.

Add the following code to your build.gradle (Module: app) file, below the dependencies block.

// 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"

In your build.gradle (Project: RoomWordsSample) file, add the version numbers to the end of the file, as given in the code below.

ext {
   roomVersion = '1.0.0'
   archLifecycleVersion = '1.1.0'
}

The data for this app is words, and each word is an Entity. Create a class called Word that describes a word Entity. You need a constructor and a "getter" method for the data model class, because that's how Room knows to instantiate your objects.

Here is the code:

public class Word {

   private String mWord;

   public Word(@NonNull String word) {this.mWord = word;}

   public String getWord(){return this.mWord;}
}

To make the Word class meaningful to a Room database, you need to annotate it. Annotations identify how each part of this class relates to an entry in the database. Room uses this information to generate code.

You can find a complete list of annotations in the Room package summary reference.

Update your Word class with annotations as shown in this code. If you type the annotations, Android Studio will auto-import.

@Entity(tableName = "word_table")
public class Word {

   @PrimaryKey
   @NonNull
   @ColumnInfo(name = "word")
   private String mWord;

   public Word(String word) {this.mWord = word;}

   public String getWord(){return this.mWord;}
}

What is the DAO?

In the DAO (data access object), you specify SQL queries and associate them with method calls. The compiler checks the SQL and generates queries from convenience annotations for common queries, such as @Insert.

The DAO must be an interface or abstract class.

By default, all queries must be executed on a separate thread.

Room uses the DAO to create a clean API for your code.

Implement the DAO

The DAO for this codelab is basic and provides queries for getting all the words, inserting a word, and deleting all the words.

  1. Create a new Interface and call it WordDao.
  2. Annotate the class with @Dao to identify it as a DAO class for Room.
  3. Declare a method to insert one word: void insert(Word word);
  4. Annotate the 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 are not using them in this app.)
  5. Declare a method to delete all the words: void deleteAll();.
  6. There is no convenience annotation for deleting multiple entities, so annotate the method with the generic @Query.
  7. Provide the SQL query as a string parameter to @Query. Use @Query for read and complicated queries and provide SQL.
    @Query("DELETE FROM word_table")
  8. Create a method to get all the words: getAllWords();.
    Have the method return a List of Words.
    List<Word> getAllWords();
  9. Annotate the method with the SQL query:
    @Query("SELECT * from word_table ORDER BY word ASC")

Here is the completed code:

@Dao
public interface WordDao {

   @Insert
   void insert(Word word);

   @Query("DELETE FROM word_table")
   void deleteAll();

   @Query("SELECT * from word_table ORDER BY word ASC")
   List<Word> getAllWords();
}

When data changes you usually want to take some action, such as displaying the updated data in the UI. This means you have to observe the data so that when it changes, you can react. Depending on how the data is stored, this can be tricky. Observing changes to data across multiple components of your app can create explicit, rigid dependency paths between the components. This makes testing and debugging difficult, among other things.

LiveData, a lifecycle library class for data observation, solves this problem. Use a return value of type LiveData in your method description, and Room generates all necessary code to update the LiveData when the database is updated.

In WordDao, change the getAllWords() method signature so that the returned List<Word> is wrapped with LiveData.

   @Query("SELECT * from word_table ORDER BY word ASC")
   LiveData<List<Word>> getAllWords();

Later in this codelab, you create an Observer of the data in the onCreate() method of MainActivity and override the observer's onChanged() method. When the LiveData changes, the observer is notified and onChanged() is executed. You will then update the cached data in the adapter, and the adapter will update what the user sees.

What is a Room database?

Room is a database layer on top of an SQLite database. Room takes care of mundane tasks that you used to handle with an SQLiteOpenHelper.

Implement the Room database

  1. Create a public abstract class that extends RoomDatabase and call it WordRoomDatabase.
    public abstract class WordRoomDatabase extends RoomDatabase {}
  2. Annotate the class to be a Room database, declare the entities that belong in the database and set the version number. Listing the entities will create tables in the database.
    @Database(entities = {Word.class}, version = 1)
  3. Define the DAOs that work with the database. Provide an abstract "getter" method for each @Dao.
    public abstract WordDao wordDao();

Here is the code:

@Database(entities = {Word.class}, version = 1)
public abstract class WordRoomDatabase extends RoomDatabase {
   public abstract WordDao wordDao();

}
  1. Make the WordRoomDatabase a singleton to prevent having multiple instances of the database opened at the same time.

Here is the code:

private static WordRoomDatabase INSTANCE;

public static WordRoomDatabase getDatabase(final Context context) {
   if (INSTANCE == null) {
       synchronized (WordRoomDatabase.class) {
           if (INSTANCE == null) {
               // Create database here
           }
       }
   }
   return INSTANCE;
}
  1. Add the code to get a database. This code uses Room's database builder to create a RoomDatabase object in the application context from the WordRoomDatabase class and names it "word_database".
// Create database here
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
       WordRoomDatabase.class, "word_database")
       .build();

Here is the complete code for the class:

@Database(entities = {Word.class}, version = 1)
public abstract class WordRoomDatabase extends RoomDatabase {

   public abstract WordDao wordDao();

   private static WordRoomDatabase INSTANCE;


   static WordRoomDatabase getDatabase(final Context context) {
       if (INSTANCE == null) {
           synchronized (WordRoomDatabase.class) {
               if (INSTANCE == null) {
                   INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
                           WordRoomDatabase.class, "word_database")
                           .build();                
                   
               }
           }
       }
       return INSTANCE;
   }

}

What is a Repository?

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.

Why use a Repository?

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 a local database.

Implementing the Repository

  1. Create a public class called WordRepository.
  2. Add member variables for the DAO and the list of words.
private WordDao mWordDao;
private LiveData<List<Word>> mAllWords;
  1. Add a constructor that gets a handle to the database and initializes the member variables.
WordRepository(Application application) {
    WordRoomDatabase db = WordRoomDatabase.getDatabase(application);
    mWordDao = db.wordDao();
    mAllWords = mWordDao.getAllWords();
}
  1. Add a wrapper for getAllWords(). Room executes all queries on a separate thread. Observed LiveData will notify the observer when the data has changed.
LiveData<List<Word>> getAllWords() {
   return mAllWords;
}
  1. Add a wrapper for the insert() method. You must call this 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, blocking the UI.
public void insert (Word word) {
    new insertAsyncTask(mWordDao).execute(word);
}
  1. There is nothing magical about the AsyncTask, so here it is for you to copy.
private static class insertAsyncTask extends AsyncTask<Word, Void, Void> {

    private WordDao mAsyncTaskDao;

    insertAsyncTask(WordDao dao) {
        mAsyncTaskDao = dao;
    }

    @Override
    protected Void doInBackground(final Word... params) {
        mAsyncTaskDao.insert(params[0]);
        return null;
    }
}

Here is the complete code:

public class WordRepository {

   private WordDao mWordDao;
   private LiveData<List<Word>> mAllWords;

   WordRepository(Application application) {
       WordRoomDatabase db = WordRoomDatabase.getDatabase(application);
       mWordDao = db.wordDao();
       mAllWords = mWordDao.getAllWords();
   }

   LiveData<List<Word>> getAllWords() {
       return mAllWords;
   }


   public void insert (Word word) {
       new insertAsyncTask(mWordDao).execute(word);
   }

   private static class insertAsyncTask extends AsyncTask<Word, Void, Void> {

       private WordDao mAsyncTaskDao;

       insertAsyncTask(WordDao dao) {
           mAsyncTaskDao = dao;
       }

       @Override
       protected Void doInBackground(final Word... params) {
           mAsyncTaskDao.insert(params[0]);
           return null;
       }
   }
}

What is a ViewModel?

The ViewModel's 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. You can also use a ViewModel to share data between fragments. The ViewModel is part of the lifecycle library.

For an introductory guide to this topic, see ViewModel.

Why use a ViewModel?

A ViewModel holds your app's UI data in a lifecycle-conscious 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 can take care of 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. Using LiveData has several benefits:

Implement the ViewModel

  1. Create a class called WordViewModel that extends AndroidViewModel.
public class WordViewModel extends AndroidViewModel {}
  1. Add a private member variable to hold a reference to the repository.
   private WordRepository mRepository;
  1. Add a private LiveData member variable to cache the list of words.
  private LiveData<List<Word>> mAllWords;
  1. Add a constructor that gets a reference to the repository and gets the list of words from the repository.
   public WordViewModel (Application application) {
       super(application);
       mRepository = new WordRepository(application);
       mAllWords = mRepository.getAllWords();
   }
  1. Add a "getter" method for all the words. This completely hides the implementation from the UI.
   LiveData<List<Word>> getAllWords() { return mAllWords; }
  1. 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.
public void insert(Word word) { mRepository.insert(word); }

Here is the complete code for WordViewModel:

public class WordViewModel extends AndroidViewModel {

   private WordRepository mRepository;

   private LiveData<List<Word>> mAllWords;

   public WordViewModel (Application application) {
       super(application);
       mRepository = new WordRepository(application);
       mAllWords = mRepository.getAllWords();
   }

   LiveData<List<Word>> getAllWords() { return mAllWords; }

   public void insert(Word word) { mRepository.insert(word); }
}

Next, you need to add the XML layout for the list and items.

This codelab assumes that you are familiar with creating layouts in XML, so we are just providing you with the code.

Add a style for list items in values/styles.xml:

<!-- The default font for RecyclerView items is too small.
The margin is a simple delimiter between the words. -->
<style name="word_title">
   <item name="android:layout_width">match_parent</item>
   <item name="android:layout_height">26dp</item>
   <item name="android:textSize">24sp</item>
   <item name="android:textStyle">bold</item>
   <item name="android:layout_marginBottom">6dp</item>
   <item name="android:paddingLeft">8dp</item>
</style>

Add a layout/recyclerview_item.xml layout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/textView"
        style="@style/word_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@android:color/holo_orange_light" />
</LinearLayout>

In layout/content_main.xml, replace the TextView with a RecyclerView:

<android.support.v7.widget.RecyclerView
   android:id="@+id/recyclerview"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:background="@android:color/darker_gray"
   tools:listitem="@layout/recyclerview_item" />

Your floating action button (FAB) should correspond to the available action. In the layout/activity_main.xml file, give the FloatingActionButton a + symbol icon:

  1. In the layout/activity_main.xml file, select File > New > Vector Asset.
  2. Select Material Icon.
  3. Click the Android robot icon in the Icon: field, then select the + ("add") asset.
  4. Change the layout file code as follows.
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 codelab assumes that you know how RecyclerView, RecyclerView.LayoutManager, RecyclerView.ViewHolder, and RecyclerView.Adapter work.

Note that the mWords variable in the adapter caches the data. In the next task, you add the code that updates the data automatically.

Also note that 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.

Add a class WordListAdapter that extends RecyclerView.Adapter. Here is the code.

public class WordListAdapter extends RecyclerView.Adapter<WordListAdapter.WordViewHolder> {

   class WordViewHolder extends RecyclerView.ViewHolder {
       private final TextView wordItemView;

       private WordViewHolder(View itemView) {
           super(itemView);
           wordItemView = itemView.findViewById(R.id.textView);
       }
   }

   private final LayoutInflater mInflater;
   private List<Word> mWords; // Cached copy of words

   WordListAdapter(Context context) { mInflater = LayoutInflater.from(context); }

   @Override
   public WordViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
       View itemView = mInflater.inflate(R.layout.recyclerview_item, parent, false);
       return new WordViewHolder(itemView);
   }

   @Override
   public void onBindViewHolder(WordViewHolder holder, int position) {
       if (mWords != null) {
           Word current = mWords.get(position);
           holder.wordItemView.setText(current.getWord());
       } else {
           // Covers the case of data not being ready yet.
           holder.wordItemView.setText("No Word");
       }
   }

   void setWords(List<Word> words){
       mWords = words;
       notifyDataSetChanged();
   }

   // getItemCount() is called many times, and when it is first called,
   // mWords has not been updated (means initially, it's null, and we can't return null).
   @Override
   public int getItemCount() {
       if (mWords != null)
           return mWords.size();
       else return 0;
   }
}

Add the RecyclerView in the onCreate() method of MainActivity.

In the onCreate() method:

RecyclerView recyclerView = findViewById(R.id.recyclerview);
final WordListAdapter adapter = new WordListAdapter(this);
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this));

Run your app to make sure everything works. There are no items, because you have not hooked up the data yet, so the app should display a gray background without any list items.

There is no data in the database. You will add data in two ways: Add some data when the database is opened, and add an Activity for adding words.

To delete all content and repopulate the database whenever the app is started, you create a RoomDatabase.Callback and override onOpen(). Because you cannot do Room database operations on the UI thread, onOpen() creates and executes an AsyncTask to add content to the database.

Here is the code for creating the callback in the WordRoomDatabase class:

private static RoomDatabase.Callback sRoomDatabaseCallback = 
    new RoomDatabase.Callback(){

    @Override
    public void onOpen (@NonNull SupportSQLiteDatabase db){
        super.onOpen(db);
       new PopulateDbAsync(INSTANCE).execute();
   }
};

Here is the code for the AsyncTask that deletes the contents of the database, then populates it with the two words "Hello" and "World". Feel free to add more words!

private static class PopulateDbAsync extends AsyncTask<Void, Void, Void> {

   private final WordDao mDao;

   PopulateDbAsync(WordRoomDatabase db) {
       mDao = db.wordDao();
   }

   @Override
   protected Void doInBackground(final Void... params) {
       mDao.deleteAll();
       Word word = new Word("Hello");
       mDao.insert(word);
       word = new Word("World");
       mDao.insert(word);
       return null;
   }
}

Finally, add the callback to the database build sequence right before calling .build().

.addCallback(sRoomDatabaseCallback)

Add these string resources in values/strings.xml:

<string name="hint_word">Word...</string>
<string name="button_save">Save</string>
<string name="empty_not_saved">Word not saved because it is empty.</string>

Add this color resource in value/colors.xml:

<color name="buttonLabel">#d3d3d3</color>

Add these dimension resources in values/dimens.xml:

<dimen name="small_padding">6dp</dimen>
<dimen name="big_padding">16dp</dimen>

Create the activity_new_word.xml file in the layout folder:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:orientation="vertical" android:layout_width="match_parent"
   android:layout_height="match_parent">

   <EditText
       android:id="@+id/edit_word"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:fontFamily="sans-serif-light"
       android:hint="@string/hint_word"
       android:inputType="textAutoComplete"
       android:padding="@dimen/small_padding"
       android:layout_marginBottom="@dimen/big_padding"
       android:layout_marginTop="@dimen/big_padding"
       android:textSize="18sp" />

   <Button
       android:id="@+id/button_save"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:background="@color/colorPrimary"
       android:text="@string/button_save"
       android:textColor="@color/buttonLabel" />

</LinearLayout>

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>

Here is the code for the activity:

public class NewWordActivity extends AppCompatActivity {

   public static final String EXTRA_REPLY = "com.example.android.wordlistsql.REPLY";

   private  EditText mEditWordView;

   @Override
   public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_new_word);
       mEditWordView = findViewById(R.id.edit_word);

       final Button button = findViewById(R.id.button_save);
       button.setOnClickListener(new View.OnClickListener() {
           public void onClick(View view) {
               Intent replyIntent = new Intent();
               if (TextUtils.isEmpty(mEditWordView.getText())) {
                   setResult(RESULT_CANCELED, replyIntent);
               } else {
                   String word = mEditWordView.getText().toString();
                   replyIntent.putExtra(EXTRA_REPLY, word);
                   setResult(RESULT_OK, replyIntent);
               }
               finish();
           }
       });
   }
}

The final step is to connect the UI to the database by saving new words the user enters and displaying the current contents of the word database in the RecyclerView.

To display the current contents of the database, add an observer that observes the LiveData in the ViewModel. Whenever the data changes, the onChanged() callback is invoked, which calls the adapter's setWord() method to update the adapter's cached data and refresh the displayed list.

In MainActivity, create a member variable for the ViewModel:

private WordViewModel mWordViewModel;

Use ViewModelProviders to associate your ViewModel with your UI controller. When your app first starts, the ViewModelProviders will create the ViewModel. When the activity is destroyed, for example through a configuration change, the ViewModel persists. When the activity is re-created, the ViewModelProviders return the existing ViewModel. See ViewModel.

In onCreate(), get a ViewModel from the ViewModelProvider.

mWordViewModel = ViewModelProviders.of(this).get(WordViewModel.class);

Also in onCreate(), add an observer for the LiveData returned by getAllWords().
The onChanged() method fires when the observed data changes and the activity is in the foreground.

mWordViewModel.getAllWords().observe(this, new Observer<List<Word>>() {
   @Override
   public void onChanged(@Nullable final List<Word> words) {
       // Update the cached copy of the words in the adapter.
       adapter.setWords(words);
   }
});

In MainActivity, add the onActivityResult() code for the NewWordActivity.

If the activity returns with RESULT_OK, insert the returned word into the database by calling the insert() method of the WordViewModel.

public void onActivityResult(int requestCode, int resultCode, Intent data) {
   super.onActivityResult(requestCode, resultCode, data);

   if (requestCode == NEW_WORD_ACTIVITY_REQUEST_CODE && resultCode == RESULT_OK) {
       Word word = new Word(data.getStringExtra(NewWordActivity.EXTRA_REPLY));
       mWordViewModel.insert(word);
   } else {
       Toast.makeText(
               getApplicationContext(),
               R.string.empty_not_saved,
               Toast.LENGTH_LONG).show();
   }
}

Define the missing request code:

public static final int NEW_WORD_ACTIVITY_REQUEST_CODE = 1;

In MainActivity,start NewWordActivity when the user taps the FAB. Replace the code in the FAB's onClick() click handler with this code:

Intent intent = new Intent(MainActivity.this, NewWordActivity.class);
startActivityForResult(intent, NEW_WORD_ACTIVITY_REQUEST_CODE);

RUN YOUR APP!!!

When you add a word to the database in NewWordActivity, the UI will automatically update.

Now that you have a working app, let's recap what you've built. Here is the app structure again, from the beginning.

You have an app that displays words in a list (MainActivity, RecyclerView, WordListAdapter).

You can add words to the list (NewWordActivity).

A word is an instance of the Word entity class.

The words are cached in the RecyclerViewAdapter as a List of words (mWords). This list of words automatically updates and redisplays when the words in the database change.

Flow of Data for Automatic UI Updates (Reactive UI)

The automatic update is possible because we are using LiveData. In the MainActivity, there is an Observer that observes the words LiveData from the database and is notified when they 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's getAllWords() method.

The WordViewModel hides everything about the backend from the UI layer. It provides methods for accessing the data layer, and it returns LiveData so that MainActivity can set up the observer relationship. Views and 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.

The Repository manages one or more data sources. In the WordListSample app, that backend is a Room database. Room is a wrapper around and implements a 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 do with an SQLiteOpenHelper class.

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.

Because the result returned from the query is observed LiveData, every time the data in Room changes, the Observer interface's onChanged() method is executed and the UI updated.

[Optional] Download the solution code

Click the following link to download the solution code for this codelab:

Download source code

Unpack the downloaded zip file. This will unpack a root folder, android-room-with-a-view-master, which contains the complete app. The app is highly modular, so there is not a separate folder for each step.