I have problem working with realm.
findAll and findAllAsync doesnot return any data from realm.
I am updating realm object from main thread like this.
public void updatePhoto(final int ticketID) {
realm.beginTransaction();
RealmResults ticketPhotos = realm.where(TicketPhoto.class).equalTo("TicketID", ticketID).findAll();`
for (TicketPhoto ticketPhoto : ticketPhotos) {
ticketPhoto.IsModified = true;
}
realm.commitTransaction();
} '$'
At same time one background service is running for every five minutes and keeps checking for any objects having IsModified flag as true. From my background service(IntentService), am using AsyncTask and in doInBackground, am trying to get those IsModified records and I assume realm should pick those records and update with my server. Used the below code to get data from realm.
public RealmResults getTicketPhotosToSave (){
return realm.where(TicketPhoto.class)
.equalTo("IsModified", true)
.findAll();
}
When am still in the same Photo activity where I save photo to realm with IsModified flag as true, realm in background service is not picking those records. But when I destroy the app and just run it again, service is now picking those records. Am not sure if am doing something wrong here.
Its working. thanks for your support
Now I understood that unless we are updating the data on same thread, no need to close realm.
We need to close realm always when we need access to those changes in different thread .
Since background thread always needs access to all objects, we have to close and open realm just before accessing data.
So before accessing/querying data, I am refreshing realm as #Zhuinden suggested ( realm not fetching data)
and then realm.Close(). After this I am creating instance (realm = Realm.getDefaultInstance(); )
I really hate recommending this solution, but you should force a refresh with the package-internal methods after Realm.getInstance() on your IntentService's Realm instance. This current solution I provide works for v1.2.0. Use it only on background threads (primarily your periodically running method).
package io.realm; // <---- this is important
public class RealmRefresh {
public static void refreshRealm(Realm realm) {
Message message = Message.obtain();
msg.what = HandlerControllerConstants.LOCAL_COMMIT;
realm.handlerController.handleMessage(msg);
}
}
And then call
try {
mRealm = Realm.getDefaultInstance();
RealmRefresh.refreshRealm(mRealm);
// do things
} finally {
if(mRealm != null) {
mRealm.close();
}
}
Related
I am creating an App that will use a SearchView to let user make queries to filter data. I am using RoomDB, and trying to follow Model-View-ViewModel architecture as recommended in Android Developers' Guidelines.
I have one entity and one DAO (for now my DB has only one table). I have a method in the DAO that looks like this:
#Query("SELECT * FROM table WHERE column1 = :search OR column2 = :search")
LiveData<List<TableRow>> filteredSearch(String search);
So, from an AsyncTask I can use the RoomDatabase's instance, right, and obtain the results of an user's query, like this, right?
// Let's assume search already contains user's input
String search;
// DatabaseClient is a singleton that holds MyRoomDatabase instance
// I am using it as Repository for now... bad call?
LiveData<List<TableRow>> user_query = DatabaseClient
.getInstance(getApplicationContext())
// my DatabaseClient has this method that
// I made to call DAO's query method
.getFilteredList(search);
So, I want to load one fragment or another in my Main Activity depending on this user query's length (if 0 results, fragmentA else fragmentB). This is business logic to some extent? I wonder... should I read the query's length from the Viemodel, or from the View (AKA Activity)? As you can see, I am still struggling with RoomDB and ViewModels at all.
My plan was making a method in the ViewModel that returns the LiveData<List<TableRow>> with the query results by using a code snippet similar to the one above, and then, from the MainActivity:
search_view.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
// Should I delegate AsyncTask to the Repository AKA
// DatabaseClient? Maybe... but please bear with me
class UserSearchTask extends AsyncTask<Void, Void, List<TableRows>> {
#Override
protected List<TableRows> doInBackground(Void... voids) {
TableRowsViewModel my_viewmodel = new TableRowsViewModel(getApplication());
LiveData<List<TableRows>> search_results;
search_results = my_viewmodel.getUserSearch(query);
// TODO I will care about type mismatches later
return search_results;
}
#Override
protected void onPostExecute(List<TableRows> found_elements) {
super.onPostExecute(found_elements);
// TODO So, I want to check user's search results from here
if ( found_elements.length > 0) {
showFragmentB();
} else {
showFragmentA();
}
}
}
}
I have been without coding for more than half year, and I am a little rusty. I forgot concepts that I knew before about ViewModels, LiveData and stuff. Am I breaking MVVM's architecture with my approach? What's the role of LiveData in this kind of logic attempt of mine? Can I read the SQL query result's length directly from some LiveData's method, or else, I should retrieve the list from it to do so?
I guess the actual question is: is my approach wrong? What would be the cleanest way to implement my fragment's logic depending on user search's length?
EDIT: I am not asking only about good practices (which are still welcome); I have barely dedicated 7 hours to this app yet and I couldn't still build a first alpha version to start testing it. My first priority in short-term is putting this thing together (and myself together I might add); in other words: first, I want to make it work even if it is not clean. Right now I am a simple-minded monkey which just thinks about this like if this was a normal PC app in which I don't have to struggle with App lifecycles, multithreading and all related stuff,in which I just retrieve the SQL query's result right away. I beg your pardon for my ignorance.
So, in order to add more context about what I am trying to do: If searxh results are zero, fragmentA would be a form for adding a new row to the table; fragmentB would show just the data of the first row, not listing yet (I will reach there eventually, but not yet).
Here is the way I ended up implementing the logic I had in mind when I made the question. But that doesn't mean this is the clean way to do it.
In order to get the size of the User query, I ended up using an Observer (as Teo said in his comment). I am not sure if using Observer and LiveData for a Database that is merely local in the phone's app (and therefore shall only be modified by the App's user himself) for obtaining query results each time the user hits "Search" button, I am not sure if using Oberser and LiveData for this is overkill or not... and the aberration of using DatabaseClient (the RoomDatabase's singleton) as a Repository? Not anymore... I have created a dedicated Repository Class to handle the DatabaseClient and the DAOs.
That said, the relevant part of my Repository class:
public class Repository {
private final TableRowDao tablerow_dao;
public Repository(Application application) {
AppDatabase app_db = DatabaseClient.getInstance(application).getAppDatabase();
tablerow_dao = app_db.tableRowDao();
}
public LiveData<List<TableRow>> getFilteredList(String search) {
return tablerow_dao.filteredSearch(search);
}
// [...]
...here, the ViewModel:
public class TableRowsViewModel extends AndroidViewModel {
private Repository repository;
public TableRowsViewModel(#NonNull Application application) {
super(application);
repository = new Repository(application);
}
public LiveData<List<TableRow>> getUserSearch(String search) {
return repository.getFilteredList(search);
}
No AsyncTasks were used for this purpose.
In MainActivity, within OnCreate method:
search_view.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
my_viewmodel = new TableRowsViewModel(getApplication());
search_results = my_viewmodel.getUserSearch(query);
observeSearchResults(search_results);
return true;
}
// [...]
});
ObserveSearchResults is a private method that I declared in MainActivity as well:
// Having a observer is good and stuff, but am I overdoing it?
private void observeSearchResults(LiveData<List<TableRow>> search_results) {
search_results.observe(this, new Observer<List<TableRow>>() {
#Override
public void onChanged(List<TableRow> rows) {
if ( rows.size() > 0 ) {
// TODO I don't list the results yet, I show the first one right away
profileFragment = ProfileFragment.newInstance(rows.get(0));
transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragmentContainer, profileFragment);
transaction.addToBackStack(null);
transaction.commit();
} else {
transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragmentContainer, insertFragment);
transaction.addToBackStack(null);
transaction.commit();
}
}
});
}
This worked for me, but that doesn't mean that I am doing this in a clean way at all.
In my project I'm using Realm for storing data from API.
Before updating objects to Realm I'd like to check which objects are new (doesn't exist in database) and which objects should be deleted (exist in database, but don't exist in API response).
For checking new objects I'm iterating through API response and using simple Realm query to check which object is new
for(Follower follower: results.data){
Follower followerFromDb = realm.where(Follower.class).equalTo("id", follower.id).findFirst();
if(followerFromDb == null){
Log.d("REALM", "Object is not in the DB");
}
}
My problem is - how to efficiently check which objects should be deleted from the database.
I have a pretty nice trick for deleting objects not in API response, which is that I add an indexed field called #Index private boolean isBeingSaved; to my RealmObject:
public class Thingy extends RealmObject {
//...
#Index
private boolean isBeingSaved;
}
Then as I map the API response to RealmObjects, I set this to true:
ApiResponse apiResponse = retrofitService.getSomething();
Thingy thingy = new Thingy();
thingy.set/*...*/;
thingy.setIsBeingSaved(true);
realm.insertOrUpdate(thingy);
Afterwards, you've set each of these to true for the new elements. So you can do a deletion for all that is false.
realm.where(Thingy.class)
.equalTo(ThingyFields.IS_BEING_SAVED, false)
.findAll()
.deleteAllFromRealm();
Then you'll need to iterate the remaining objects and set their boolean field to false
for(Thingy thingy: realm.where(Thingy.class).findAll()) {
thingy.setIsBeingSaved(false);
}
And it works!
I do not know of a more optimized solution unfortunately, I can clearly see that this is O(N) because of iteration at the end. But you can follow https://github.com/realm/realm-java/issues/762 for bulk update support.
In your particular case, the special flag is isBeingSaved, and I guess you don't want to immediately delete them, but this is how I did it when I needed this functionality.
It sounds like you're database only contains data from the API, and local data is defunkt when an api call response is returned. If that's the case then you can simply delete everything in your database, and add everything from the api response into your Realm.
Realm realm = Realm.getDefaultInstance();
realm.executeTransaction(new Realm.Transaction() {
public void execute(Realm realm) {
realm.deleteAll(); //Delete everything
object.delete(); //Delete specific object
realm.delete(RealmModel) //Delete all of specific type
}
}
Remember to close your realm when you're done.
I need to check some data, whether or not to send a tracking info. This data is saved inside the Realm database. Here is the model:
public class RealmTrackedState extends RealmObject {
#PrimaryKey
private int id = 1;
private RealmList<RealmChat> realmChatsStarted;
private boolean isSupportChatOpened;
private boolean isSupportChatAnswered;
/* getters and setters */
}
The idea is - every chat that is not inside the realmChatsStarted should be tracked and then added to this list. Similar thing for isSupportChatOpened boolean - however because of the business logic this is a special case.
So - I've wrapped this inside one Realm object. And I've wrapped this into few shouldTrack() methods, like this:
#Override
public void insertOrUpdateAsync(#NonNull final RealmModel object, #Nullable OnInsertListener listener) {
Realm instance = getRealmInstance();
instance.executeTransactionAsync(realm -> realm.insertOrUpdate(object), () ->
notifyOnSuccessNclose(listener, instance),
error -> notifyOnErrorNclose(listener, error, instance));
}
#Override
public RealmTrackedState getRealmTrackedState() {
try (Realm instance = getRealmInstance()) {
RealmResults<RealmTrackedState> trackedStates = instance.where(RealmTrackedState.class).findAll();
if (!trackedStates.isEmpty()) {
return instance.copyFromRealm(trackedStates.first());
}
RealmTrackedState trackedState = new RealmTrackedState();
trackedState.setRealmChatsStarted(new RealmList<>());
insertOrUpdateAsync(trackedState, null);
return trackedState;
}
}
#Override
public boolean shouldTrackChatStarted(#NonNull RealmChat chat) {
if (getCurrentUser().isRecruiter()) {
return false;
}
RealmList<RealmChat> channels = getRealmTrackedState().getRealmChatsStarted();
for (RealmChat trackedChats : channels) {
if (trackedChats.getId() == chat.getId()) {
return false;
}
}
getRealmInstance().executeTransaction(realm -> {
RealmTrackedState realmTrackedState = getRealmTrackedState();
realmTrackedState.addChatStartedChat(chat);
realm.insertOrUpdate(realmTrackedState);
});
return true;
}
And for any other field inside RealmTrackedState model happens the same.
So, within the presenter class, where I'm firing a track I have this:
private void trackState(){
if(dataManager.shouldTrackChatStarted(chatCache)){
//track data
}
if(dataManager.shouldTrackSupportChatOpened(chatCache)){
//track data
}
if(dataManager.shouldTrackWhatever(chatCache)){
//track data
}
...
}
And I wonder:
a. How much of a performance impact this would have.
I'm new to Realm, but for me opening and closing a DB looks ... heavy.
I like in this implementation that each should(...) method is standalone. Even though I'm launching three of them in a row - in other cases I'd probably use only one.
However would it be wiser to get this main object once and then operate on it? Sounds like it.
b. I see that I can either operate on synchronous and asynchronous transactions. I'm afraid that stacking a series of synchronous transactions may clog the CPU, and using the series of asynchronous may cause unexpected behaviour.
c. #PrimaryKey - I used this because of the wild copy paste session. Assuming that this class should have only instance - is it a correct way to do this?
ad a.
Realm caches instances so opening and closing instances are not that expensive as it sounds. First time an app is opening a Realm file, a number of consistency checks are performed (primarily does model classes match classes on disk) but next time you open an instance, you don't do this check.
ad b.
If your transactions depend on each other, you might have to be careful. On the other hand, why have multiple transactions? An async transaction will notify you when it has completed which can help me to get the behaviour you except.
ad c.
Primary keys are useful when you update objects (using insertOrUpdate()) as the value is use to decide if you are creating/inserting or updating an object.
I don't have a lot of experience writing Android apps. For fun I am writing an app that will upload my call logs to my server. This entire app is running as a Service. The Service (starts when the phone is booted) is the one that registers the ContentObserver, which then calls my custom CallLog class. I use ContentObserver to listen for content change events. Unfortunately, the ContentObserver is called multiple times when I e.g. dial a number.
For that reason, I have function that I call after a successful upload (I use Retrofit) called markAsUploaded(). This function creates a RealmObject called CallLogUploaded (which is different from my regular CallLog model). This CallLogUploaded simply has one identifier which is the dateTime of the call, which should be unique enough. Then, when I am iterating through the list of all the call logs, I check every single call log against a isDataUploaded() function, which does a Realm query and checks to see if there is already a call log with that dateTime stored in the database (realm). In theory, it should work.
However, I have noticed that it does not always work. It seems that very often my data is stale. When I do realm.isAutoRefresh(), it returns false (although I swear it returned true once). In my isDataUploaded function, even when I do a findAll() on the Realm, I do not see all of my data - but the data definitely did hit the markDataAsUploaded function.
Here's my code - it's in Kotlin but should be easy to understand:
val callLogCall = service.sendCalLLogs(childId, dataToUpload)
callLogCall.enqueue(object : Callback<Void> {
override fun onResponse(call: Call<Void>, response: Response<Void>) {
if (response.isSuccessful) {
Log.i(AppConstants.LOG_TAG, "Call log data uploaded successfully!")
this#CallLogData.markDataAsUploaded(dataToUpload)
} else {
Log.w(AppConstants.LOG_TAG, "Call log data upload failed")
}
}
override fun onFailure(call: Call<Void>, t: Throwable) {
Log.w(AppConstants.LOG_TAG, "Call log data upload error (onFailure) called")
}
})
// This function simply stores a Realm model for all the data that has been uploaded to the server
private fun markDataAsUploaded(dataToUpload: List<CallLog>) {
realm = Realm.getDefaultInstance()
for (data in dataToUpload) {
realm.beginTransaction()
val callLogUploaded = realm.createObject(CallLogUploaded::class.java)
callLogUploaded.callDate = data.callDate
realm.commitTransaction()
}
}
// This function checks to see if the data is already uploaded.
private fun isDataUploaded(callLog: CallLog) : Boolean {
return realm
.where(CallLogUploaded::class.java)
.equalTo("callDate", callLog.callDate)
.count() > 0L
}
// Gets the call logs - not the entire function
for (call in callLogs) {
val callLog = CallLog()
callLog.id = call.id
callLog.callDate = Utilities.getTimestampAsSeconds(call.callDate)
if (this.isDataUploaded(callLog)) {
continue
}
callLog.name = call.name
callLog.number = call.number
}
I am very new to Realm and fairly new to Android development, so I would appreciate any help you can give me. Thanks!
That's because
// This function simply stores a Realm model for all the data that has been uploaded to the server
private fun markDataAsUploaded(dataToUpload: List<CallLog>) {
realm = Realm.getDefaultInstance()
for (data in dataToUpload) {
realm.beginTransaction()
val callLogUploaded = realm.createObject(CallLogUploaded::class.java)
callLogUploaded.callDate = data.callDate
realm.commitTransaction()
}
}
This method has quite a few errors that I had written about a long time ago
The Realm instance is opened, but never closed
There is a new transaction per every element, rather than inserting all elements in a single transaction
If you don't close the Realm instance (each instance requires its own close() call), then your Realm instance will never update unless you actually begin a transaction and do things inside the transaction.
You have three solutions:
1.) do your logic on the background thread inside a transaction, if there is nothing to be done, then cancel the transaction - queries made in transactions are never stale
2.) make sure the Realm instance is closed properly (although this is definitely a necessity on any non-autoupdating thread)
3.) a hacky solution is to call RealmRefresh.refreshRealm() after getDefaultInstance() according to my answer on Stack Overflow which relies on package-private API, but it works to solve this issue
Typically you need to open the Realm instance at the beginning of the thread, and close it at the end of the thread.
so, it's basically one big try(Realm realm = Realm.getDefaultInstance() { ... } for onHandleIntent().
enqueue(new Callback() { #Override public void onSuccess(..) {...} runs on the UI thread. To run it on the current thread, you should use call.execute().
instead of
for (data in dataToUpload) {
realm.beginTransaction()
val callLogUploaded = realm.createObject(CallLogUploaded::class.java)
callLogUploaded.callDate = data.callDate
realm.commitTransaction()
}
do
realm.beginTransaction()
for (data in dataToUpload) {
val callLogUploaded = realm.createObject(CallLogUploaded::class.java)
callLogUploaded.callDate = data.callDate
}
realm.commitTransaction()
In order to understand about version retention you can read https://medium.com/#Zhuinden/understanding-realm-version-retention-and-synchronization-9a513c2445bb .
I am not really sure where my problem lies, as I am experimenting in two areas that I don't have much experience with: JPA and Futures (using Play! Framework's Jobs and Promises).
I have the following bit of code, which I want to return a Meeting object, when one of the fields of this object has been given a value, by another thread from another HTTP request. Here is what I have:
Promise<Meeting> meetingPromise = new Job<Meeting> () {
#Override
public Meeting doJobWithResult() throws Exception {
Meeting meeting = Meeting.findById(id);
while (meeting.bbbMeetingId == null) {
Thread.sleep(1000);
meeting = meeting.refresh(); // I tried each of these
meeting = meeting.merge(); // lines but to no avail; I
meeting = Meeting.findById(id); // get the same result
}
return meeting;
}
}.now();
Meeting meeting = await(meetingPromise);
As I note in the comments, there are three lines in there, any one of which I think should allow me to refresh the contents of my object from the database. From the debugger, it seems that the many-to-one relationships are refreshed by these calls, but the single values are not.
My Meeting object extends Play! Framework's Model, and for convenience, here is the refresh method:
/**
* Refresh the entity state.
*/
public <T extends JPABase> T refresh() {
em().refresh(this);
return (T) this;
}
and the merge method:
/**
* Merge this object to obtain a managed entity (usefull when the object comes from the Cache).
*/
public <T extends JPABase> T merge() {
return (T) em().merge(this);
}
So, how can I refresh my model from the database?
So, I ended up cross-posting this question on the play-framework group, and I got an answer there. So, for the discussion, check out that thread.
In the interest of having the answer come up in a web search to anyone who has this problem in the future, here is what the code snippet that I pasted earlier looks like:
Promise<Meeting> meetingPromise = new Job<Meeting> () {
#Override
public Meeting doJobWithResult() throws Exception {
Meeting meeting = Meeting.findById(id);
while (meeting.bbbMeetingId == null) {
Thread.sleep(1000);
if (JPA.isInsideTransaction()) {
JPAPlugin.closeTx(false);
}
JPAPlugin.startTx(true);
meeting = Meeting.findById(id);
JPAPlugin.closeTx(false);
}
return meeting;
}
}.now();
Meeting meeting = await(meetingPromise);
I am not using the #NoTransaction annotation, because that messes up some other code that checks if the request is coming from a valid user.
I'm not sure about it but JPA transactions are managed automatically by Play in the request/controller context (the JPAPlugin opens a transaction before invocation and closes it after invocation).
But I'm not sure at all what happens within jobs and I don't think transactions are auto-managed (or it's a feature I don't know). So, is your entity attached to an entitymanager or still transient? Is there a transaction somewhere? I don't really know but it may explain some weird behavior if not...