How to use ExecutorService with Android Room? - android

I am using ExecutorService with Android Room to avoid multiple in insetAsyncTasks. I was using Debanjan example from this post: link but I modified it a bit.
Problem is that I don't get any exception but nothing is inserted.
DaoAsyncProcessor:
public abstract class DaoAsyncProcessor<T> {
public interface DaoProcessCallback<T>{
void onResult(T result);
}
private DaoProcessCallback daoProcessCallback;
public DaoAsyncProcessor(DaoProcessCallback daoProcessCallback) {
this.daoProcessCallback = daoProcessCallback;
}
protected abstract T doAsync();
public void start(){
new DaoProcessAsyncTask().execute();
}
private class DaoProcessAsyncTask extends AsyncTask<Void, Void, T> {
#Override
protected T doInBackground(Void... params) {
return doAsync();
}
#Override
protected void onPostExecute(T t) {
if(daoProcessCallback != null)
daoProcessCallback.onResult(t);
}
}
}
Repository:
public void createUpdate(final Projects item){
EncertDatabase.databaseWriteExecutor.execute(() ->{
if(item!=null){
if(item.getId()==null){
encertDAO.insert(item);
}
else {
encertDAO.update(item);
}
}else {
Log.e("Krkec", "Item was null");
}
});
}
Database:
public abstract class EncertDatabase extends RoomDatabase {
private static volatile EncertDatabase INSTANCE;
public abstract EncertDAO encertDAO();
private static final int NUMBER_OF_THREADS = 4;
public static final ExecutorService databaseWriteExecutor =
Executors.newFixedThreadPool(NUMBER_OF_THREADS);
public static EncertDatabase getDatabase(final Context context){
if(INSTANCE== null){
synchronized (EncertDatabase.class){
if (INSTANCE ==null){
INSTANCE = Room.databaseBuilder(context.getApplicationContext(), EncertDatabase.class,"encertDatabase").fallbackToDestructiveMigration().build();
}
}
}
return INSTANCE;
}
}
What am I doing wrong?

assign executor service instance to db builder instance using setQueryExecutor

Related

