Showing posts with label Android Room Architecture. Show all posts

Room Android Architecture

Android Mads

In Google I/O 2017, Google announced about Room Architecture for Android. This Architecture is used to maintain the State of Android Application when the orientation changes. As well as google announced about Room Architecture.

Room

We have more boiler plates while creating SQLite Database in Android even it is small. Room as a library used to remove the boiler plates like Cursors & Handlers and database can be handled with annotations and model classes. If we remember about Sugar ORM or Active Android, the same approach is dealt with Room. 
We don't want to go for any third party libraries, when the official Android libraries give you an equal, or better solution.

Life Cycle Activity

We have faced the problem mostly as that to maintain the State of Android Application when the orientation changes. The Life Cycle Activity used to handle the state easily.

Coding Part

Create a new project in Android Studio.

First, Add Google’s maven repository to your project-level build.gradle file.
allprojects {
    repositories {
        jcenter()
        maven { url 'https://maven.google.com' }
    }
}
Then, Add following dependencies to your app-level build.gradle file.
compile 'android.arch.persistence.room:runtime:1.0.0-alpha1'
annotationProcessor 'android.arch.persistence.room:compiler:1.0.0-alpha1'
compile 'android.arch.lifecycle:extensions:1.0.0-alpha1'

Creating the Model

Create a Model class and named as ProductModel.
@Entity
public class ProductModel {

    @PrimaryKey(autoGenerate = true)
    public int itemId;
    private String itemName;
    private String itemQty;
    @TypeConverters(DateConverter.class)
    private Date itemAddedDate;

    public ProductModel(int itemId, String itemName, String itemQty, Date itemAddedDate) {
        this.itemId = itemId;
        this.itemName = itemName;
        this.itemQty = itemQty;
        this.itemAddedDate = itemAddedDate;
    }

    public void setItemName(String itemName) {
        this.itemName = itemName;
    }

    public void setItemQty(String itemQty) {
        this.itemQty = itemQty;
    }

    public void setItemAddedDate(Date itemAddedDate) {
        this.itemAddedDate = itemAddedDate;
    }

    public String getItemName() {
        return itemName;
    }

    public String getItemQty() {
        return itemQty;
    }

    public Date getItemAddedDate() {
        return itemAddedDate;
    }

    public int getItemId() {
        return itemId;
    }
}
Here,
  1. @Entity annotation is used to tell the Model Class as Database Table. 
  2. @PrimaryKey annotation is used to set Primary Key for Table and autoGenerate = true is used to set Auto Increment to Primary Key. 
  3. @TypeConverters annotation is used to convert the Date into String and Vice-Versa. The DateConverter is class created by your own as like below.

Creating Type Converter

Create a class and named as DateConverter and Paste the following code.
class DateConverter {

    @TypeConverter
    public static Date toDate(Long timestamp) {
        return timestamp == null ? null : new Date(timestamp);
    }

    @TypeConverter
    public static Long toTimestamp(Date date) {
        return date == null ? null : date.getTime();
    }
}
This Converter is used to convert date to string and vice versa. Because, We cannot save Date format in SQLite Directly.

Creating Data Access Object(DAO)

Create a class and named as ProductModelDao.class and paste the following code.
Here, the Query for storing and retrieving data from Local DB performed.
@Dao
@TypeConverters(DateConverter.class)
public interface ProductModelDao {
    
    @Query("select * from ProductModel")
    LiveData<List<ProductModel>> getAllProducts();

    @Query("select * from ProductModel where itemId = :itemId")
    ProductModel getProductById(int itemId);

    @Insert(onConflict = REPLACE)
    void addProduct(ProductModel ProductModel);

    @Update(onConflict = REPLACE)
    void updateProduct(ProductModel ProductModel);

    @Delete
    void deleteProduct(ProductModel ProductModel);
    
}
  1. @Dao annotation indicate this interface as DAO. 
  2. @Query annotation indicate the data surrounded is Queries to retrieve data from DB. 
  3. @Insert, @Update, @Delete,annotations used to insert, update and delete the data stored in DB respectively. 
  4. onConflict indicates that to replace the data when conflicts occurs while performing the tasks..

Creating Database

