I'm using room database in my application. Android emulator and %99 of my user's devices has no problem with it, but some of my users gets that error.
java.lang.IllegalStateException: Couldn't read row 0, col 1 from CursorWindow. Make sure the Cursor is initialized correctly before accessing data from it.
Full Error
android.database.CursorWindow.nativeGetString (CursorWindow.java)
android.database.AbstractWindowedCursor.getString (AbstractWindowedCursor.java:66)
me.ibrahimsn.applock.data.local.app.AppDao_Impl.getAllApps (AppDao_Impl.java:202)
me.ibrahimsn.applock.data.local.app.AppRepository.getAll (AppRepository.java:20)
me.ibrahimsn.applock.ui.start.StartViewModel.updateApps (StartViewModel.java:51)
me.ibrahimsn.applock.ui.start.StartActivity.onCreate (StartActivity.java:48)
I'm using it with Dagger 2 and here is my classes
#Database(entities = {App.class}, version = 1, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
private static AppDatabase INSTANCE;
public abstract AppDao appDao();
public static AppDatabase getAppDatabase(Context context) {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, "mydb")
.allowMainThreadQueries().build();
}
return INSTANCE;
}
public static void destroyInstance() {
INSTANCE = null;
}
}
Dagger Module
#Module
public class ApplicationModule {
#Provides
#Singleton
AppDatabase provideRoomDatabase(Context context) {
return AppDatabase.getAppDatabase(context);
}
#Provides
#Singleton
AppDao provideAppDao(AppDatabase appDatabase) {
return appDatabase.appDao();
}
}
Dao
#Dao
public interface AppDao {
#Query("SELECT * FROM app")
List<App> getAllApps();
#Query("SELECT * FROM app WHERE label LIKE :query ORDER BY status DESC")
List<App> findApps(String query);
#Query("SELECT * FROM app WHERE status = 1")
List<App> getLockedApps();
#Insert
void insertAll(App... apps);
#Update
void update(App app);
#Query("UPDATE app SET status = :st")
void updateAll(Integer st);
#Delete
void delete(App app);
}
Repository
#Singleton
public class AppRepository {
private final AppDao appDao;
#Inject
public AppRepository(AppDao appDao) {
this.appDao = appDao;
}
public List<App> getAll() {
return appDao.getAllApps();
}
public List<App> find(String query) {
return appDao.findApps(query);
}
public void insert(App... apps) {
appDao.insertAll(apps);
}
public void update(App app) {
appDao.update(app);
}
public void updateAll(Boolean status) {
appDao.updateAll(status ? 1 : 0);
}
public void delete(App app) {
appDao.delete(app);
}
}
And here is the error file
public class StartViewModel extends ViewModel {
private final AppRepository repository;
private final PackageManager packageManager;
private final IconHelper iconHelper;
private final MutableLiveData<Boolean> status = new MutableLiveData<>();
#Inject
StartViewModel(AppRepository repository, PackageManager packageManager) {
this.repository = repository;
this.packageManager = packageManager;
this.iconHelper = new IconHelper(packageManager);
}
void updateApps() {
Intent launcherIntent = new Intent(Intent.ACTION_MAIN, null);
launcherIntent.addCategory(Intent.CATEGORY_LAUNCHER);
List<ResolveInfo> Resolves = this.packageManager.queryIntentActivities(launcherIntent, 0);
List<String> AppPackages = new ArrayList<>();
List<String> ResolvePackages = new ArrayList<>();
List<App> Apps = repository.getAll(); //Error is here
for(App a : Apps) AppPackages.add(a.getPackageName());
Related
I am creating application with following architecture: Room Database <-> Repository <-> View Model <-> Fragments. What I want to achieve is to execute queries from View Model via Repository via Dao object with some given variables. I have created query for selecting some data from the table in Dao object:
#Query("SELECT * FROM meal_table WHERE mealDate = :date")
public LiveData<MealWithProducts> getMealWithProductsFromDate(String date);
Then I have set fixed value in Repository class for testing purposes, something like:
MealWithProductsRepository.class
public class MealWithProductsRepository {
private final MealWithProductsDao mealWithProductsDao;
private final LiveData<MealWithProducts> mealWithProductsFromDate;
public MealWithProductsRepository(Application application){
AppDatabase db = AppDatabase.getDatabase(application);
mealWithProductsDao = db.mealWithProductsDao();
mealWithProductsFromDate = mealWithProductsDao.getMealWithProductsFromDate("15-08-2021");
}
public LiveData<MealWithProducts> getMealWithProductsFromDate() {
return mealWithProductsFromDate;
}
}
DiaryEntryViewModel.class
public class DiaryEntryViewModel extends AndroidViewModel {
private final MealWithProductsRepository mealWithProductsRepository;
private final LiveData<MealWithProducts> mealWithProductsFromDate;
public DiaryEntryViewModel(#NonNull Application application) {
super(application);
mealWithProductsRepository = new MealWithProductsRepository(application);
mealWithProductsFromDate = mealWithProductsRepository.getMealWithProductsFromDate();
}
public LiveData<MealWithProducts> getMealWithProductsFromDate() { return mealWithProductsFromDate; }
}
I have tested this single case and it worked, however I do not know how to pass parameters from View Model class into the repository since this query is executed in Repository constructor as well as Repository reference in View Model is created in View Model constructor. Could You please point me a direction on how should I proceed further with this issue. Thanks in advance!
public class DiaryEntryViewModel extends AndroidViewModel {
private final MealWithProductsRepository mealWithProductsRepository;
private final LiveData<MealWithProducts> mealWithProductsFromDate;
public DiaryEntryViewModel(#NonNull Application application) {
super(application);
mealWithProductsRepository = new MealWithProductsRepository(application);
}
public LiveData<MealWithProducts> getMealWithProductsFromDate(String date) { return mealWithProductsRepository.getMealWithProductsFromDate(); }
}
public class MealWithProductsRepository {
private final MealWithProductsDao mealWithProductsDao;
private final LiveData<MealWithProducts> mealWithProductsFromDate;
public MealWithProductsRepository(Application application){
AppDatabase db = AppDatabase.getDatabase(application);
mealWithProductsDao = db.mealWithProductsDao();
}
public LiveData<MealWithProducts> getMealWithProductsFromDate(String date) {
return mealWithProductsDao.getMealWithProductsFromDate(date);
}
}
#Query("SELECT * FROM meal_table WHERE mealDate = :date")
public LiveData<MealWithProducts> getMealWithProductsFromDate(String date);
Why Don't you use like this?
I am using room persistence library for save some data and get these data.
Firstly I insert some data to room and after that I try to get inserted data in another fragment. But first time it is empty, after opening fragment second time it shows data. What I forget?
#Database(entities = {Cart.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
private static AppDatabase INSTANCE;
public abstract CartDao m=CartDao();
public static synchronized AppDatabase getInstance(Context ctx) {
if (INSTANCE == null)
INSTANCE = Room.databaseBuilder(ctx.getApplicationContext(),
AppDatabase.class, "db")
.fallbackToDestructiveMigration()
.enableMultiInstanceInvalidation()
.build();
return INSTANCE;
}
}
public class CartRepository {
private Application application;
private static CartRepository CartRepository;
private CartDao mCartDao;
private CartRepository(Application application) {
this.application = application;
AppDatabase db;
db = AppDatabase.getInstance(application);
mCartDao = db.mCartDao();
}
public synchronized static CartRepository getInstance(Application application) {
if (CartRepository == null) {
CartRepository = new CartRepository(application);
}
return CartRepository;
}
public void addCart(Cart cart) {
InsertAsyncTask task = new InsertAsyncTask(mCartDao);
task.execute(cart);
}
public List<Cart> getCartList() throws ExecutionException, InterruptedException {
return new GetAllCartAsyncTask(mCartDao).execute().get();
}
private static class InsertAsyncTask extends AsyncTask<Cart, Void, Void> {
private CartDao asyncTaskDao;
InsertAsyncTask(CartDao dao) {
asyncTaskDao = dao;
}
#Override
protected Void doInBackground(final Cart... result) {
asyncTaskDao.addCart(result[0]);
return null;
}
}
private static class GetAllCartAsyncTask extends AsyncTask<Void, Void, List<Cart>> {
private CartDao asyncTaskDao;
GetAllCartAsyncTask(CartDao dao) {
asyncTaskDao = dao;
}
#Override
protected List<Cart> doInBackground(Void... voids) {
return asyncTaskDao.getCartList();
}
}
}
FirstFragment
mCartRepository = CartRepository.getInstance(getActivity().getApplication());
mCartRepository .addCart(cart);
SecondFragment
mCartRepository = CartRepository.getInstance(getActivity().getApplication());
List<Cart> cartList = mCartRepository.getCartList();
// cartList.size is 0 after inserting, only after start fragment again it shows data
if (cartList.size() > 0) {
mList.addAll(cartList);
item_id = cartList.get(0).getItem_id();
}
I have the CidVo class:
#Entity
public class CidVo implements Serializable {
private static final long serialVersionUID = 6128323407787450445L;
#NonNull
#PrimaryKey
private String idCid;
private String dsCid;
//private StatusType idStatus;
public CidVo() {
super();
}
public String getIdCid() {
return idCid;
}
public void setIdCid(String idCid) {
this.idCid = idCid;
}
public String getDsCid() {
return dsCid;
}
public void setDsCid(String dsCid) {
this.dsCid = dsCid;
}
/*public StatusType getIdStatus() {
return idStatus;
}
public void setIdStatus(StatusType idStatus) {
this.idStatus = idStatus;
} */
}
The DAO class:
#Dao
public interface CIDDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
void insertAllCID(ArrayList<CidVo>... cidVos);
}
I get a list of objects, via JSON. And I would like to save it in the database.
But, I get this return error when trying to compile:
Error:Entity class must be annotated with #Entity
E:\workspace_android_studio\app_atendimento_branch_test\app\src\main\java\br\com\sisteplan\app\atendimento\Model\CIDDao.java
Error:(19, 43) error: Type of the parameter must be a class annotated
with #Entity or a collection/array of it.
How to solve this question?
The following is the postExecute:
#Override
protected void onPostExecute(AsyncTaskResult<Retorno> respAtestadoVo) {
super.onPostExecute(respAtestadoVo);
if (respAtestadoVo.getExceptionResult() == null) {
RetornoCid retornoCid = (RetornoCid) respAtestadoVo.getResult();
ArrayList<CidVo> cidVos = (ArrayList<CidVo>) retornoCid.getRetorno();
appDataBase.getCidDao().insertAllCID(cidVos);
Toast.makeText(context, "Sucesso", Toast.LENGTH_SHORT).show();
}
}
Instance:
#Database(entities = {CidVo.class}, version = 1)
public abstract class AppDataBase extends RoomDatabase {
public abstract CIDDao getCidDao();
private static AppDataBase appDataBase;
public static AppDataBase getInstance(Context context){
if(null == appDataBase){
appDataBase = buildDataBaseInstance(context);
}
return appDataBase;
}
private static AppDataBase buildDataBaseInstance(Context context){
return Room.databaseBuilder(context,
AppDataBase.class,
"app_database")
.allowMainThreadQueries().build();
}
private void cleanUp(){
appDataBase = null;
}
}
The reason is a vararg notation here void insertAllCID(ArrayList<CidVo>... cidVos);
You're trying to insert an array of array lists.
So, just replace it with
void insertAllCID(List<CidVo> cidVos);
Also, it's better to use interfaces over implementations, when you're working with collections. So, you can change this part
RetornoCid retornoCid = (RetornoCid) respAtestadoVo.getResult();
ArrayList<CidVo> cidVos = (ArrayList<CidVo>) retornoCid.getRetorno();
to this
RetornoCid retornoCid = (RetornoCid) respAtestadoVo.getResult();
List<CidVo> cidVos = new ArrayList(retornoCid.getRetorno());
I have been beating my head against the wall and I cannot understand why this is happening. I am working with the new Architectural Components for Android and I am having problems updating a LiveData with a List of Objects.
I have two spinners. When i change the option in the first one, The second one must have its content changed. But this last part is not happening.
Can anyone help me?
State.java
#Entity(tableName = "states")
public class State{
#PrimaryKey(autoGenerate = false)
private int id;
private String name;
#ColumnInfo(name = "countryId")
private String CountryId;
#Ignore
private Object geoCenter, geoLimit;
public State(){
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCountryId() {
return CountryId;
}
public void setCountryId(String countryId) {
CountryId = countryId;
}
}
StateDAO
#Dao
public interface StateDao {
#Query("SELECT * FROM states")
LiveData<List<State>> getAllStates();
#Query("SELECT * FROM states WHERE countryId = :countryID")
LiveData<List<State>> getStatesFromCountry(String countryID);
#Query("SELECT COUNT(*) FROM states")
int getNrStates();
#Query("SELECT COUNT(*) FROM states WHERE countryId = :countryID")
int getNrStatesByCountry(String countryID);
#Insert(onConflict = IGNORE)
void insertAll(List<State> states);
#Delete
void delete(State state);
}
StateRepository
#Singleton
public class StatesRepository {
private final WebServices services;
private final StateDao stateDao;
private final Executor executor;
#Inject
public StatesRepository(Executor executor, StateDao stateDao, WebServices services) {
this.services = services;
this.stateDao = stateDao;
this.executor = executor;
}
public LiveData<List<State>> getStates(String token){
refreshStates(token);
return stateDao.getAllStates();
}
public LiveData<List<State>> getStatesFromCountry(String countryID){
return stateDao.getStatesFromCountry(countryID);
}
private void refreshStates(final String token){
executor.execute(() -> {
Log.d("oooooo", stateDao.getNrStates() + "");
if(stateDao.getNrStates() == 0){
try {
Response<List<State>> response = services.getStates("Bearer "+token).execute();
stateDao.insertAll(response.body());
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
}
StateViewModel
public class StatesViewModel extends ViewModel {
private LiveData<List<State>> states;
private StatesRepository repo;
#Inject
public StatesViewModel(StatesRepository repository){
this.repo = repository;
}
public void init(String token){
states = repo.getStates(token);
}
public void getStatesFromCountry(String countryID){
states = repo.getStatesFromCountry(countryID);
}
public LiveData<List<State>> getStates(){
return this.states;
}
}
Fragment
public class EditAddressFragment extends LifecycleFragment implements View.OnClickListener, Injectable{
private Spinner country, city, state, zip_code;
private String token;
private List<Country> countries;
private List<City> cities;
private List<State> states;
#Inject ViewModelFactory viewModelFactory;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.addresses_edit_layout, container, false);
city = view.findViewById(R.id.city);
state = view.findViewById(R.id.state);
country = view.findViewById(R.id.country);
...
countries = new ArrayList<>();
cities = new ArrayList<>();
states = new ArrayList<>();
return view;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
CountrySpinnerAdapter adapter = new CountrySpinnerAdapter(getActivity(), android.R.layout.simple_spinner_item, countries);
country.setAdapter(adapter);
CitySpinnerAdapter cityAdapter = new CitySpinnerAdapter(getActivity(), android.R.layout.simple_spinner_item, cities);
city.setAdapter(cityAdapter);
StateSpinnerAdapter stateAdapter = new StateSpinnerAdapter(getActivity(), android.R.layout.simple_spinner_item, states);
state.setAdapter(stateAdapter);
CountriesViewModel countriesViewModel = ViewModelProviders.of(this, viewModelFactory).get(CountriesViewModel.class);
countriesViewModel.init(token);
countriesViewModel.getCountries().observe(this, adapter::setValues);
CityViewModel cityViewModel = ViewModelProviders.of(this, viewModelFactory).get(CityViewModel.class);
cityViewModel.init(token);
cityViewModel.getCities().observe(this, cityAdapter::setValues);
StatesViewModel statesViewModel = ViewModelProviders.of(this, viewModelFactory).get(StatesViewModel.class);
statesViewModel.init(token);
statesViewModel.getStates().observe(this, states -> {
Log.d("called", states.toString());
stateAdapter.setValues(states); } );
country.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
Country c = (Country) adapterView.getItemAtPosition(i);
Log.d("cd", c.getId());
//states = new ArrayList<State>();
statesViewModel.getStatesFromCountry(c.getId());
}
#Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
....
Adapter
public void setValues(List<State> states)
{
this.states = states;
Log.d("s", states.isEmpty()+" "+states.toString());
notifyDataSetChanged();
}
Well, I have reached a solution for this issue and found out how this LiveData things works.
Thanks to #MartinMarconcini for all his help is debugging ;)
So apparently, the observers are linked to the object you first set it up to. You cannot replace the object (by attribution) or otherwise it will not work.
Also, if the value of your variable is going to change then you should use MutableLiveData
So the change necessary were:
1. Change from LiveData to MutableLiveData and pass that MutableLiveData to the repository when you need to update it
public class StatesViewModel extends ViewModel {
private MutableLiveData<List<State>> states; ;;CHANGED
private StatesRepository repo;
#Inject
public StatesViewModel(StatesRepository repository){
this.repo = repository;
}
public void init(String token){
states = repo.getStates(token);
}
public void getStatesFromCountry(String countryID){
repo.getStatesFromCountry(this.states, countryID); ;;CHANGED
}
public LiveData<List<State>> getStates(){
return this.states;
}
}
2. In the repository, update the MutableLiveData using setValue
#Singleton
public class StatesRepository {
private final WebServices services;
private final StateDao stateDao;
private final Executor executor;
#Inject
public StatesRepository(Executor executor, StateDao stateDao, WebServices services) {
this.services = services;
this.stateDao = stateDao;
this.executor = executor;
}
public MutableLiveData<List<State>> getStates(String token){
refreshStates(token);
final MutableLiveData<List<State>> data = new MutableLiveData<>();
data.setValue(stateDao.getAllStates());
return data;
}
;; CHANGED
public void getStatesFromCountry(MutableLiveData states, final String countryID){
states.setValue(stateDao.getStatesFromCountry(countryID));
}
private void refreshStates(final String token){
executor.execute(() -> {
if(stateDao.getNrStates() == 0){
try {
Response<List<State>> response = services.getStates("Bearer "+token).execute();
stateDao.insertAll(response.body());
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
}
3. Changed the DAO to return List instead of LiveData>
#Dao
public interface StateDao {
#Query("SELECT * FROM states")
List<State> getAllStates();
#Query("SELECT * FROM states WHERE ctrId = :countryID")
List<State> getStatesFromCountry(String countryID);
#Query("SELECT COUNT(*) FROM states")
int getNrStates();
#Query("SELECT COUNT(*) FROM states WHERE ctrId = :countryID")
int getNrStatesByCountry(String countryID);
#Insert(onConflict = IGNORE)
void insertAll(List<State> states);
#Delete
void delete(State state);
}
4.Finally allow to perform queries in the main thread
AppModule.java
#Singleton #Provides
AppDatabase provideDb(Application app) {
return Room.databaseBuilder(app, AppDatabase.class,"unitail.db")
.allowMainThreadQueries()
.fallbackToDestructiveMigration()
.build();
}
Dao must be same across all operations.
You use different Dao instance for insert and observe
You should not update the livedata reference once you set it and start observing it.Instead to update the live data with repository you should use the MediatorLiveData.
In your case do the following changes:
private MediatorLiveData<List<State>> states; // change
.....
.....
states.addSource(repo.getStatesFromCountry(countryID), newData -> states.setValue(newData)); //change
Writing an answer for better discussion.
So I have (in Kotlin, sry) a model that is a list of notes (it’s just a sandbox app to play w/all this) and here’s my architecture: I don’t have a Repo, but I have Activity -> ViewModel -> Dao.
So Dao exposes a LiveData<MutableList<Note>>
#Query("SELECT * FROM notes")
fun loadAll(): LiveData<MutableList<Note>>
My ViewModel… exposes it through:
val notesList = database.notesDao().loadAll()
and my Activity (onCreate) does…
viewModel.notesList.observe(this,
Observer<MutableList<Note>> { notes ->
if (notes != null) {
progressBar?.hide()
adapter.setNotesList(notes)
}
})
This works. The adapter is a RecyclerView adapter that does literally nothing but:
fun setNotesList(newList: MutableList<Note>) {
if (notes.isEmpty()) {
notes = newList
notifyItemRangeInserted(0, newList.size)
} else {
val result = DiffUtil.calculateDiff(object : DiffUtil.Callback() {
override fun getOldListSize(): Int {
return notes.size
}
override fun getNewListSize(): Int {
return newList.size
}
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return notes[oldItemPosition].id == newList[newItemPosition].id
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val (id, title, _, priority) = newList[newItemPosition]
val (id1, title1, _, priority1) = notes[oldItemPosition]
return id == id1
&& priority == priority1
&& title == title1
}
})
notes = newList
result.dispatchUpdatesTo(this)
}
}
If ANY other part of the app modifies that list of notes, the adapter updates automagically. I hope this gives you a playground to try a simple(r?) approach.
Working with room persistence, when trying to get the database to insert or make select in the items, the error appears:
AppDataBase.getMovieDao()' on a null object reference
The classes related to the process are as follows:
AppDataBase class:
#Database(entities = {Movies.class}, version = 1)
public abstract class AppDataBase extends RoomDatabase {
public static final String DB_NAME = "Movies";
private static AppDataBase INSTANCE;
public static AppDataBase getDataBase(Context context){
if (INSTANCE == null){
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),AppDataBase.class,DB_NAME).build();
}
return INSTANCE;
}
public abstract MovieDao getMovieDao();
}
Dao Class:
#Dao
public interface MovieDao {
#Insert
void insertAll(Movies movies);
#Update
void updateAll(Movies... notes);
#Query("SELECT * FROM moviestb")
List<Movies> getAll();
#Delete
void deleteAll(Movies... notes);
}
Entity class:
#Entity(tableName = "moviestb")
public class Movies {
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "idmovie")
private long id;
#ColumnInfo(name = "titlemovie")
private String titlemovie;
........
}
Searching for the registrations:
public void loadFromDB(){
db.getDataBase(view.getContext());
if(db.getMovieDao().getAll().size() > 0){
adapter.setResults(db.getMovieDao().getAll());
}else{
Toast.makeText(view.getContext(),"Não há filmes cadastrados",Toast.LENGTH_SHORT).show();
view.getActivity().finish();
}
}
Insert:
public View.OnClickListener onSaveClick(final String plot, final String diretor, final String autor,
final String nome, final String tipo, final String ano, final String ator, final String imdb) {
return new View.OnClickListener() {
#Override
public void onClick(View itemView) {
db.getDataBase(view.getContext());
Movies movies = new Movies(nome,plot,imdb,"",ator,ano,tipo,diretor,autor);
new InsertAsyncTask(db).execute(movies);
}
};
}
private class InsertAsyncTask extends AsyncTask<Movies,Void,Void>{
private AppDataBase db;
public InsertAsyncTask(AppDataBase appDataBase) {
db = appDataBase;
}
#Override
protected Void doInBackground(Movies... params) {
db.getMovieDao().insertAll(params[0]);
return null;
}
}
Searching all:
public void loadFromDB(){
db.getDataBase(view.getContext());
if(db.getMovieDao().getAll().size() > 0){
adapter.setResults(db.getMovieDao().getAll());
}else{
Toast.makeText(view.getContext(),"Não há filmes cadastrados",Toast.LENGTH_SHORT).show();
view.getActivity().finish();
}
}
The crash occurs while fetching the database, what am I doing wrong? thank you!
db is null, apparently. Your question does not show where you ever assign it a value.
Please note that getDataBase() is a static method. Perhaps you should have a db=AppDataBase.getDataBase() statement somewhere.