How should I code if i want Query specific the with Room? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
I am now learing how to save data in a local database using Room in Android Dev Doc. And I had done the Codelab in this links.https://developer.android.com/codelabs/android-room-with-a-view#0 I can Upddate, Insert, Delete and Query all data in table.
Now, I want Query the data with specific id. How should I code in ViewModel and Repository class? Thanks.
My table named "Diary",
Diary.class
#Entity(tableName = "diary_table")
public class Diary {
#PrimaryKey(autoGenerate = true)
private int id;
#ColumnInfo(name = "diary_title")
private String diary_Title;
#ColumnInfo(name = "diary_content")
private String diary_Content;
public Diary(#NonNull String diary_Title, String diary_Content) {
this.diary_Title = diary_Title;
this.diary_Content = diary_Content;
}
public void setDiary_Title(String diary_Title) {
this.diary_Title = diary_Title;
}
public void setDiary_Content(String diary_Content) {
this.diary_Content = diary_Content;
}
public void setId(int id) {
this.id = id;
}
public int getId() {
return id;
}
public String getStrId() {
return String.valueOf(id);
}
public String getDiary_Title() {
return diary_Title;
}
public String getDiary_Content() {
return diary_Content;
}
}
DiaryDao.class
#Dao
public interface DiaryDao {
#Insert
void insertDiaries(Diary... diaries);
#Update
void updateDiaries(Diary... diaries);
#Delete
void deleteDiaries(Diary... diaries);
//删除到一无所有 慎用!
#Query("DELETE FROM diary_table")
void deleteAllDiaries();
#Query("SELECT * FROM diary_table ORDER BY ID ASC")
LiveData<List<Diary>>getAllDiariesLive();//get all diary
#Query("SELECT * FROM diary_table WHERE ID=:id")
LiveData<Diary> getSpecificDiariesLive(int id);//get specific diary
}
DiaryRepository
class DiaryRepository {
private LiveData<Diary> specificDiary;
private LiveData<List<Diary>> allDiariesLive;
private DiaryDao diaryDao;
DiaryRepository(Context context) {
DiaryRoomDatabase diaryRoomDatabase = DiaryRoomDatabase.getDiaryDatabase(context.getApplicationContext());
diaryDao = diaryRoomDatabase.getDiaryDao();
allDiariesLive = diaryDao.getAllDiariesLive();
specificDiary = diaryDao.getSpecificDiariesLive(/**how should i code here***/);
}
void insetDiaries(Diary... diaries) {
new InsertAsyncTask(diaryDao).execute(diaries);
}
void updateDiaries(Diary... diaries) {
new UpdateAsyncTask(diaryDao).execute(diaries);
}
void deleteDiaries(Diary... diaries) {
new DeleteAsyncTask(diaryDao).execute(diaries);
}
void deleteAllDiaries(Diary... diaries) {
new DeleteAllAsyncTask(diaryDao).execute();
}
LiveData<Diary> getSpecificDiary(int i) {return specificDiary; }
LiveData<List<Diary>> getAllDiariesLive() {
return allDiariesLive;
}
static class InsertAsyncTask extends AsyncTask<Diary, Void, Void> {
private DiaryDao diaryDao;
InsertAsyncTask(DiaryDao diaryDao) {
this.diaryDao = diaryDao;
}
#Override
protected Void doInBackground(Diary... diaries) {
diaryDao.insertDiaries(diaries);
return null;
}
}
static class UpdateAsyncTask extends AsyncTask<Diary, Void, Void> {
private DiaryDao diaryDao;
UpdateAsyncTask(DiaryDao diaryDao) {
this.diaryDao = diaryDao;
}
#Override
protected Void doInBackground(Diary... diaries) {
diaryDao.updateDiaries(diaries);
return null;
}
}
static class DeleteAsyncTask extends AsyncTask<Diary, Void, Void> {
private DiaryDao diaryDao;
DeleteAsyncTask(DiaryDao diaryDao) {
this.diaryDao = diaryDao;
}
#Override
protected Void doInBackground(Diary... diaries) {
diaryDao.deleteDiaries(diaries);
return null;
}
}
static class DeleteAllAsyncTask extends AsyncTask<Void, Void, Void> {
private DiaryDao diaryDao;
DeleteAllAsyncTask(DiaryDao diaryDao) {
this.diaryDao = diaryDao;
}
#Override
protected Void doInBackground(Void... voids) {
diaryDao.deleteAllDiaries();
return null;
}
}
}
DiaryViewModel.class
public class DiaryViewModel extends AndroidViewModel {
private DiaryRepository diaryRepository;
public DiaryViewModel(#NonNull Application application) {
super(application);
diaryRepository = new DiaryRepository(application);
}
public LiveData<Diary> getSpecificDiary(/**how should i code here?**/) {
return diaryRepository.getSpecificDiary(i);
}
public LiveData<List<Diary>> getAllDiariesLive() {
return diaryRepository.getAllDiariesLive();
}
public void insertDiaries(Diary... diaries) {
diaryRepository.insetDiaries(diaries);
}
public void updateDiaries(Diary... diaries) { diaryRepository.updateDiaries(diaries); }
public void deleteDiary(Diary... diaries) { diaryRepository.deleteDiaries(diaries);}
public void deleteAllDiaries() {
diaryRepository.deleteAllDiaries();
}
}
I think in DiaryRepository:
class DiaryRepository {
private LiveData<List<Diary>> allDiariesLive;
private DiaryDao diaryDao;
DiaryRepository(Context context) {
DDiaryRoomDatabase diaryRoomDatabase = DiaryRoomDatabase.getDiaryDatabase(context.getApplicationContext());
diaryDao = diaryRoomDatabase.getDiaryDao();
allDiariesLive = diaryDao.getAllDiariesLive();
}
void insetDiaries(Diary... diaries) {
new InsertAsyncTask(diaryDao).execute(diaries);
}
void updateDiaries(Diary... diaries) {
new UpdateAsyncTask(diaryDao).execute(diaries);
}
void deleteDiaries(Diary... diaries) {
new DeleteAsyncTask(diaryDao).execute(diaries);
}
void deleteAllDiaries(Diary... diaries) {
new DeleteAllAsyncTask(diaryDao).execute();
}
LiveData<Diary> getSpecificDiary(int i) {
return diaryDao.getSpecificDiariesLive(i);
}
LiveData<List<Diary>> getAllDiariesLive() {
return allDiariesLive;
}
...
so your ViewModel will be changed to this:
public class DiaryViewModel extends AndroidViewModel {
private DiaryRepository diaryRepository;
public DiaryViewModel(#NonNull Application application) {
super(application);
diaryRepository = new DiaryRepository(application);
}
public LiveData<Diary> getSpecificDiary(int i) {
return diaryRepository.getSpecificDiary(i);
}
...
DiaryRepository.class and ViewModel.class like hosseini sajad code. And i should use this with livedata like follow:
mDiaryViewModel = new ViewModelProvider(activity).get(DiaryViewModel.class);
mDiaryViewModel.getSpecificDiary(pos+1).observe(activity, diary -> {
Log.d("test", "Here is the Title: " + diary.getDiary_Title() + "\n" + "Here is the Content: " +diary.getDiary_Content() );
});

How to access data from a Roomdatabase inside a BroadcastReceiver class

I need to access data from my Room database inside a BroadCastReceiver class, but as you know we need a lifecycle owner to get an instance of ViewModel class as shown below.
public class AlertReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
NotificationHelper.sendFinanceLoggingNotification(context);
RecurrenceInfoViewModel recurrenceInfoViewModel = new ViewModelProvider(this).get(RecurrenceInfoViewModel.class);
}
}
when passing "this" as the lifecycle owner android studio is throwing error. Can anyone please guide me from where I can get a lifecycle owner inside a BroadCastReceiver or if you can suggest any other way of accessing the data. Below are my ViewModel and Repository classes
public class RecurrenceInfoViewModel extends AndroidViewModel {
private LiveData<List<RecurrenceInfoEntity>> allRecurrenceInfos;
private RecurrenceInfoRepository recurrenceInfoRepository;
public RecurrenceInfoViewModel(#NonNull Application application) {
super(application);
recurrenceInfoRepository=new RecurrenceInfoRepository(application);
}
public void insertRecurrenceInfo(RecurrenceInfoEntity recurrenceInfoEntity) {
recurrenceInfoRepository.insertRecurrenceInfo(recurrenceInfoEntity);
}
public void updateRecurrenceInfo(RecurrenceInfoEntity recurrenceInfoEntity) {
recurrenceInfoRepository.updateRecurrenceInfo(recurrenceInfoEntity);
}
public void deleteRecurrenceInfo(RecurrenceInfoEntity recurrenceInfoEntity) {
recurrenceInfoRepository.deleteRecurrenceInfo(recurrenceInfoEntity);
}
public void deleteAllRecurrenceInfos() {
recurrenceInfoRepository.deleteAllRecurrenceInfo();
}
public LiveData<RecurrenceInfoEntity> getAllRecurrenceInfos(String recurrenceInfoKey) {
return recurrenceInfoRepository.getRecurrenceInfoEntityList(recurrenceInfoKey);
}
}
public class RecurrenceInfoRepository {
private RecurrenceInfoDao recurrenceInfoEntityDao;
private LiveData<List<RecurrenceInfoEntity>> recurrenceInfoEntityList;
public RecurrenceInfoRepository(Context context) {
MoneyManagerDatabase moneyManagerDatabase = MoneyManagerDatabase.getInstance(context);
recurrenceInfoEntityDao = moneyManagerDatabase.getRecurrenceInfoDao();
recurrenceInfoEntityList = recurrenceInfoEntityDao.getAllRecurrenceInfo();
}
public void insertRecurrenceInfo(RecurrenceInfoEntity data) {
new PerformSingleColumnDataOperations(recurrenceInfoEntityDao,
Constants.INSERT_SINGLE_NODE_DATABASE_OPERATION).execute(data);
}
public void updateRecurrenceInfo(RecurrenceInfoEntity data) {
new PerformSingleColumnDataOperations(recurrenceInfoEntityDao,
Constants.UPDATE_SINGLE_NODE_DATABASE_OPERATION).execute(data);
}
public void deleteRecurrenceInfo(RecurrenceInfoEntity data) {
new PerformSingleColumnDataOperations(recurrenceInfoEntityDao,
Constants.DELETE_SINGLE_NODE_DATABASE_OPERATION).execute(data);
}
public void deleteRecurrenceInfo(String type) {
new PerformSingleColumnDataOperations(recurrenceInfoEntityDao,
Constants.DELETE_SINGLE_NODE_DATABASE_OPERATION).execute();
}
public void deleteAllRecurrenceInfo() {
new PerformSingleColumnDataOperations(recurrenceInfoEntityDao,
Constants.DELETE_ALL_NODES_DATABASE_OPERATION).execute();
}
public LiveData<RecurrenceInfoEntity> getRecurrenceInfoEntityList(String key) {
return recurrenceInfoEntityDao.getAllRecurrenceInfo(key);
}
private static class PerformSingleColumnDataOperations extends AsyncTask<RecurrenceInfoEntity, Void, Void> {
private RecurrenceInfoDao dataDao;
private String operationType;
PerformSingleColumnDataOperations(RecurrenceInfoDao dataDao, String operationType) {
this.dataDao = dataDao;
this.operationType = operationType;
}
#Override
protected Void doInBackground(RecurrenceInfoEntity... recurrenceInfoEntities) {
switch (operationType) {
case Constants.INSERT_SINGLE_NODE_DATABASE_OPERATION:
dataDao.insertRecurrenceInfo(recurrenceInfoEntities[0]);
break;
case Constants.UPDATE_SINGLE_NODE_DATABASE_OPERATION:
dataDao.updateRecurrenceInfo(recurrenceInfoEntities[0]);
break;
case Constants.DELETE_SINGLE_NODE_DATABASE_OPERATION:
dataDao.deleteRecurrenceInfo(recurrenceInfoEntities[0]);
break;
case Constants.DELETE_ALL_NODES_DATABASE_OPERATION:
dataDao.deleteAllRecurrenceInfo();
}
return null;
}
}
}
Thanks in advance.
I have solved the above problem by NOT using LiveData.
You can access data from Room anywhere by just providing the ApplicationContext as shown below.
DAO:
#Query("SELECT * FROM reference_info where recurrenceInfoPrimaryKey=:recurrenceinfoprimkey")
RecurrenceInfoEntity getAllRecurrenceInfoWithOutLiveData(String recurrenceinfoprimkey);
Repository:
public RecurrenceInfoEntity getRecurrenceInfoEntityWithOutLiveData(String key) {
return recurrenceInfoEntityDao.getAllRecurrenceInfoWithOutLiveData(key);
}
BroadCastReceiver:
public class AlertReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
new Thread(() -> {
RecurrenceInfoEntity recurrenceInfoEntity =
recurrenceInfoRepository.getRecurrenceInfoEntityWithOutLiveData(Constants.LOG_FINANCES_RECURRENCE_KEY);
}).start();
}

