I downloaded the Room BasicSample app from here:
https://github.com/googlesamples/android-architecture-components
This sample is a readonly database. There is no example to insert a single entity. I am modifying it, and struggling to figure out how to call the getDatabase so I can do a simple insert on the db on a button click -
getDatabase().wordDao().insert(...) ?
How do I get access to the singleton BasicApp and call getDatabase method, and where do I call it from?
Any help is appreciated.
single Insert
#Insert(onConflict = IGNORE)
void insert(WordEntity word);
AppDatabase.java (not sure if this insert method goes here)
private static void insert(final AppDatabase database, final WordEntity word) {
database.wordDao().insert(word);
}
BasicApp.java
public class BasicApp extends Application {
private AppExecutors mAppExecutors;
#Override
public void onCreate() {
super.onCreate();
mAppExecutors = new AppExecutors();
}
public AppDatabase getDatabase() {
return AppDatabase.getInstance(this, mAppExecutors);
} // ==> how do I get access to this?
public DataRepository getRepository() {
return DataRepository.getInstance(getDatabase());
}
}
In the case you are accessing BasicApp class from an activity or service you can just call ((BasicApp)getApplication()).getDatabase().
Depends a little on what class you are working in.
if it is an activity method (like onCreate):
BasicApp basicApp = (BasicApp) this.getApplicationContext();
AppDatabase appDatabase = basicApp.getDatabase();
//... do work here
If you only have a view (like in an onClickListener which passes a view as an arg):
BasicApp basicApp = (BasicApp) view.getContext().getApplicationContext();
AppDatabase appDatabase = basicApp.getDatabase();
//... do work here
Related
I'm using Room, in my dao I have a method:
#Query("SELECT * FROM Flower")
LiveData<List<Flower>> getAllFlowers();
In my activity I'm invoking this method via ViewModel:
flowerViewModel.getAllFlowers().observe(ExampleFlowerbedActivity.this, new Observer<List<Flower>>() {
#Override
public void onChanged(List<Flower> flowers) {
...
int current_color = getRandom(flowers);
...
}
});
Inside I'm calling method getRandom():
public Integer getRandom(List<Flower> flowers){
Flower random = randomFlower(flowers); // here I get an error
addedToLayoutFlowers.add(random);
List<Integer> random_color = random.getColors_ids();
return randomColor(random_color);
}
Method randomFlower looks like:
public Flower randomFlower(List<Flower> list){
rand = new Random();
return list.get(rand.nextInt(list.size()));
}
And I'm getting this error:
java.lang.IllegalArgumentException: n <= 0: 0
I checked the List value that I get from database and it's 0 only when I install app and visit this activity in the first time. When I close the error and open app again everything works perfect.
What can be the problem? Thanks for any help.
Edit:
I'm adding flowers right in the database class:
#Database(entities = {Flower.class}, version = 2)
#TypeConverters(TypeConverter.class)
public abstract class FlowerDatabase extends RoomDatabase {
private static FlowerDatabase instance2;
public abstract FlowerDao flowerDao();
public static synchronized FlowerDatabase getInstance1(Context context) {
if (instance2 == null) {
instance2 = Room.databaseBuilder(context.getApplicationContext(),
FlowerDatabase.class, "flower_database")
.fallbackToDestructiveMigration()
.addCallback(room1Callback)
.build();
}
return instance2;
}
private static RoomDatabase.Callback room1Callback = new RoomDatabase.Callback(){
#Override
public void onCreate(#NonNull SupportSQLiteDatabase db) {
super.onCreate(db);
new Populate1DBAsyncTask(instance2).execute();
}
};
private static class Populate1DBAsyncTask extends AsyncTask<Void, Void, Void> {
private FlowerDao flowerDao;
private Populate1DBAsyncTask(FlowerDatabase db){
flowerDao = db.flowerDao();
}
#Override
protected Void doInBackground(Void... voids) {
flowerDao.insertFlower(new Flower(....)); // HERE
));
return null;
}
}
}
From the code you have posted, it appears you have a race condition. Room does not create the database until the first request to perform an operation in the database. I presume that is your call to getAllFlowers(). Only after that call will the database be created and callback onCreate() executed. Because the population of the database is triggered by onCreate() and runs as a background task, it does not complete before your main thread code runs. getAllFlowers() returns the empty list of Flowers, which causes the exception.
You need to rework your code to ensure the adding of Flowers is complete before the database is queried. Or consider using a prepopulated database.
I am attempting to call to my database from many different areas of my app, but I am having trouble saving and retrieving objects. When I inspect my data it looks like I am getting the cursor for the right columns, but it is always returning a null object even though it seems there is data in those columns.
I'm not sure what the issue is, but it may have to do with this recursive looking call into the database I found in the variable explorer (it continues this cascading for as far as I cared to explore).
To access my SQLite DB via Android Room I use a method call such as below from any relevant class (this gets called many times from various unique objects [should I be closing it?]):
#Entity(tableName = "cell")
public class LivingCell extends AsyncTask<Integer, Integer, Integer>{
....
getCellsDB.getCell(uid);}
public CellsDBHandler getCellsDB(){
if (cellsDBHandler == null)
cellsDBHandler = CellsDBHandler.getAppDatabase(MainActivity.getContext(),uid);
return cellsDBHandler;
}
Now I was under the impression that it wouldn't create a new instance of the database handler because this is what my CellsDBHandler looks like:
#Database(entities = {LivingCell.class})
public abstract class CellsDBHandler extends RoomDatabase {
private static CellsDBHandler INSTANCE;
public abstract CellDao cellDao();
public static CellsDBHandler getAppDatabase(Context context, String cityUid) {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
CellsDBHandler.class,
DBPath).build();}
return INSTANCE;
}
public LivingCell getCell(String cellUid) {
return cellDao().getCellByID(cellUid);}
}
And my CellDao
//ALL DAO CALLS ARE RETURNING NULL OBJECTS BUT HAVE POPULATED TABLES
#Dao
public interface CellDao {
#Query("SELECT * FROM cell")
List<LivingCell> getAll();
#Query("SELECT uid FROM cell")
List<String> getAllUids();
#Query("SELECT * FROM cell WHERE uid LIKE (:cellUid)")
LivingCell getCellByID(String cellUid);
#Insert(onConflict = REPLACE)
void insert(LivingCell cell);
...
Perhaps my call to MainActivity is the issue though... is this at all an acceptable way to find the application context?
private static MainActivity instance;
public static Context getContext(){
return instance;
}
Incase it matters, all my UIDs are:
//form of: 1c21df2a-5b8f-4a56-ac5b-ed385ba8e4b8
uid = UUID.randomUUID().toString();
as the cellUid is a single String, not an Array or Collection, try the following
#Query("SELECT * FROM cell WHERE uid LIKE :cellUid")
So I have a Room database with some Users stored in it. This is the current method im using to access the database and get data.
User user;
userDAO = AppDatabase.getAppDatabase(getApplicationContext()).userDao();
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
user = userDAO.getUser("testuser1");
runOnUiThread(new Runnable() {
#Override
public void run() {
String name = user.getName();
}
});
}
});
thread.start();
My question is there a better way to do this. I'm doing all this in my Activity code and I'd like to clean it up.
How would you go around implementing something like a static or abstract class which has callbacks to the main thread/activity so I can listen for responses. I'm still new to asynchronous tasks, so apologies if im being stupid.
I also need to be able to access the database from my Service which is always running. Also I sometimes need to save some data when the OnDestroy() method is called, and if I run an thread from there, it results in an crash. Maybe something like a IntentService? But I need to be able to call different methods to get and save different objects.
in AppDatabase class you have to use below code:
#Database(entities = {User.class},version = 1)
public abstract class AppDatabase extends RoomDatabase
{
public abstract userDAO userDao();
}
in UserDAo class you have to use below codes:
#Query("select * from User where user =:search")
User getUser(String search);
for use these methods you can use below codes:
User user;
public static AppDatabase database=null;
if (database==null)
{
database= Room.databaseBuilder(getApplicationContext(),AppDatabase.class,"nameOfDatabase")
.allowMainThreadQueries().build();
}
user=database.userDao.getUser("testuser1");
i have 2 dao and i need to call method between them
I did something like this but it s really bad because it does a infinite loop and it's does not respect quality standard
What is the proper way to do this please ?
Thank you very much
DAO 1
public class InfractionDAO {
private SQLiteDatabase database;
private SqLiteManager dbHelper;
private OffenderDAO offenderDAO;
public InfractionDAO(Context context) {
dbHelper = new SqLiteManager(context);
offenderDAO= new OffenderDAO (context);
}
List<Infractions> getInfractions(int id) {
offenderDao.getOffender(id);
}
}
DAO 2
public class OffenderDAO {
private SQLiteDatabase database;
private SqLiteManager dbHelper;
private InfractionDAO infractionDAO;
public OffenderDAO (Context context) {
dbHelper = new SqLiteManager(context);
infractionDAO = new InfractionDAO(context);
}
Offender getOffender(int id) {
infractionDAO.getInfractions(id);
}
}
What I would do, is the following:
Solution 1.
create a base class(maybe make abstract?) for both, where they share the common things and in each of the separate classes, offernder and infraction put the different stuff.
Solution 2.
I would create a singleton class that in the constructor initializes both.
like in the following linke:
http://pastebin.com/84g9SgFT
Hey! I want to use a singleton class, because if I open the database every activity I get "Leak found"( that happens because I open the database even if it is already open ) . I create a singleton class , but I don't know how should I use it.
Here is my class:
package com.ShoppingList;
import com.ShoppingList.databases.DbAdapter;
public class DbManager {
DbAdapter db;
// singleton
private static DbManager instance = null;
private DbManager() {
}
public static DbManager getInstance() {
if (instance == null)
instance = new DbManager();
return instance;
}
public void setinstance(DbAdapter db){
this.db=db;
}
public DbAdapter getinstancedb(){
return db;
}
}
In the first activity I put :
db = new DbAdapter(this);
db.open();
DbManager.getInstance().setinstance(db);
and for the next activity : DbManager.getInstance().getinstancedb(); but I get force close for second activity.
Can anyone help me how to use it? Thanks...
You can extend Application class and create there an instance of DbAdapter. This way it will be shared by all your activities.
Because db has the same context and life cycle of your first activity. Make your methods public and make them do all the setup/teardown necessary to return your desired result.
regarding the leak warning. Are you closing your db manager connection in onDestroy()?