Create a abstract class and named as AppDataBase.class and pass the following code.
@Database(entities = {ProductModel.class}, version = 1)
public abstract class AppDataBase extends RoomDatabase {
    private static AppDataBase INSTANCE;

    public static AppDataBase getDatabase(Context context) {
        if (INSTANCE == null) {
            INSTANCE = Room.databaseBuilder(context.getApplicationContext(), AppDataBase.class, "product_db")
                    .build();
        }
        return INSTANCE;
    }

    public static void destroyInstance() {
        INSTANCE = null;
    }

    public abstract ProductModelDao itemAndPersonModel();
}
  1. @Database annotation indicate this class as Database of our Application. 
  2. entities is an array of tables or entities and separated by comma. 
  3. version is used to denote the version of the database.
This class is used to create the database and get an instance of it. We can create the database using
Room.databaseBuilder(context.getApplicationContext(), AppDataBase.class, "product_db")
.build();
Create Android View Model for Retrieving all the data from DB.
public class ProductListViewModel extends AndroidViewModel {

    private final LiveData<List<ProductModel>> itemAndPersonList;
    private AppDataBase appDatabase;

    public ProductListViewModel(Application application) {
        super(application);
        appDatabase = AppDataBase.getDatabase(this.getApplication());
        itemAndPersonList = appDatabase.itemAndPersonModel().getAllProducts();
    }

    public LiveData<List<ProductModel>> getItemAndPersonList() {
        return itemAndPersonList;
    }

    public void deleteItem(ProductModel borrowModel) {
        new deleteAsyncTask(appDatabase).execute(borrowModel);
    }

    private static class deleteAsyncTask extends AsyncTask<ProductModel, Void, Void> {

        private AppDataBase db;
        deleteAsyncTask(AppDataBase appDatabase) {
            db = appDatabase;
        }
        @Override
        protected Void doInBackground(final ProductModel... params) {
            db.itemAndPersonModel().deleteProduct(params[0]);
            return null;
        }

    }

}
Create Android View Model for Retrieve a single data from DB as well as the code update the Data.
public class AddProductViewModel extends AndroidViewModel {

    private AppDataBase appDatabase;

    public AddProductViewModel(Application application) {
        super(application);
        appDatabase = AppDataBase.getDatabase(this.getApplication());
    }

    public void addProduct(final ProductModel borrowModel) {
        new addAsyncTask(appDatabase).execute(borrowModel);
    }

    private static class addAsyncTask extends AsyncTask {

        private AppDataBase db;

        addAsyncTask(AppDataBase appDatabase) {
            db = appDatabase;
        }

        @Override
        protected Void doInBackground(final ProductModel... params) {
            db.itemAndPersonModel().addProduct(params[0]);
            return null;
        }

    }
}
Create Android View Model for Retrieve a insert the Data.
public class UpdateProductViewModel extends AndroidViewModel {

    private AppDataBase appDatabase;

    public UpdateProductViewModel(Application application) {
        super(application);
        appDatabase = AppDataBase.getDatabase(this.getApplication());
    }

    public ProductModel readProduct(final int itemId) {
        try {
            return new readAsyncTask(appDatabase).execute(itemId).get();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
        return null;
    }

    public void updateProduct(final ProductModel borrowModel) {
        new UpdateProductViewModel.updateAsyncTask(appDatabase).execute(borrowModel);
    }

    private static class updateAsyncTask extends AsyncTask<ProductModel, Void, Void> {

        private AppDataBase db;

        updateAsyncTask(AppDataBase appDatabase) {
            db = appDatabase;
        }

        @Override
        protected Void doInBackground(final ProductModel... params) {
            db.itemAndPersonModel().updateProduct(params[0]);
            return null;
        }

    }

    private static class readAsyncTask extends AsyncTask<Integer, Void, ProductModel> {

        private AppDataBase db;

        readAsyncTask(AppDataBase appDatabase) {
            db = appDatabase;
        }

        @Override
        protected ProductModel doInBackground(final Integer... params) {
            return db.itemAndPersonModel().getProductById(params[0]);
        }
    }
}

Creating Custom Adapter

Create Adapter for Recyclerview and Paste the Following code.
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.RecyclerViewHolder> {

    private List<ProductModel> ProductModelList;
    private View.OnLongClickListener longClickListener;
    private View.OnClickListener clickListener;

    public RecyclerViewAdapter(List<ProductModel> ProductModelList,
                               View.OnLongClickListener longClickListener,
                               View.OnClickListener clickListener) {
        this.ProductModelList = ProductModelList;
        this.longClickListener = longClickListener;
        this.clickListener = clickListener;
    }

    @Override
    public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new RecyclerViewHolder(LayoutInflater.from(parent.getContext())
                .inflate(R.layout.recycler_item, parent, false));
    }