Android Room JUnit testing: "AsyncTask #1" java.lang.IllegalStateException: Cannot perform this operation because there is no current transaction

I'm trying to run simple test to my project but this exception is holding me back and i cannot find out why.
Im doing a simple test to insert some new customers to my database. My first problem i encountered when inserting customers was that I could NOT insert data in the Main Thread so I used an AsyncTask.execute(new Runnable()) but this time i ran into this problem.
I've provided the following classes that should give a good insight I hope..
TestClass:
#RunWith(RobolectricTestRunner.class)
public class ExampleUnitTest {
ShopDatabase db;
private CustomerDao customerDao;
#Before
public void createDB() {
Context context = ApplicationProvider.getApplicationContext();
db = Room.inMemoryDatabaseBuilder(context, ShopDatabase.class).build();
customerDao = db.customerDao();
}
#Test
public void createUser(){
final Customer customer = new Customer("Test", "Test test", Date.valueOf("2020-10-10"));
AsyncTask.execute(new Runnable() {
#Override
public void run() {
customerDao.insert(customer);
}
});
Customer customerFound = customerDao.getCustomerByName("Test", "Test test");
assertEquals(customerFound.getFirstName(), customer.getFirstName(), "Could not find user..");
}
#After
public void closeDB() {
db.close();
}
Repository:
public class Repository {
private CustomerDao customerDao;
private LiveData<List<Customer>> allCustomers;
private Customer customer;
public Repository(Application application) {
// Get DB instance
ShopDatabase db = ShopDatabase.getInstance(application);
customerDao = db.customerDao();
allCustomers = customerDao.getAllCustomers();
}
public void insert(Customer customer) {
new InsertCustomerAsyncTask(customerDao).execute(customer);
}
public Customer getCustomerByName(String first, String last) {
return customerDao.getCustomerByName(first, last);
}
public LiveData<List<Customer>> getAllCustomers() {
return allCustomers;
}
// Inner Async class to insert customers
private static class InsertCustomerAsyncTask extends AsyncTask<Customer, Void, Void> {
private CustomerDao customerDao;
public InsertCustomerAsyncTask(CustomerDao customerDao) {
this.customerDao = customerDao;
}
#Override
protected Void doInBackground(Customer... customers) {
customerDao.insert(customers[0]);
return null;
}
}
Database:
#Database(entities = {Customer.class}, version = 1)
#TypeConverters({Converters.class})
public abstract class ShopDatabase extends RoomDatabase {
private static ShopDatabase instance;
public abstract CustomerDao customerDao();
public static synchronized ShopDatabase getInstance(Context context) {
if (instance == null) {
instance = Room.databaseBuilder(context.getApplicationContext(),
ShopDatabase.class,"shop_database")
.fallbackToDestructiveMigration()
.addCallback(roomCallback)
.build();
}
return instance;
}
private static RoomDatabase.Callback roomCallback = new RoomDatabase.Callback() {
#Override
public void onCreate(#NonNull SupportSQLiteDatabase db) {
super.onCreate(db);
new PopulateDbAsyncTask(instance).execute();
}
};
private static class PopulateDbAsyncTask extends AsyncTask<Void, Void, Void> {
private CustomerDao customerDao;
PopulateDbAsyncTask(ShopDatabase db) {
customerDao = db.customerDao();
}
#Override
protected Void doInBackground(Void... voids) {
// Customers
long c = customerDao.insert(new Customer("One", "A", Date.valueOf("2019-05-10")));
long c2 = customerDao.insert(new Customer("Two", "B", Date.valueOf("2020-07-10")));
long c3 = customerDao.insert(new Customer("Three", "C", Date.valueOf("1860-12-10")));
return null;
}
}
}
Firstly I thought it was because my app was not running so it meant my database was closed but that was not the case. I had my app open while testing.
Secondly - when I check if the database is open after i instantiate my database (db = Room.inMemoryDatabaseBuilder(context, ShopDatabase.class).build();) - db.isOpen() -> it returns false every time.
What can cause the problem?
when I check if the database is open after i instantiate my database (db = Room.inMemoryDatabaseBuilder(context, ShopDatabase.class).build();) - db.isOpen() -> it returns false every time.
The database is not opened until you attempt to access it. You can force accessing it (getting a Writable or Readable database/connection aka opening the database file itself which is all done behind the scenes on your behalf) before you return the instance. e.g.
......
instance.getOpenHelper().getWritableDatabase();
return instance;
Example
using :-
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
shopDatabase = ShopDatabase.getInstance(this);
boolean dbopen = shopDatabase.isOpen();
if (dbopen); //<<<<<<<<<< breakpoint here
}
With :-
public static synchronized ShopDatabase getInstance(Context context) {
if (instance == null) {
instance = Room.databaseBuilder(context.getApplicationContext(),
ShopDatabase.class,"shop_database")
.fallbackToDestructiveMigration()
.addCallback(roomCallback)
.build();
}
//instance.getOpenHelper().getWritableDatabase();
return instance;
}
results in
changing to use :-
public static synchronized ShopDatabase getInstance(Context context) {
if (instance == null) {
instance = Room.databaseBuilder(context.getApplicationContext(),
ShopDatabase.class,"shop_database")
.fallbackToDestructiveMigration()
.addCallback(roomCallback)
.build();
}
instance.getOpenHelper().getWritableDatabase(); //<<<<<<<<< ADDED
return instance;
}
results in :-
I could NOT insert data in the Main Thread
You can if you add .allowMainThreadQueries() when building
e.g.
public static synchronized ShopDatabase getInstance(Context context) {
if (instance == null) {
instance = Room.databaseBuilder(context.getApplicationContext(),
ShopDatabase.class,"shop_database")
.fallbackToDestructiveMigration()
.allowMainThreadQueries()
.addCallback(roomCallback)
.build();
}
instance.getOpenHelper().getWritableDatabase();
return instance;
}
You may wish to not use this though.

Room Insert List of POJO and Retrieve in Recyclerview

I am using room as data store for my app. I am trying to save a list of sessions from a successful network call in viewmodel class. I have used a repository for interacting with the dao and asynctask for making crud operations async.
Now, I'm trying to display the "saved data" in a recyclerview but it shows nothing. On inspection of my database table, I find that nothing was saved. Here's my model class:
#Entity(tableName = "sessions")
public class Sessions{
// #PrimaryKey(autoGenerate = true)
// public int id;
#SerializedName("prg_session_image")
public String sessionImage;
#SerializedName("prg_session_name")
public String session_name;
#SerializedName("prg_session_id") // used session id as PK
#PrimaryKey
#NonNull
public String prog_sessionId;
#SerializedName("prg_session_description")
public String session_desc;
#SerializedName("reference_id")
public String reference_id;
#SerializedName("prg_name")
public String program_name;
#SerializedName("batch_name")
public String batch_name;
#SerializedName("player_count")
public String participants_count;
#SerializedName("prg_session_focus_points")
public String session_focus_points;
#SerializedName("prg_session_equipment")
public String equipments_reqd;
#SerializedName("session_complete")
public String is_complete;
public Sessions() {
}
// public int getId() {
// return id;
// }
public String getSessionImage() {
return sessionImage;
}
public void setSessionImage(String sessionImage) {
this.sessionImage = sessionImage;
}
public String getSession_name() {
return session_name;
}
public void setSession_name(String session_name) {
this.session_name = session_name;
}
public String getProg_sessionId() {
return prog_sessionId;
}
public void setProg_sessionId(String prog_sessionId) {
this.prog_sessionId = prog_sessionId;
}
public String getSession_desc() {
return session_desc;
}
public void setSession_desc(String session_desc) {
this.session_desc = session_desc;
}
public String getReference_id() {
return reference_id;
}
public void setReference_id(String reference_id) {
this.reference_id = reference_id;
}
public String getProgram_name() {
return program_name;
}
public void setProgram_name(String program_name) {
this.program_name = program_name;
}
public String getBatch_name() {
return batch_name;
}
public void setBatch_name(String batch_name) {
this.batch_name = batch_name;
}
public String getParticipants_count() {
return participants_count;
}
public void setParticipants_count(String participants_count) {
this.participants_count = participants_count;
}
public String getSession_focus_points() {
return session_focus_points;
}
public void setSession_focus_points(String session_focus_points) {
this.session_focus_points = session_focus_points;
}
public String getEquipments_reqd() {
return equipments_reqd;
}
public void setEquipments_reqd(String equipments_reqd) {
this.equipments_reqd = equipments_reqd;
}
public String getIs_complete() {
return is_complete;
}
public void setIs_complete(String is_complete) {
this.is_complete = is_complete;
}
}
And Dao class:
#Dao
public interface SessionsDAO {
// #Insert
// LiveData<List<Sessions>> saveSessions(List<Sessions> sessions);
#Insert
void addSessions(List<Sessions> list);
#Query("select * from sessions")
LiveData<List<Sessions>> getAllSessions();
#Query("select * from sessions where prog_sessionId = :id")
Sessions getSessionById(String id);
}
In repository, I have asynctasks for various operations with the Dao:
public class SessionsRepository {
public SessionsDAO dao;
private MutableLiveData<List<Sessions>> querySingleSession;
private LiveData<List<Sessions>> allSessions;
public SessionsRepository(Application application){
SportsDatabase database = SportsDatabase.getInstance(application);
dao = database.sessionsDAO();
querySingleSession = new MutableLiveData<>();
allSessions = dao.getAllSessions();
}
public void saveSessions(List<Sessions> sessions){
new SaveSessionsTask(dao).execute(sessions);
}
public LiveData<List<Sessions>> getAllSessions() {
return allSessions;
}
public void getSessionById(List<Sessions> sessions){
querySingleSession.setValue(sessions);
}
public class SaveSessionsTask extends AsyncTask<List<Sessions>, Void, Void>{
private SessionsDAO dao;
public SaveSessionsTask(SessionsDAO dao) {
this.dao = dao;
}
#Override
protected Void doInBackground(List<Sessions>... lists) {
dao.addSessions(lists[0]);
return null;
}
}
// public void getSessions(){
// new GetSessionsTask(dao).execute();
// }
// public class GetSessionsTask extends AsyncTask<Void, >
}
I am trying to at the moment save all the results from network call and display them from the database. Here's my operation in viewmodel class:
public class HomeSessionsViewModel extends AndroidViewModel {
private static final String TAG = HomeSessionsViewModel.class.getSimpleName();
private MutableLiveData<SessionDetails> liveDetails;
private SessionsRepository repository;
public HomeSessionsViewModel(#NonNull Application application) {
super(application);
repository = new SessionsRepository(application);
}
// public HomeSessionsViewModel (Application application){
// repository = new SessionsRepository(application);
// }
public MutableLiveData<SessionDetails> getSessions(){
if (liveDetails == null){
liveDetails = new MutableLiveData<>();
fetchSessions();
}
return liveDetails;
}
private void fetchSessions(){
String coachId = "4086";
Call<SessionDetails> call = RestClient.getRestInstance().getSessionsService().fetchSessions(coachId);
call.enqueue(new Callback<SessionDetails>() {
#Override
public void onResponse(Call<SessionDetails> call, Response<SessionDetails> response) {
if (response.isSuccessful()){
SessionDetails details = response.body();
List<Sessions> sessions = details.getSessions();
Log.d(TAG, "N/w sesh size:\t" + sessions.size());
liveDetails.setValue(details); // now just displaying from network
saveSessions(sessions);
}
}
#Override
public void onFailure(Call<SessionDetails> call, Throwable t) {
}
});
}
private void saveSessions(List<Sessions> sessions) {
repository.saveSessions(sessions);
}
public LiveData<List<Sessions>> fetchSessionsDB(){
return repository.getAllSessions();
}
}
and in ui controller (fragment), I have called the viewmodel's fetchSessionsDB() method but no data is shown. The network request works well as I was displaying from there before adding room. What could be wrong here? Thank you.
API Response:
{
"session_details": [
{
"prg_session_name": "Session-16",
"prg_session_id": "987",
"prg_session_equipment": null,
"prg_session_description": "",
"prg_session_focus_points": "",
"prg_session_image": "http://devsports.copycon.in/includes/uploads/Jellyfish5.jpg",
"session_complete": "0",
"prg_name": "cricket coaching",
"reference_id": "293",
"batch_id": "57",
"batch_name": "Batch 3",
"player_count": "10"
}, .... ]}
and SessionDetails POJO:
public class SessionDetails {
#SerializedName("session_details")
#Expose
private List<Sessions> sessions;
#SerializedName("status")
private String status;
#SerializedName("message")
private String msg;
public List<Sessions> getSessions() {
return sessions;
}
}
fragment class where db data should be displayed:
private void populateSessions() {
sessionsRV = fragmentBinding.sessionsRV;
sessionsRV.setHasFixedSize(false);
LinearLayoutManager hlm = new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL, false);
sessionsRV.setLayoutManager(hlm);
sessionsViewModel = ViewModelProviders.of(this).get(HomeSessionsViewModel.class);
// sessionsViewModel.fetchSessions(""); // TODO: 3/16/2019 Use coach id from db
// calling db from viewmodel
sessionsViewModel.fetchSessionsDB().observe(this, new Observer<List<Sessions>>() {
#Override
public void onChanged(#Nullable List<Sessions> sessions) {
sessionsAdapter = new SessionsAdapter(getActivity(), sessions);
sessionsRV.setAdapter(sessionsAdapter);
Log.d(TAG, "Sessions Count:\t" + sessionsAdapter.getItemCount()); // logs 0
}
});
// previously from network directly displayed
// sessionsViewModel.getSessions().observe(this, new Observer<SessionDetails>() {
// #Override
// public void onChanged(#Nullable SessionDetails details) {
// List<Sessions> list = details.getSessions();
// sessionsAdapter = new SessionsAdapter(getActivity(), list);
// sessionsRV.setAdapter(sessionsAdapter);
// Log.d(TAG, "Sessions Count:\t" + sessionsAdapter.getItemCount());
// }
// });
}
Sports Database class:
#Database(entities = {CoachDB.class, Sessions.class}, version = 1, exportSchema = false)
public abstract class SportsDatabase extends RoomDatabase {
private static SportsDatabase instance;
public abstract CoachDAO coachDAO();
public abstract SessionsDAO sessionsDAO();
public static synchronized SportsDatabase getInstance(Context context) {
if (instance == null){
instance = Room.databaseBuilder(context.getApplicationContext(), SportsDatabase.class, "sports_db")
.fallbackToDestructiveMigration()
.build();
}
return instance;
}
}
I have solved this issue by modifying my #insert method in dao like
#Dao
public interface SessionsDAO {
#Insert
void addSessions(List<Sessions> sessions);
#Query("select * from sessions")
LiveData<List<Sessions>> getAllSessions();
#Query("select * from sessions where prog_sessionId = :id")
Sessions getSessionById(String id);
}
and run my async task with a list of sessions as input and it worked successfully.
private void saveSessions(List<Sessions> sessions) {
new SaveSessionsTask(dao).execute(sessions);
}
public class SaveSessionsTask extends AsyncTask<List<Sessions>, Void, Void> {
private SessionsDAO dao;
public SaveSessionsTask(SessionsDAO dao) {
this.dao = dao;
}
#Override
protected Void doInBackground(List<Sessions>... lists) {
dao.addSessions(lists[0]);
return null;
}
}

