I am recently trying to learn Android and I am very new to backend knowledge e.g. threading and stuff. I got Room figured out and try to integrate it with front end component. So, I am not worried how front end adapting the data I want it to present. I have the problem trying to design and implement the integration in a clean way using thread and trying to implement threading since I am new to it.
Here is my code.
Database.class
#Database(entities = {Groups.class, Member.class}, version = 1, exportSchema
= false)
public abstract class DatabaseConfig extends RoomDatabase {
private static final String DATABASE_NAME = "db";
private static DatabaseConfig INSTANCE;
public abstract GroupDao groupDao();
public abstract MemberDao memberDao();
public static DatabaseConfig getDatabase(final Context context) {
if (INSTANCE == null) {
synchronized (DatabaseConfig.class) {
if (INSTANCE == null) {
INSTANCE =
Room.databaseBuilder(context.getApplicationContext(),
DatabaseConfig.class, DATABASE_NAME)
.addCallback(DatabaseCallBack)
.build();
}
}
}
return INSTANCE;
}
private static RoomDatabase.Callback DatabaseCallBack =
new RoomDatabase.Callback(){
#Override
public void onOpen (#NonNull SupportSQLiteDatabase db){
super.onOpen(db);
}
};
}
GroupRepo.class
public class GroupRepo {
private final GroupDao groupDao;
//ExecutorService es = Executors.newSingleThreadExecutor();
public GroupRepo (Context context){
DatabaseConfig db = DatabaseConfig.getDatabase(context);
groupDao = db.groupDao();
}
public List<Groups> getAllGroups(){
/*
So my idea is to have some sort of threading implement from here
and use executor.run() to run my query and capture data
*/
}
}
Groups.class
#Dao
public interface GroupDao {
#Query("SELECT * from groups")
List<Groups> getAllGroups();
#Query("Select * from groups where groups.id = :groupsId")
GroupAllMembers getAllMember(int groupsId);
#Insert
void insert(Groups... groups);
#Delete
void delete(Groups groups);
}
I am not going to post my Entity class since my intention is not about that. I am fairly new to background threads. Please help and ideally provide some example to help me understand.
There are two ways to handle this: if you are doing the query on the database for UI view, I'd recommend your Day return LiveData> and then put that inside of a viewmodel. All of this is covered in Android docs.
If you are doing it in a service or don't want to interact with UI simply do this:
Thread(Runnable() {
#Override
public void run() {
// Do your stuff here with Room
}
}).start()
Related
I am newbie to the Android X Room Database. Earlier I used Sqlitehelper class to access the sqlite databases and then migrated to room. The question is , when I insert data into Room database table it stores for that moment and when I restart the app the data is gone. I want to have multiple tables in a single database.
DAO :
#androidx.room.Dao
public interface ItineraryDao {
#Insert(onConflict = OnConflictStrategy.IGNORE)
void insert(ItineraryData model);
#Update
void update(ItineraryData model);
#Delete
void delete(ItineraryData model);
#Query("DELETE FROM "+ CONSTANTS.ITINERARY_TABLE)
void deleteAllItinerary();
#Query("SELECT * FROM "+CONSTANTS.ITINERARY_TABLE+" ORDER BY Date ASC")
LiveData<List<ItineraryData>> getAllItinerary();
Database:
#Database(entities = {ItineraryData.class},version = 1,exportSchema = false)
public abstract class ItineraryDatabase extends RoomDatabase {
private static ItineraryDatabase instance;
public abstract ItineraryDao Dao();
public static synchronized ItineraryDatabase getInstance(Context mCon){
if (instance == null) {
instance =
Room.databaseBuilder(mCon.getApplicationContext(),
ItineraryDatabase.class, CONSTANTS.ITINERARY_TABLE)
.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> {
PopulateDbAsyncTask(ItineraryDatabase instance) {
ItineraryDao dao = instance.Dao();
}
#Override
protected Void doInBackground(Void... voids) {
return null;
}
The above is the code I used is every table to generate table with different Dao's . As I need to create the tablets in a single database i used same CONSTANTS.ITINERARY_TABLE for all of them. If I use differnt names in CONSTANTS.ITINERARY_TABLE this works fine. But it creates different databases.
How can I create multiple tables in a single database without loosing data on restart. I went thorugh some earlier posts. But they suggest to use different database names.
Thank you
How can I create multiple tables in a single database without loosing data on restart.
You define multiple tables by specifying multiple #Entity annotated classes in the entities parameter of the #Database annotation.
That is how you tell Room what tables will be created.
Where you use ItineraryDatabase.class, CONSTANTS.ITINERARY_TABLE, this is telling Room that the database (i.e. the file name) will be named as per CONSTANTS.ITINERARY_TABLE.
I suspect that you want the same table layout/schema but multiple tables (possibly itself not a good idea as each table has overheads when a single column could indicate the equivalent of the row belonging to a specific set of data).
you may wish to edit your question to say more about what you are trying to achieve and you may therefore get more elaborate and specific answers. If you do then feel free to notify myself by comment as such to this answer. At the minimum I would then look at any changes, but there is a very good chance that I would elaborate.
Additional (DEMO)
Considering the comment:-
I want to have different table with different table schema's in a single database file. I have seperate entity classes for them. Do i need to have getinstance for each table in this single class ( which extends with roomdatabase) ? One Database Class with multiple getinstance?
and also the presumption of a single schema (not that it matters much if not, simpler if not). Then consider the following code that is based very much on your code.
ItineraryData (used as a model for two tables, so not #Entity annotated - can be ignored if different schemas);-
class ItineraryData {
#PrimaryKey
Long id=null;
long date=System.currentTimeMillis() / 1000;
#NonNull
String otherColumn;
}
CONSTANTS :-
class CONSTANTS {
static final String ITINERARY_TABLE = "it01";
static final String ITINERARY_TABLE_2 = "it02";
}
ItineraryTable01 (bit of a cheat as schema grabbed from ItineraryData) :-
#Entity(tableName = CONSTANTS.ITINERARY_TABLE)
class ItineraryTable01 extends ItineraryData {
}
ItineraryTable02 (cheat again):-
#Entity(tableName = CONSTANTS.ITINERARY_TABLE_2)
class ItineraryTable02 extends ItineraryData {
}
ItineraryDao
#Dao
public interface ItineraryDao {
#Insert(onConflict = OnConflictStrategy.IGNORE)
void insert(ItineraryTable01 model);
#Insert(onConflict = OnConflictStrategy.IGNORE)
void insert(ItineraryTable02 model);
#Update
void update(ItineraryTable01 model);
#Update
void update(ItineraryTable02 model);
#Delete
void delete(ItineraryTable01 model);
#Delete
void delete(ItineraryTable02 model);
#Query("DELETE FROM " + CONSTANTS.ITINERARY_TABLE)
void deleteAllItineraryTable01();
#Query("DELETE FROM " + CONSTANTS.ITINERARY_TABLE_2)
void deleteAllItineraryTable02();
#Query("SELECT * FROM " + CONSTANTS.ITINERARY_TABLE + " ORDER BY Date ASC")
/*LiveData<*/List<ItineraryTable01>/*>*/ getAllItineraryTable01();
#Query("SELECT * FROM " + CONSTANTS.ITINERARY_TABLE + " ORDER BY Date ASC")
/*LiveData<*/List<ItineraryTable02>/*>*/ getAllItineraryTable02();
}
commented out LiveData so can run on MainThread (although queries not used in demo, but just in case)
note the close duplications for each table.
ItineraryDatabase
#Database(entities = { ItineraryTable01.class,ItineraryTable02.class/*ItineraryData.class*/} ,version = 1,exportSchema = false)
public abstract class ItineraryDatabase extends RoomDatabase {
private static ItineraryDatabase instance;
public abstract ItineraryDao Dao();
public static synchronized ItineraryDatabase getInstance(Context mCon) {
if (instance == null) {
instance =
Room.databaseBuilder(mCon.getApplicationContext(),
ItineraryDatabase.class, CONSTANTS.ITINERARY_TABLE)
.allowMainThreadQueries() /*<<<<<<<<<< Added for convenience brevity of demo */
.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> {
PopulateDbAsyncTask(ItineraryDatabase instance) {
ItineraryDao dao = instance.Dao();
}
#Override
protected Void doInBackground(Void... voids) {
return null;
}
}
}
allow to run on Main thread, otherwise unchanged (no need for callback though, it does nothing)
Finally to demo some code in an activity :-
public class MainActivity extends AppCompatActivity {
ItineraryDatabase db;
ItineraryDao dao;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ItineraryTable01 it01 = new ItineraryTable01();
ItineraryTable02 it02 = new ItineraryTable02();
it01.otherColumn = "blah01";
it02.otherColumn = "blah02";
db = ItineraryDatabase.getInstance(this);
dao = db.Dao();
/* Not until following code is executed is the database actually opened */
dao.insert(it01); /* Inserts into table01 */
dao.insert(it02); /* Inserts into table02 */
}
}
Results
When first run
Using App Inspection:-
and
Run again (aka rerun)
and
as can be seen the data (rows highlighted) from the first run has been retained and data from the second run has been added (different generated id's and timestamps)
I am using reactive mongoDB with Micronaut application
implementation("io.micronaut.mongodb:micronaut-mongo-reactive")
Trying to create a TextIndex and search Free text functionality
public class Product {
#BsonProperty("id")
private ObjectId id;
private String name;
private float price;
private String description;
}
In spring data we have #TextIndexed(weight = 2) to create a TextIndex to the collection, what is the equivalent in the Micronaut application.
I'm afraid that Micronaut Data does not yet support automatic index creation based on annotations for MongoDB. Micronaut Data now simplifies only work with SQL databases.
But you can still create the index manually using MongoClient like this:
#Singleton
public class ProductRepository {
private final MongoClient mongoClient;
public ProductRepository(MongoClient mongoClient) {
this.mongoClient = mongoClient;
}
public MongoCollection<Product> getCollection() {
return mongoClient
.getDatabase("some-database")
.getCollection("product", Product.class);
}
#PostConstruct
public void createIndex() {
final var weights = new BasicDBObject("name", 10)
.append("description", 5);
getCollection()
.createIndex(
Indexes.compoundIndex(
Indexes.text("name"),
Indexes.text("description")
),
new IndexOptions().weights(weights)
)
.subscribe(new DefaultSubscriber<>() {
#Override
public void onNext(String s) {
System.out.format("Index %s was created.%n", s);
}
#Override
public void onError(Throwable t) {
t.printStackTrace();
}
#Override
public void onComplete() {
System.out.println("Completed");
}
});
}
}
You can of course use any subscriber you want. That anonymous class extending DefaultSubscriber is used here only for demonstration purpose.
Update: You can create indexes on startup for example by using #PostConstruct. It means to add all index creation logic in a method annotated by #PostConstruct in some repository or service class annotated by #Singleton, then it will be called after repository/service singleton creation.
here is my problem:
i have used MVVM/Repository design pattern like this:
Activity -(Observes)-> ViewModel's LiveData -> Repository -> WebService API (GET Resource)
i have another calls for UPDATING Resource to WebService.
Problem:
after changing resource on the server. how i can make the Resource livedata to update itself with new servers data
i want to force it fetch data from server again because some other data may have been changed.
and i dont want to use local database (Room) and change it because my server data might be changed. and they need to fetch each time.
The Only solution passed my Mind was to create a Livedata Source (as dataVersion) to it.
and increment it after every update like this (pseudo code):
dataVersion = new MutableLiveData();
dataVersion.setValue(0);
// my repository get method hasnt anything to do with the dataVersion.
myData = Transformation.switchmap(dataVersion, versionNum -> { WebServiceRepo.getList() });
and how dataVersion should get updated in ViewModel.
You could extend MutableLiveData to give it manual fetch functionality.
public class RefreshLiveData<T> extends MutableLiveData<T> {
public interface RefreshAction<T> {
private interface Callback<T> {
void onDataLoaded(T t);
}
void loadData(Callback<T> callback);
}
private final RefreshAction<T> refreshAction;
private final Callback<T> callback = new RefreshAction.Callback<T>() {
#Override
public void onDataLoaded(T t) {
postValue(t);
}
};
public RefreshLiveData(RefreshAction<T> refreshAction) {
this.refreshAction = refreshAction;
}
public final void refresh() {
refreshAction.loadData(callback);
}
}
Then you can do
public class YourViewModel extends ViewModel {
private RefreshLiveData<List<Project>> refreshLiveData;
private final GithubRepository githubRepository;
private final SavedStateHandle savedStateHandle;
public YourViewModel(GithubRepository githubRepository, SavedStateHandle savedStateHandle) {
this.githubRepository = githubRepository;
this.savedStateHandle = savedStateHandle;
refreshLiveData = Transformations.switchMap(savedStateHandle.getLiveData("userId", ""), (userId) -> {
githubRepository.getProjectList(userId);
});
}
public void refreshData() {
refreshLiveData.refresh();
}
public LiveData<List<Project>> getProjects() {
return refreshLiveData;
}
}
And then repository can do:
public RefreshLiveData<List<Project>> getProjectList(String userId) {
final RefreshLiveData<List<Project>> liveData = new RefreshLiveData<>((callback) -> {
githubService.getProjectList(userId).enqueue(new Callback<List<Project>>() {
#Override
public void onResponse(Call<List<Project>> call, Response<List<Project>> response) {
callback.onDataLoaded(response.body());
}
#Override
public void onFailure(Call<List<Project>> call, Throwable t) {
}
});
});
return liveData;
}
I am working on an Android project using MVVM architecture, repositories and a Room database. So in my repository, I fetch some data from database and return the LiveData object to my ViewModel. Here is my code (very simplified):
public class Repository {
private final IDao mDao;
private OrderModel mOrderModel;
private LiveData<OrderModel> mOrderModelLiveData;
public Repository(Database database) {
mDao = database.dao();
mOrderModelLiveData= mDao.getOrderModel();
mOrderModel = new OrderModel();
}
public LiveData<OrderModel> getOrderModelLiveData() {
return mOrderModelLiveData;
}
}
As you can see I have a field called mOrderModel. So how can achieve to assign the actual value of the LiveData called mOrderModelLiveData to mOrderModel?
One option is to use LiveData's getValue() method. But this works asynchronously and can return NULL. A second option is to add a new DAO method which returns the mOrderModel directly without LiveData. But then mOrderModel is not updated automatically. Or do I have to use Transformations.switchMap maybe like this:
Transformations.map(mDao.getOrderModel(), new Function<LiveData<OrderModel>>() {
#Override
public Object apply(OrderModel orderModel) {
mOrderModel = orderModel;
return null;
}
};
I have seen an example, where observeForever is used, but I am not sure if it is a good idea to observe LiveData without a lifecycle.
My goal is to have in my repository the mOrderModel field set without using LiveData.
I hope I described my problem understandable.
Thanks in advance.
Chris
You can follow your second approach like below:
public class Repository {
private final IDao mDao;
private OrderModel mOrderModel;
private MutableLiveData<OrderModel> mOrderModelLiveData;
public Repository(Database database) {
mDao = database.dao();
mOrderModel = new OrderModel();
mOrderModelLiveData = new MutableLiveData<>();
}
public void getData() {
mOrderModel = mDao.getOrderModel(); // Your dao should return model instead of livedata
mOrderModelLiveData.postValue(mOrderModel);
}
public MutableLiveData<OrderModel> getOrderModelLiveData() {
return mOrderModelLiveData;
}
}
This will be placed in your repository.
public LiveData<OrderModel> getOrderModelLiveData() {
MutableLiveData<OrderModel> data = new MutableLiveData<>();
service.getOrderModelLiveData().enqueue(new Callback<OrderModel>() {
#Override
public void onResponse(Call<OrderModel> call, Response<OrderModel> response) {
ResponseObj body = response.body();
if (body != null ) {
data.postValue(body);
} else {
data.postValue(null);
}
}
#Override
public void onFailure(Call<MostResponse> call, Throwable t) {
data.postValue(null);
}
});
return data;
}
Thanks for your answer.
But how can I transform/assign the actual value of the LiveData object (LiveData) to the "normal" object (OrderModel).
On the one hand I want to observe LiveData as usual, and on the other hand I also want to access OrderModel directly from outside of the repository .
I tried to use TDD and got following code:
public class ViewModel extends BaseObservable {
private final Subscription subscription;
public ReleasesViewModel(Observable<List<Data>> model) {
subscription = model.subscribe(this::setData);
}
public void destroy() { //method is not under test
subscription.unsubscribe();
}
public List<Data> getData() {
return data;
}
public void setData(List<Data> data) {
this.data = data;
}
}
and my test for :
public class ViewModelTest {
#Test
public void getData() {
BehaviorSubject<List<Data>> observable = BehaviorSubject.create();
ViewModel viewModel = new ViewModel(observable);
List<Data> dataList = Arrays.asList(mock(Data.class), mock(Data.class));
observable.onNext(dataList);
assertTrue(viewModel.getData().equals(dataList));
}
}
The question is following:
I should verify that subscription.unsubscribe(); will be called to release resources, I can wrap subscription in some wrapper and inject dependency via constructor but I feel like I can violate an encapsulation of ViewModel class. After a lot of googling I did not find any clue for the case of verifying memory releasing in TDD practice. Can some one point me to some "best practices" for this case.
maybe you have some example of proper mvvm testing
This is what I'd do using Mockito (Just my preference, any other Mocking Framework will do either...):
public class ViewModelTest {
#Rule
public MockitoRule rule = MockitoJUnit.rule();
#Mock
BehaviorSubject<List<Data>> observable;
#Mock
Subscription subscription;
List<Data> data = Collections.emptyList();
#Test
public void Constructor_CalledWithObservable_subcribesSetDataMethod() {
// prepare
when(model.subscribe()).thenRetun(subscription);
// act
ViewModel viewModel = new ViewModel(observable);
// assert
verify(observable).subscribe(ViewModel::setData);
}
#Test
public void destroy_unsubscribes() {
// prepare
when(model.subscribe()).thenRetun(subscription);
ViewModel viewModel = new ViewModel(observable);
// act
viewModel.destroy();
// assert
verify(subscription).unsubscribe();
}
}