    @Override
    public void onBindViewHolder(final RecyclerViewHolder holder, int position) {
        ProductModel productModel = ProductModelList.get(position);
        holder.itemTextView.setText(productModel.getItemName());
        holder.nameTextView.setText(productModel.getItemQty());
        holder.dateTextView.setText(productModel.getItemAddedDate().toLocaleString().substring(0, 11));
        holder.itemView.setTag(productModel);
        holder.itemView.setOnLongClickListener(longClickListener);
        holder.itemView.setOnClickListener(clickListener);
    }

    @Override
    public int getItemCount() {
        return ProductModelList.size();
    }

    public void addItems(List<ProductModel> ProductModelList) {
        this.ProductModelList = ProductModelList;
        notifyDataSetChanged();
    }

    static class RecyclerViewHolder extends RecyclerView.ViewHolder {
        private TextView itemTextView;
        private TextView nameTextView;
        private TextView dateTextView;

        RecyclerViewHolder(View view) {
            super(view);
            itemTextView = view.findViewById(R.id.itemTextView);
            nameTextView = view.findViewById(R.id.nameTextView);
            dateTextView = view.findViewById(R.id.dateTextView);
        }
    }
}
To use the View models inside our Application, use LifeCycleActivity instead of extending the Activity. and Access the view models by
public class MainActivity extends AppCompatLifeCycleActivity implements View.OnLongClickListener, View.OnClickListener {

    private ProductListViewModel viewModel;
    private RecyclerViewAdapter recyclerViewAdapter;
    private RecyclerView recyclerView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startActivity(new Intent(MainActivity.this, AddActivity.class));
            }
        });
        recyclerView = findViewById(R.id.recyclerView);
        recyclerViewAdapter = new RecyclerViewAdapter(new ArrayList<ProductModel>(), this, this);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));

        recyclerView.setAdapter(recyclerViewAdapter);

        viewModel = ViewModelProviders.of(this).get(ProductListViewModel.class);

        viewModel.getItemAndPersonList().observe(MainActivity.this, new Observer<List<ProductModel>>() {
            @Override
            public void onChanged(@Nullable List<ProductModel> itemAndPeople) {
                recyclerViewAdapter.addItems(itemAndPeople);
            }
        });

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        AppDataBase.destroyInstance();
    }

    @Override
    public boolean onLongClick(View v) {
        ProductModel productModel = (ProductModel) v.getTag();
        viewModel.deleteItem(productModel);
        return true;
    }

    @Override
    public void onClick(View v) {
        ProductModel productModel = (ProductModel) v.getTag();
        Intent i = new Intent(MainActivity.this, UpdateActivity.class);
        i.putExtra("itemId",productModel.itemId);
        startActivity(i);
    }
}
Here, AppCompatLifeCycleActivity is custom Activity inherited with AppCompatActivity and LifeCycleActivity's feature. Create a class and Named as AppCompatLifeCycleActivity.class and paste the following code.
public class AppCompatLifeCycleActivity extends AppCompatActivity 
                   implements LifecycleRegistryOwner {

    private final LifecycleRegistry mRegistry = new LifecycleRegistry(this);

    @Override
    public LifecycleRegistry getLifecycle() {
        return mRegistry;
    }
}
I did this, because of we cannot use setSupportActionbaras like in AppCompatActivity with NoActionBar Theme. You Can simply use LifeCycleActivitywith WithActionBar Themes.
I have added the Add Product and Update Product Activity Screens in the Samples. You Download in the Download Section. If you have any doubt regarding this, feel free to comment in the comment section.

Download Code

You can download the full source code for this tutorial from the following Github link. If you Like this tutorial, Please star it in Github.

Download From Github