I am using Retrofit2 and Rxjava2 in my android app as a networking library and NodeJS and MongoDB as a backend service.I want to fetch data from server and store data in room database in order to if user open app again it fetches data from room and not from server until some new data is added on server.
So far I have successfully fetched data from server and showing it in recycler view.
What I want to achieve:
1) Store data in room database after fetching from server.
2) Show data from room database until some new data updated on server.
This is my code below:
ApiService.java
public interface ApiService {
@POST("retrofitUsers")
@FormUrlEncoded
Observable<String> saveData(@Field("name") String name,
@Field("age") String age);
@GET("getUsers")
Observable<List<BioData>> getData();
}
RetrofitClient.java
public class RetrofitClient {
private static Retrofit retrofit = null;
public static Retrofit getInstance(){
if(retrofit == null)
retrofit = new Retrofit.Builder()
.baseUrl("https://bookbudiapp.herokuapp.com/")
.addConverterFactory(GsonConverterFactory.create(new GsonBuilder().setLenient().create()))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
return retrofit;
}
private RetrofitClient(){
}
}
BioData.java
public class BioData {
@SerializedName("name")
@Expose
private String name;
@SerializedName("age")
@Expose
private String age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
MainActivity.java
public class Users extends AppCompatActivity {
RecyclerView recycle;
UserAdapter adapter;
List<BioData> list;
CompositeDisposable compositeDisposable;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recycle = findViewById(R.id.recycle);
recycle.setHasFixedSize(true);
recycle.setLayoutManager(new LinearLayoutManager(this));
list = new ArrayList<>();
compositeDisposable = new CompositeDisposable();
fetchData();
}
private void fetchData(){
Retrofit retrofit = RetrofitClient.getInstance();
ApiService myApi = retrofit.create(ApiService.class);
Disposable disposable = myApi.getData().subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<List<BioData>>() {
@Override
public void accept(List<BioData> bioData) throws Exception {
adapter = new UserAdapter(bioData,getApplicationContext());
recycle.setAdapter(adapter);
}
});
compositeDisposable.add(disposable);
}
@Override
protected void onStop() {
super.onStop();
compositeDisposable.clear();
}
}
How can I add Room database in my app let me know I have no idea of it any help would be appreciated.
THANKS
Android developers has a good start tutorial for Room: https://developer.android.com/training/data-storage/room/index.html
For the functionallity you want to add would be good for you to use Repository Pattern. To keep it simple, the Repository Pattern is like a class between the app and the server where you ask some data (for example user name) and the app doesn't know where that data is coming from (database or server). The repository the will do something like this:
class UserRepository {
public User getUser() {
User user = db.userDao().getUser() //Room sintax
if(user==null){
//access to server and get user object
db.userDao().insert(user)
}
return db.userDao().getUser()
}
}
This allows the app to decouple, and if you for example want to change server in a future, you would only have to change repository classes and the rest of the app will be the same. I recommend you to investigate it. You also should use an interface that the repository sould implement, this decouple a bit more
Room Basics
The Room library acts as an abstract layer for underlying SQLite database. Thus, Room annotations are used:
There are 3 major components in Room
Entity : A class annotated with the @Entity annotation is mapped to a table in database. Every entity is persisted in its own table and every field in class represents the column name.
tableName attribute is used to define the name of the table Every entity class must have at-least one Primary Key field, annotated with @PrimaryKey Fields in entity class can be annotated with @ColumnInfo(name = “name_of_column”) annotation to give specific column names
DAO : Data Access Object is either be an interface or an abstract class annotated with @Doa annotation, containing all the methods to define the operations to be performed on data. The methods can be annotated with
@Query to retrieve data from database
@Insert to insert data into database
@Delete to delete data from database
@Update to update data in database
Add these dependencies :
dependencies {
// Room dependencies
compile 'android.arch.persistence.room:runtime:1.0.0'
annotationProcessor 'android.arch.persistence.room:compiler:1.0.0'
}
Create Entity
Before creating a database, Let's create an Entity, named as Note and later, Objects of this class will be added to database.
@Entity
public class Note {
@PrimaryKey(autoGenerate = true)
private int note_id;
@ColumnInfo(name = "note_content") // column name will be "note_content" instead of "content" in table
private String content;
private String title;
private
public Note(int note_id, String content, String title) {
this.note_id = note_id;
this.content = content;
this.title = title;
}
public int getNote_id() {
return note_id;
}
public void setNote_id(int note_id) {
this.note_id = note_id;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Note)) return false;
Note note = (Note) o;
if (note_id != note.note_id) return false;
return title != null ? title.equals(note.title) : note.title == null;
}
@Override
public int hashCode() {
int result = note_id;
result = 31 * result + (title != null ? title.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "Note{" +
"note_id=" + note_id +
", content='" + content + '\'' +
", title='" + title + '\'' +
'}';
}}
Creating DAOs
DAOs define all methods to access database, annotated with @Dao annotation. The DAO acts as a contract to perform CRUD operations on data within a database.
@Dao
public interface NoteDao {
@Query("SELECT * FROM user "+ Constants.TABLE_NAME_NOTE)
List<Note> getAll();
/*
* Insert the object in database
* @param note, object to be inserted
*/
@Insert
void insert(Note note);
/*
* update the object in database
* @param note, object to be updated
*/
@Update
void update(Note repos);
/*
* delete the object from database
* @param note, object to be deleted
*/
@Delete
void delete(Note note);
/*
* delete list of objects from database
* @param note, array of objects to be deleted
*/
@Delete
void delete(Note... note); // Note... is varargs, here note is an array
}
Create Database
Now, we have table defined as Entity and CRUD methods defined via NoteDao. The last piece of the database puzzle is the database itself.
@Database(entities = { Note.class }, version = 1)
public abstract class NoteDatabase extends RoomDatabase {
public abstract NoteDao getNoteDao();
private static NoteDatabase noteDB;
public static NoteDatabase getInstance(Context context) {
if (null == noteDB) {
noteDB = buildDatabaseInstance(context);
}
return noteDB;
}
private static NoteDatabase buildDatabaseInstance(Context context) {
return Room.databaseBuilder(context,
NoteDatabase.class,
Constants.DB_NAME)
.allowMainThreadQueries().build();
}
public void cleanUp(){
noteDB = null;
}
}
Implement Database Interactions
The below snippet will demonstrate the working of insert, update, and delete functionality using the Room database.
public class AddNoteActivity extends AppCompatActivity {
private TextInputEditText et_title,et_content;
private NoteDatabase noteDatabase;
private Note note;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_note);
et_title = findViewById(R.id.et_title);
et_content = findViewById(R.id.et_content);
noteDatabase = NoteDatabase.getInstance(AddNoteActivity.this);
Button button = findViewById(R.id.but_save);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// fetch data and create note object
note = new Note(et_content.getText().toString(),
et_title.getText().toString());
// create worker thread to insert data into database
new InsertTask(AddNoteActivity.this,note).execute();
}
});
}
private void setResult(Note note, int flag){
setResult(flag,new Intent().putExtra("note",note));
finish();
}
private static class InsertTask extends AsyncTask<Void,Void,Boolean> {
private WeakReference<AddNoteActivity> activityReference;
private Note note;
// only retain a weak reference to the activity
InsertTask(AddNoteActivity context, Note note) {
activityReference = new WeakReference<>(context);
this.note = note;
}
// doInBackground methods runs on a worker thread
@Override
protected Boolean doInBackground(Void... objs) {
activityReference.get().noteDatabase.getNoteDao().insertNote(note);
return true;
}
// onPostExecute runs on main thread
@Override
protected void onPostExecute(Boolean bool) {
if (bool){
activityReference.get().setResult(note,1);
}
}
}
}
Retrieve And Display NoteList
public class NoteListActivity extends AppCompatActivity implements NotesAdapter.OnNoteItemClick{
private TextView textViewMsg;
private RecyclerView recyclerView;
private NoteDatabase noteDatabase;
private List<Note> notes;
private NotesAdapter notesAdapter;
private int pos;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initializeVies();
displayList();
}
private void displayList(){
// initialize database instance
noteDatabase = NoteDatabase.getInstance(NoteListActivity.this);
// fetch list of notes in background thread
new RetrieveTask(this).execute();
}
private static class RetrieveTask extends AsyncTask<Void,Void,List<Note>>{
private WeakReference<NoteListActivity> activityReference;
// only retain a weak reference to the activity
RetrieveTask(NoteListActivity context) {
activityReference = new WeakReference<>(context);
}
@Override
protected List<Note> doInBackground(Void... voids) {
if (activityReference.get()!=null)
return activityReference.get().noteDatabase.getNoteDao().getNotes();
else
return null;
}
@Override
protected void onPostExecute(List<Note> notes) {
if (notes!=null && notes.size()>0 ){
activityReference.get().notes = notes;
// hides empty text view
activityReference.get().textViewMsg.setVisibility(View.GONE);
// create and set the adapter on RecyclerView instance to display list
activityReference.get().notesAdapter = new NotesAdapter(notes,activityReference.get());
activityReference.get().recyclerView.setAdapter(activityReference.get().notesAdapter);
}
}
}
private void initializeVies(){
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
textViewMsg = (TextView) findViewById(R.id.tv\_\_empty);
// Action button to add note
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(listener);
recyclerView = findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(NoteListActivity.this));
}
}
Update Note
public class AddNoteActivity extends AppCompatActivity {
private TextInputEditText et_title,et_content;
private NoteDatabase noteDatabase;
private Note note;
private boolean update;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_note);
et_title = findViewById(R.id.et_title);
et_content = findViewById(R.id.et_content);
noteDatabase = NoteDatabase.getInstance(AddNoteActivity.this);
Button button = findViewById(R.id.but_save);
if ( (note = (Note) getIntent().getSerializableExtra("note"))!=null ){
getSupportActionBar().setTitle("Update Note");
update = true;
button.setText("Update");
et_title.setText(note.getTitle());
et_content.setText(note.getContent());
}
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
note.setContent(et_content.getText().toString());
note.setTitle(et_title.getText().toString());
noteDatabase.getNoteDao().updateNote(note);
}
});
}
}
Delete Note
noteDatabase.getNoteDao().deleteNote(notes.get(pos));
adapterObj.notifyDataSetChanged();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With