Equivalent of iOS NSNotificationCenter in Android?

Is there an equivalent of the iOS class NSNotificationCenter in Android ? Are there any libraries or useful code available to me ?
In Android there is not a central notification center as in ios.
But you can basically use Observable and Observer objects to achieve your task.
You can define a class like something below, just modify it for singleton use and add synchronized for concurrent use but the idea is the same:
public class ObservingService {
HashMap<String, Observable> observables;
public ObservingService() {
observables = new HashMap<String, Observable>();
}
public void addObserver(String notification, Observer observer) {
Observable observable = observables.get(notification);
if (observable==null) {
observable = new Observable();
observables.put(notification, observable);
}
observable.addObserver(observer);
}
public void removeObserver(String notification, Observer observer) {
Observable observable = observables.get(notification);
if (observable!=null) {
observable.deleteObserver(observer);
}
}
public void postNotification(String notification, Object object) {
Observable observable = observables.get(notification);
if (observable!=null) {
observable.setChanged();
observable.notifyObservers(object);
}
}
}
Take a look at the Otto event bus from Square:
http://square.github.com/otto/
It has essentially the same features as NSNotificationCenter but thanks to annotations and static typing it is easier to follow the dependencies of components and paths that events follow. It's much simpler to use than the stock Android broadcast API, IMO.
i had the same wondrings.. so i wrote this:
public class NotificationCenter {
//static reference for singleton
private static NotificationCenter _instance;
private HashMap<String, ArrayList<Runnable>> registredObjects;
//default c'tor for singleton
private NotificationCenter(){
registredObjects = new HashMap<String, ArrayList<Runnable>>();
}
//returning the reference
public static synchronized NotificationCenter defaultCenter(){
if(_instance == null)
_instance = new NotificationCenter();
return _instance;
}
public synchronized void addFucntionForNotification(String notificationName, Runnable r){
ArrayList<Runnable> list = registredObjects.get(notificationName);
if(list == null) {
list = new ArrayList<Runnable>();
registredObjects.put(notificationName, list);
}
list.add(r);
}
public synchronized void removeFucntionForNotification(String notificationName, Runnable r){
ArrayList<Runnable> list = registredObjects.get(notificationName);
if(list != null) {
list.remove(r);
}
}
public synchronized void postNotification(String notificationName){
ArrayList<Runnable> list = registredObjects.get(notificationName);
if(list != null) {
for(Runnable r: list)
r.run();
}
}
}
and a usage for this will be:
NotificationCenter.defaultCenter().addFucntionForNotification("buttonClick", new Runnable() {
#Override
public void run() {
Toast.makeText(MainActivity.this, "Hello There", Toast.LENGTH_LONG).show();
}
});
tried to make the interface as similar to IOS as possible, but simpler (no object registration needed).
hope that helps:)
If you don't want to use Observer - it can be problematic in cases you want a Fragment to be your Observer cause you can't extend more then one class-
You can use google's Guava Library (https://code.google.com/p/guava-libraries/)
for "Function" and "Multimap" - although you can use as well HashMap> for the subscibersCollection
and implement something like this:
import java.util.Collection;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.base.Function;
public class EventService {
ArrayListMultimap<String, Function<Object, Void>> subscibersCollection;
private static EventService instance = null;
private static final Object locker = new Object();
private EventService() {
subscibersCollection = ArrayListMultimap.create();
}
public static EventService getInstance() {
if (instance == null) {
synchronized (locker) {
if (instance == null) {
instance = new EventService();
}
}
}
return instance;
}
/**
* Subscribe to the notification, and provide the callback functions in case
* notification is raised.
*
* #param notification
* - notification name
* #param func
* - function to apply when notification is raised
*/
public void addSubscription(String notification, Function<Object, Void> func) {
synchronized (subscibersCollection) {
if (!subscibersCollection.containsEntry(notification, func)) {
subscibersCollection.put(notification, func);
}
}
}
/**
* Remove subscription from notification
*/
public void removeSubscription(String notification,
Function<Object, Void> func) {
synchronized (subscibersCollection) {
subscibersCollection.remove(notification, func);
}
}
/**
* raise notification for all its subscribers
*
* #param notification
* - notification name
* #param data
* - update data
*/
public void publish(String notification, Object data) {
Collection<Function<Object, Void>> observableList = subscibersCollection
.get(notification);
for (Function<Object, Void> func : observableList) {
func.apply(data);
}
}
}
On the basis of Behlül answer, I change the code to make it closer to iOS NSNotificationCenter.
Another thing: the notifications will fire on the main thread
package com.oxygen.utils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import android.os.Handler;
public class NotificationCenter {
//---------------- event type list ---------------------
public static enum NotificationID{
IMAGES_CACHE_READY
}
//---------------- singelton ---------------------------
private static NotificationCenter instance = null;
private NotificationCenter() { observables = new HashMap<NotificationID, MyObservable>(); }
public static synchronized NotificationCenter singelton() {
if (instance == null) {
instance = new NotificationCenter ();
}
return instance;
}
//-------------------------------------------
public class Notification {
private Object poster; // the object that post the event
private Object info; // event specific data
private NotificationID id; // event name
public Notification(Object poster, NotificationID id, Object info) {
super();
this.poster = poster;
this.info = info;
this.id = id;
}
public Object getPoster() {
return poster;
}
public Object getInfo() {
return info;
}
public NotificationID getId() {
return id;
}
}
//-------------------------------------------
public interface Notifiable {
public void onNotification(Notification notify);
}
//-------------------------------------------
protected class MyObservable {
List<Notifiable> observers = new ArrayList<Notifiable>();
public MyObservable() {
}
public void addObserver(Notifiable observer) {
if (observer == null) {
throw new NullPointerException("observer == null");
}
synchronized (this) {
if (!observers.contains(observer))
observers.add(observer);
}
}
public int countObservers() {
return observers.size();
}
public synchronized void deleteObserver(Notifiable observer) {
observers.remove(observer);
}
public synchronized void deleteObservers() {
observers.clear();
}
public void notifyObservers(Notification notify) {
int size = 0;
Notifiable[] arrays = null;
synchronized (this) {
size = observers.size();
arrays = new Notifiable[size];
observers.toArray(arrays);
}
if (arrays != null) {
for (Notifiable observer : arrays) {
observer.onNotification(notify);
}
}
}
}
//-------------------------------------------
HashMap<NotificationID, MyObservable > observables;
public void addObserver(NotificationID id, Notifiable observer) {
MyObservable observable = observables.get(id);
if (observable==null) {
observable = new MyObservable ();
observables.put(id, observable);
}
observable.addObserver(observer);
}
public void removeObserver(NotificationID id, Notifiable observer) {
MyObservable observable = observables.get(id);
if (observable!=null) {
observable.deleteObserver(observer);
}
}
public void removeObserver(Notifiable observer) {
for (MyObservable observable : observables.values()) {
if (observable!=null) {
observable.deleteObserver(observer);
}
}
}
public void postNotification(final Object notificationPoster, final NotificationID id, final Object notificationInfo) {
final MyObservable observable = observables.get(id);
if (observable!=null) {
// notification post to the maim (UI) thread
// Get a handler that can be used to post to the main thread
Handler mainHandler = new Handler(AppContext.get().getMainLooper());
Runnable myRunnable = new Runnable() {
#Override
public void run() {
observable.notifyObservers(new Notification(notificationPoster, id, notificationInfo) );
}
};
mainHandler.post(myRunnable);
}
}
}
Listener sample:
public class CustomGridViewAdapter extends ArrayAdapter<Category> implements Notifiable {
int layoutResourceId;
public CustomGridViewAdapter(Context context, int layoutResourceId) {
super(context, layoutResourceId);
this.layoutResourceId = layoutResourceId;
loadCategories(false);
NotificationCenter.singelton().addObserver(NotificationID.IMAGES_CACHE_READY, this);
}
public void onDestroy() {
NotificationCenter.singelton().removeObserver(this);
}
#Override
public void onNotification(Notification notify) {
switch (notify.getId()) {
case IMAGES_CACHE_READY:
loadCategories(true);
break;
}
}
...
}

Categories

Resources