I am currently using objectbox via koin dependency injection within my android app. It works fine however i need to re-initialise my DI and so i need to destroy the boxStore before. This is because i initialise the box via DI and if i do not destroy the current BoxStore I cannot create a new one.
I've found a similar post How to close Objectbox Store and delete data files however it hasn't solved my issue.
I have tried calling deleteAllFiles however i am getting an error.
BoxStore.deleteAllFiles(context, (BoxStoreBuilder.DEFAULT_NAME))
BoxStore.deleteAllFiles(context, null)
I am getting the error message:
java.lang.IllegalStateException: Cannot delete files: store is still open
this is on the line of code mentioned above. Any suggestions would be very helpful
solution:
i had to access each of my boxes individually and delete them one at a time.
fun clearAll(){
firstBox.box.removeAll()
secondBox.box.removeAll()
.......
}
fun closeAll(){
firstBox.box.close()
secondBox.box.close()
.......
}
You can just close the boxStore and then delete all files:
boxStore.close();
boxStore.deleteAllFiles();
There's also a static method for deleting all the files ( if you want to delete all files before you open boxStore )
It's the most efficient way.
Ref: https://github.com/objectbox/objectbox-java/issues/317
I can suggest a solution. You can get all entity classes and get each box and clear them.
boxStore.getAllEntityClasses().forEach( entityClass ->
boxStore.boxFor(entityClass
).removeAll());
Related
I am working on adding data persistence to an app using the Room library, based on this documentation. My database only has one column where a String corresponding to a user-selected radio button is to be placed. However, as soon as I attempt to insert a new object (in this case, an Attempt), it gives me the following error:
java.lang.ClassCastException: android.app.Application cannot be cast to com.example.mathreps.MathRepsApplication.
I believe this error comes from the following block of code within my Rating fragment:
private val dataViewModel: MathRepsViewModel by activityViewModels {
MathRepsViewModel.MathRepsViewModelFactory(
(activity?.application as MathRepsApplication).database
.attemptDao()
)
}
Which relates to a different class called MathRepsApplication
class MathRepsApplication: Application() {
val database: AttemptRoomDatabase by lazy {AttemptRoomDatabase.getDatabase(this)}
}
Since this problem most likely relates to multiple classes and packages, here is the link to the repository for the project.
My attempts to fix this have been unsuccessful, with them mostly consisting of heavily reviewing the database implementation and not much else, as I don't exactly know where to start.
In the app I'm working on, we had a complex manual migration that required data parsing, manual SQL commands, etc. This was to convert a List<X> column into a new linked table of X. I've previously written about the approach, but the specific commands are not especially relevant for this question.
The issue I'm encountering is ~1% of users are experiencing a crash as part of this migration. This cannot be reproduced in testing, and due to our table's size, Crashlytics cannot show any useful error:
Losing customer data isn't catastrophic in this context, but being stuck in the current "try migrate, crash, reopen app and repeat" loop is. As such, I want to just give up on the migration and fall back to a destructive migration if we encounter an exception.
Any ideas how this can be done? My current solution is rerunning the DB changes (but not the presumably failing data migration) inside the catch, but this feels very hacky.
Our database is defined as:
Room.databaseBuilder(
context.applicationContext,
CreationDatabase::class.java,
"creation_database"
)
.addMigrations(MIGRATION_11_12, MIGRATION_14_15)
.fallbackToDestructiveMigration()
.build()
where MIGRATION_14_15 is:
private val MIGRATION_14_15 = object : Migration(14, 15) {
override fun migrate(database: SupportSQLiteDatabase) {
try {
// database.execSQL create table etc
} catch (e: Exception) {
e.printStackTrace()
// Here is where I want to give up, and start the DB from scratch
}
}
}
The problem you have is that you cannot (at least easily) invoke the fall-back as that is only invoked when there is no migration.
What you could do is to mimic what fall back does (well close to what it does). That is the fall-back will delete (I think) the database file and create the database from scratch and then invoke the databases _Impl (generated java) createAllTables method.
However, you would likely have issues if you deleted the file as the database connection has been passed to the migration.
So instead you could DROP all the app's tables using the code copied from the dropAllTables method from the generated java. You could then follow this with the code from the createAllTables method.
These methods are in the generated java as the class that is the same as the class that is annotated with #Database suffixed with _Impl.
The gotcha, is that the exception
(Expected .... Found ....) that you have shown is NOT within the migration but after the migration when Room is trying to build the database, so you have no control/place to do the above fall-back mimic unless this was done for all 14-15 migrations.
Perhaps what you could do is to trap the exception, present a dialog requesting the user to uninstall the app and to then re-install. This would then bypass the migration as it would be a fresh install.
So currently I have a Dao with a function that emits a Flow<>
#Query("SELECT * FROM ${Constants.Redacted}")
fun loadAllContacts(): Flow<List<Redacted>>
I am calling this from a repository like so
val loadAllContacts: Flow<List<Redacted>> = contactDao.loadAllContacts()
I am injecting the repository into the viewModel's constructor, and then at the top of my viewModel I have a val like so
val contacts: LiveData<List<Redacted>> = contactRepository.loadAllContacts.asLiveData()
Which is being observed in my Activity like so
viewModel.contacts.observe(this) { contacts ->
viewModel.onContactsChange(contacts)
}
My thinking is that the Flow is converted to a LiveData, and then I can observe this LiveData from my activity and kick off this function to actually update the viewModel upon the data being updated.
For now onContactsChange just looks like
fun onContactsChange(list: List<Redacted>) {
Timber.i("VIEW UPDATE")
}
The problem is that I only see this Timber log upon opening the activity, and never again. I verified that data IS going into my database, and I verified that an insert occurred successfully while the activity & viewModel are open. But I never see the log from onContactsChange again. When I close the activity, and reopen it, I do see my new data, so that is another reason I know my insert is working correctly.
I would like to add that I am using a single instance (singleton) of my repository, and I think I can verify this by the fact that I can see my data at all, at least when the view is first made.
Figured it out:
Note: If your app runs in a single process, you should follow the singleton design pattern when instantiating an AppDatabase object. Each RoomDatabase instance is fairly expensive, and you rarely need access to multiple instances within a single process.
If your app runs in multiple processes, include enableMultiInstanceInvalidation() in your database builder invocation. That way, when you have an instance of AppDatabase in each process, you can invalidate the shared database file in one process, and this invalidation automatically propagates to the instances of AppDatabase within other processes.
It's a little bit hard to follow your question, but I think I see the overall problem with your Flow object not updating the way you want it too.
Following this quick tutorial, it seems that first you should declare your Flow object inside your Repository the same way you're already doing
val loadAllContacts: Flow<List<Redacted>> = contactDao.loadAllContacts()
and have your VM 'subscribe' to it by using the collect coroutine which would then allow you to dump all this data into a MutableLiveData State
data class YourState(..)
val state = MutableLiveData<YourState>()
init {
contactRepository.loadAllContacts().collect {
if (it.isNotEmpty()) {
state.postValue(YourState(
...
)
}
}
}
that your Activity/Fragment could then observe for changes
viewModel.state.observe(.. { state ->
// DO SOMETHING
})
P.S. The tutorial also mentions that because of how Dao's work, you might be getting updates for even the slightest of changes, but that you can use the distinctUntilChanged() Flow extension function to get more specific results.
So i was trying to use clear function of kotlin while building an app in android studio though clear is kotlin built in function it is giving an unresolved reference error my code is:
Var peerListListener= WifiP2pManager.PeerListListner(){
val refreshedPeers= peerList.deviceList
If(!refreshedPeers.equals(peers)){
Peers.clear()
}
}
Please help me to resolve this issue and this code is done outside the oncreate function
peers is defined as:
var peers:List<WifiP2pDevice>=mutableListof<WifiP2pDevice>()
I have tried declaring this both globally and locally
First you got typo, you're defining the variable as peer but trying to use clear on Peer.
Second you're exposing peer as an immutable list by saying List<WifiP2pDevice> and List interface does not have clear as it is immutable. Expose peer as mutable list by defining it as MutableList<WifiP2pDevice> and you will be able to use clear().
I've been struggling performing a simple migration. What I just want to achieve is add a new Class in realm.
The code below is inside a method that is called inside onCreate.
Realm.init(this)
val config = RealmConfiguration.Builder()
.name("db_name")
.schemaVersion(5L)
.migration { realm, oldVersion, newVersion ->
val schema = realm.schema
var _oldVersion = oldVersion
if (_oldVersion == 4L) {
if (schema.contains(XModel::class.java.simpleName))
schema.remove(XModel::class.java.simpleName)
if (!schema.contains(XModel::class.java.simpleName))
schema.create(XModel::class.java.simpleName)
.addField(XModel::id.name, Long::class.javaPrimitiveType,
FieldAttribute.PRIMARY_KEY)
...
.addField(XModel::N.name, Int::class.javaPrimitiveType)
_oldVersion += 1
}
}
.build()
Realm.setDefaultConfiguration(config)
As what the title suggest, the new class in the schema was created inside the migration object, but when I try to access it in other parts of the application using a realm query or a simple call to schema.get("XModel") it will throw an error XModel doesn't exist in current schema. Any comment will really help. Thank you...
Edit:
Additional information. I have 2 realm objects, each are in different android modules, one module is dependent to the other. I somehow have some progress, now Im a bit confuse, do I need to declare 2 configurations? Then it would mean 2 realm instance? How to switch from both, I want to merge them into 1 realm.
Edit2:
Another clarification about realm. If you have 2 android modules, each of them using realm, will they have different realm even if in the realm configuration they have the same name?
Background
I want to give you a background of what im doing because I think its needed to fully understand my case.
Originally I only have one module, but then after refactoring and also because of future apps to be develop, I need to pull out the common classes from the existing module and put it in a separate lower-level module that the future apps can depend on. This new lower-level module will also be responsible for most of the data layer, so realm was transferred to this module. But I can't just ignore the realm of the existing app because some users might already populated it, and I need to transfer those data to the new database.