Android Architecture Components


Android Architecture Components is new collection of libraries that help you design robust, testable, and maintainable apps. Main parts are: Lifecycles, ViewModel, LiveData, Room.

Add Architecture Components

Project build.gradle

allprojects {
    repositories {
        // Add this if you use Gradle 4.0+
        // Add this if you use Gradle < 4.0
        maven { url '' }

ext {
    archVersion = '1.0.0-alpha5'

Application build gradle

// For Lifecycles, LiveData, and ViewModel
compile "android.arch.lifecycle:runtime:$archVersion"
compile "android.arch.lifecycle:extensions:$archVersion"
annotationProcessor "android.arch.lifecycle:compiler:$archVersion"

// For Room
compile "$archVersion"
annotationProcessor "$archVersion"

// For testing Room migrations
testCompile "$archVersion"

// For Room RxJava support
compile "$archVersion"

Custom Lifecycle-aware component

Each UI component lifecycle changed as shown at image. lifecycle state diagram You may create component, that will be notified on lifecycle state change:

public class MyLocationListener implements LifecycleObserver {
    private boolean enabled = false;
    private Lifecycle lifecycle;
    public MyLocationListener(Context context, Lifecycle lifecycle, Callback callback) {

    void start() {
        if (enabled) {
           // connect

    public void enable() {
        enabled = true;
        if (lifecycle.getState().isAtLeast(STARTED)) {
            // connect if not connected

    void stop() {
        // disconnect if connected

Custom LiveData

You may write custom LiveData, if you need custom logic.
Don't write custom class, if you only need to transform data (use Transformations class)

public class LocationLiveData extends LiveData<Location> {
    private LocationManager locationManager;
    private LocationListener listener = new LocationListener() {
        public void onLocationChanged(Location location) {

        public void onStatusChanged(String provider, int status, Bundle extras) {
            // Do something

        public void onProviderEnabled(String provider) {
            // Do something

        public void onProviderDisabled(String provider) {
            // Do something

    public LocationLiveData(Context context) {
        locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);

    protected void onActive() {
        // We have observers, start working
        locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, listener);

    protected void onInactive() {
        // We have no observers, stop working

Room peristence

Room require four parts: Database class, DAO classes, Entity classes and Migration classes (now you may use only DDL methods):

Entity classes

// Set custom table name, add indexes
@Entity(tableName = "videos",
        indices = {@Index("title")}
public final class VideoItem {
    @PrimaryKey // required
    public long articleId;
    public String title;
    public String url;

// Use ForeignKey for setup table relation
@Entity(tableName = "tags",
        indices = {@Index("score"), @Index("videoId"), @Index("value")},
        foreignKeys = @ForeignKey(entity = VideoItem.class,
                parentColumns = "articleId",
                childColumns = "videoId",
                onDelete = ForeignKey.CASCADE)
public final class VideoTag {
    public long id;
    public long videoId;
    public String displayName;
    public String value;
    public double score;

DAO classes

public interface VideoDao {
    // Create insert with custom conflict strategy
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void saveVideos(List<VideoItem> videos);

    // Simple update
    void updateVideos(VideoItem... videos);

    @Query("DELETE FROM tags WHERE videoId = :videoId")
    void deleteTagsByVideoId(long videoId);

    // Custom query, you may use select/delete here
    @Query("SELECT v.* FROM tags t LEFT JOIN videos v ON v.articleId = t.videoId WHERE t.value = :tag ORDER BY updatedAt DESC LIMIT :limit")
    LiveData<List<VideoItem>> getVideosByTag(String tag, int limit);

Database class

// register your entities and DAOs
@Database(entities = {VideoItem.class, VideoTag.class}, version = 2)
public abstract class ContentDatabase extends RoomDatabase {
    public abstract VideoDao videoDao();


public final class Migrations {
    private static final Migration MIGRATION_1_2 = new Migration(1, 2) {
        public void migrate(SupportSQLiteDatabase database) {
            final String[] sqlQueries = {
                            " `videoId` INTEGER, `displayName` TEXT, `value` TEXT, `score` REAL," +
                            " FOREIGN KEY(`videoId`) REFERENCES `videos`(`articleId`)" +
                            " ON UPDATE NO ACTION ON DELETE CASCADE )",
                    "CREATE  INDEX `index_tags_score` ON `tags` (`score`)",
                    "CREATE  INDEX `index_tags_videoId` ON `tags` (`videoId`)"};
            for (String query : sqlQueries) {

    public static final Migration[] ALL = {MIGRATION_1_2};

    private Migrations() {

Use in Application class or provide via Dagger

ContentDatabase provideContentDatabase() {
    return Room.databaseBuilder(context, ContentDatabase.class, "data.db")

Write your repository:

public final class ContentRepository {
    private final ContentDatabase db;
    private final VideoDao videoDao;

    public ContentRepository(ContentDatabase contentDatabase, VideoDao videoDao) {
        this.db = contentDatabase;
        this.videoDao = videoDao;

    public LiveData<List<VideoItem>> getVideoByTag(@Nullable String tag, int limit) {
        // you may fetch from network, save to database
        return videoDao.getVideosByTag(tag, limit);

Use in ViewModel:

ContentRepository contentRepository = ...;
contentRepository.getVideoByTag(tag, limit);

Using Lifecycle in AppCompatActivity

Extend your activity from this activity

public abstract class BaseCompatLifecycleActivity extends AppCompatActivity implements LifecycleRegistryOwner {
    // We need this class, because LifecycleActivity extends FragmentActivity not AppCompatActivity

    private final LifecycleRegistry lifecycleRegistry = new LifecycleRegistry(this);

    public LifecycleRegistry getLifecycle() {
        return lifecycleRegistry;

ViewModel with LiveData transformations

public class BaseViewModel extends ViewModel {
    private static final int TAG_SEGMENT_INDEX = 2;
    private static final int VIDEOS_LIMIT = 100;

    // We save input params here
    private final MutableLiveData<Pair<String, String>> urlWithReferrerLiveData = new MutableLiveData<>();

    // transform specific uri param to "tag"
    private final LiveData<String> currentTagLiveData =, pair -> {
        Uri uri = Uri.parse(pair.first);
        List<String> segments = uri.getPathSegments();
        if (segments.size() > TAG_SEGMENT_INDEX)
            return segments.get(TAG_SEGMENT_INDEX);
        return null;

    // transform "tag" to videos list
    private final LiveData<List<VideoItem>> videoByTagData = Transformations.switchMap(currentTagLiveData, tag -> contentRepository.getVideoByTag(tag, VIDEOS_LIMIT));

    ContentRepository contentRepository;

    public BaseViewModel() {
        // some inits

    public void setUrlWithReferrer(String url, String referrer) {
        // set value activates observers and transformations
        urlWithReferrerLiveData.setValue(new Pair<>(url, referrer));

    public LiveData<List<VideoItem>> getVideoByTagData() {
        return videoByTagData;

Somewhere in UI:

public class VideoActivity extends BaseCompatLifecycleActivity {
    private VideoViewModel viewModel;

    protected void onCreate(Bundle savedInstanceState) {

        // Get ViewModel
        viewModel = ViewModelProviders.of(this).get(BaseViewModel.class);
        // Add observer
        viewModel.getVideoByTagData().observe(this, data -> {
            // some checks

        if (savedInstanceState == null) {
            // init loading only at first creation
            // you just set params and 
            viewModel.setUrlWithReferrer(url, referrer);