I'm currently trying to delete specific realm object in my model using for loop,
but every time that i execute the deleteFromRealm(i) it stops the loop and I can no longer delete the other object.
I haven't tried any other options though.
final Realm realms = Realm.getDefaultInstance();
realms.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
RealmResults<CashCountScoreModel> cashCountScoreModels =
CashCountScoreModel.getAll(realm);
for (int i = 0; i < cashCountScoreModels.size(); i++) {
if (cashCountScoreModels.get(i) != null && cashCountScoreModels.get(i).isCashOnHand) {
Log.d("CheckName : pos -- ", i +"~~" + cashCountScoreModels.get(i).isCashOnHand);
Log.d("CheckName : pos --", i + "~~" + cashCountScoreModels.get(i).employeeName);
cashCountScoreModels.deleteFromRealm(i);
// continue;
}
}
}
});
Whenever i try to run the app, and execute this specific code cashCountScoreModels.deleteFromRealm(i);, it stops the loop.
You should not call deleteFromRealm(i) inside a loop because it always causes crash. Use this code instead:
realms.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
RealmResults<CashCountScoreModel> results = CashCountScoreModel.getAll(realm);
results.where().equalTo("isCashOnHand", true).findAll().deleteAllFromRealm();
}
});
Not sure what version of Realm you are using. But since 3.0.0, Realm collections are live and hence updated immediately. So, cashCountScoreModels.size() will return a count less upon every deletion. In your case, I suspect you have just 2 entries in the collection. You might want to use OrderedRealmCollectionSnapshot instead. Try out following code.
final Realm realms = Realm.getDefaultInstance();
realms.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
RealmResults<CashCountScoreModel> cashCountScoreModels = CashCountScoreModel.getAll(realm);
OrderedRealmCollectionSnapshot snapshot = cashCountScoreModels.createSnapshot();
for (CashCountScoreModel cashCountScoreModel : snapshot) {
if (cashCountScoreModel != null && cashCountScoreModel.isCashOnHand) {
Log.d("CheckName : pos -- ", i +"~~" + cashCountScoreModel.isCashOnHand);
Log.d("CheckName : pos --", i + "~~" + cashCountScoreModel.employeeName);
cashCountScoreModel.deleteFromRealm();
}
}
}
});
Look for Iterations & snapshots on https://realm.io/docs/java/latest/ or the documentation available at https://realm.io/docs/java/3.0.0/api/io/realm/OrderedRealmCollection.html#loops to know more realtime update in collections and OrderedRealmCollectionSnapshot
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
RealmResults<CashCountScoreModel> cashCountScoreModels=realm.where(CashCountScoreModel.class).equalTo(CashCountScoreModel.isCashOnHand,true).findAll();
cashCountScoreModels.deleteAllFromRealm();
}
});
#Md. Nowshad Hasan is Correct. Just run in Realm Thread.
It happens because I think you want to delete multiple realm objects from a single execution block.
Try below code in execute block.
RealmResults<CashCountScoreModel> cashCountScoreModels=realm.where(CashCountScoreModel.class).equalTo(CashCountScoreModel.isCashOnHand,true).findAll();
cashCountScoreModels.deleteAllFromRealm();
Related
I know it sounds strange/ridiculous, but I am having this issue
Update#2
I am sharing the code that is indicated by #EpicPandaForce.
SyncService.onNetworkSuccess
public void onNetworkCallSuccess(Response response) {
List<TransactionHistory> historyList = (List<TransactionHistory>) response.body();
if(historyList != null && historyList.size() > 0) {
TransactionHistory max = Collections.max(historyList, new Comparator<TransactionHistory>() {
#Override
public int compare(TransactionHistory o1, TransactionHistory o2) {
return o1.getUpdatedAt().compareTo(o2.getUpdatedAt());
}
});
if(max != null) {
session.putStringForKey(Session.timeStamp, String.valueOf(max.getUpdatedAt()));
}
for(TransactionHistory history : historyList) {
String id;
if(history.getTo().equals(history.getFrom()) ||
history.getFrom().equals(session.getStringForKey(Session.fpIdKey)))
id = history.getTo();
else id = history.getFrom();
LatestTransactionResponse latestTransactionResponse = new LatestTransactionResponse();
DateTransactionResponse dateTransactionResponse = new DateTransactionResponse(DateUtility.getDateFromEpoch(history.getEpoch()));
dateTransactionResponse.addTransaction(history);
latestTransactionResponse.setArchived(history.isArchived());
latestTransactionResponse.addTransaction(history);
latestTransactionResponse.setId(id);
dateTransactionResponse.setId(id);
LatestTransactionRepository.getInstance().addLatestTransaction(realm,
latestTransactionResponse);
ContactTransactionRepository.getInstance().addNewTransaction(realm, dateTransactionResponse, id);
}
try {
Activity temp = MyFirebaseMessagingService.getRunningActivity();
if(temp != null) {
if(temp instanceof MainActivity) {
((MainActivity) temp).refreshLatestTransactions();
} else if(temp instanceof TransactionDetailActivity) {
((TransactionDetailActivity) temp).refreshOnMainThread();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
addNewTransaction
public void addNewTransaction(Realm realm, final DateTransactionResponse response, final String id) {
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
List<TransactionHistory> tempHistoryList;
DateTransactionResponse temp = realm
.where(DateTransactionResponse.class)
.equalTo("id", id)
.equalTo("date", response.getDate())
.findFirst();
if(temp == null)
realm.insertOrUpdate(response);
else {
tempHistoryList = temp.getTransactions();
for(TransactionHistory history : response.getTransactions()) {
boolean found = false;
for(int i=0; i < tempHistoryList.size(); i++) {
if (history.getId().equals(tempHistoryList.get(i).getId())) {
if(history.getStatus().equals(tempHistoryList.get(i).getStatus())) {
found = true;
break;
} else {
tempHistoryList.get(i).setStatus(history.getStatus());
}
}
}
if(!found)
tempHistoryList.add(history);
}
//realm.insertOrUpdate(temp);
realm.copyToRealm(temp);
//DateTransactionResponse transactionResponse = temp;
//temp.deleteFromRealm();
//realm.insertOrUpdate(temp);
}
}
});
//removeDuplicateTransactions(realm);
}
removeDuplicateTransaction
private void removeDuplicateTransactions(Realm realm) {
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
RealmQuery<DateTransactionResponse> query = realm.where(DateTransactionResponse.class);
RealmResults<DateTransactionResponse> results = query.findAll();
List<DateTransactionResponse> transactions = new ArrayList<>(results);
for(DateTransactionResponse response : transactions) {
List<TransactionHistory> historyList = response.getTransactions();
Set<TransactionHistory> historySet = new LinkedHashSet<>(historyList);
RealmList<TransactionHistory> histories = new RealmList<>();
histories.addAll(new ArrayList<>(historySet));
response.setTransactions(histories);
realm.copyToRealm(response);
}
}
});
}
Update#1
There are 3 tabs with RecyclerViews on my main screen. Below are the implementation of Adapter for all three.
I have been developing an App for quite a time. It has been working just fine and I occasionally work to improve its performance. It is still under development. Some days ago, I cut-out the branch and done nothing notable (just one or two bug fixes) and started testing it and OOPS it started giving ANR's. I revert back to previous branch and very strangely it started giving me the same result. I have removed all changes and tried, still the same result. I am not sure what's happening. I tried to study traces.txt, but couldn't find waiting to lock as suggested in this SO answer.
I have also difficulty reading traces, couldn't find the culprit. Here is the traces.txt file.
I am using Realm as Database in my application and couldn't find a way to perform operations on Realm on other thread. I tried to find any other culprit in code, but all is till the same as before which was working perfectly fine.
Hierarchy
Here is the App Hierarchy.
Login screen is shown and user enters PIN. Then comes the main screen. Main screen contains 4 tabs, just like WhatsApp i.e first tab is camera and rest contains RecyclerViews in which data is being populated from Realm. ANR is only happening here. Keeping in mind that it was literally perfect some days ago until I took branch out and fixed some bugs, which were not even related to the main screen.
Any help or direction is highly appreciated.
I need to solve putting data to a realm database like this:
I have an object called obtained_code;
I have a realmList of Obtained codes in an object called Offer;
I download obtained codes separately, and by their offer id assign them to the lists of each object. The problem is that I can't add them because when I check the size, it's always 0.
Here is the code:
ObtainedCodes codes = response.body();
for (ObtainedCode c : codes.getObtainedCodes()) {
Offer offer = RealmController.with(SplashActivity.this).getOffer(c.getOffer_id());
if (offer != null) {
Log.d("Size", "Offer not null");
realm1.beginTransaction();
RealmList<ObtainedCode> list = offer.getObtained_codes();
if (!list) { // if the 'list' is managed, all items in it is also managed
RealmList<ObtainedCode> managedImageList = new RealmList<>();
for (ObtainedCode item : list) {
if (item) {
managedImageList.add(item);
} else {
managedImageList.add(realm1.copyToRealm(item));
}
}
list = managedImageList;
}
offer.setObtained_codes(obtainedCodes);
Log.d("Size", String.valueOf(offer.getObtained_codes().size()));
realm1.copyToRealmOrUpdate(offer);
realm1.commitTransaction();
}
offer = RealmController.with(SplashActivity.this).getOffer(c.getOffer_id());
Log.d("Size", String.valueOf(offer.getObtained_codes().size()));
}
1.) the Ravi Tamada tutorial on InfoHive is a terrible mess, please refer to my remake of that example instead.
If you managed to start using 0.82.1 because Ravi Tamada claimed that a 4 years old version is "stable", well I know that it's not. Use 1.2.0 instead (or the latest version which is 3.4.1)
And if you see a RealmController.with(), run, because it ignores thread-confinement. The moment you try to access it from a background thread, it'll crash.
On background threads, you'd need to do
#Override
public void run() {
try(Realm realm = Realm.getDefaultInstance()) {
repository.whatever(realm); // pass Realm instance to database methods
} // auto-close
// end of thread
}
2.) you are executing writes on the UI thread, that is bad, from UI thread you should use realm.executeTransactionAsync(), but in your case you should actually execute the Retrofit call on a background thread using Ęxecutors.newSingleThreadedPool() and call it with call.execute() instead of call.enqueue().
3.) You should write to Realm on the background thread, and on the UI thread you should use RealmChangeListener to listen to writes.
4.) your code doesn't work because you're setting an unmanaged list to a managed RealmObject.
You should modify the existing RealmList inside the RealmObject, and add only managed objects to it.
Executor executor = Executors.newSingleThreadExecutor(); // field variable
// ...
void someMethod() {
executor.execute(new Runnable() {
#Override
public void run() {
Response<ObtainedCodes> response = retrofitService.getObtainedCodes().execute(); // run on current thread
ObtainedCodes codes = response.body();
if(codes == null) return;
try(Realm r = Realm.getDefaultInstance()) {
r.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
for(ObtainedCode obtainedCode : codes.getObtainedCodes()) {
Offer offer = realmRepository.getOffer(realm, obtainedCode.getOfferId());
if(offer == null) {
offer = realm.createObject(Offer.class, obtainedCode.getOfferId());
// map properties to offer if possible
}
RealmList<ObtainedCode> offerCodes = offer.getObtainedCodes();
ObtainedCode managedObtainedCode = realm.where(ObtainedCode.class).equalTo("obtainedCodeId", obtainedCode.getId()).findFirst();
if(managedObtainedCode == null) {
managedObtainedCode = realm.createObject(ObtainedCode.class, obtainedCode.getId());
// map properties from obtained code to managed obtained code
}
if(!offerCodes.contains(managedObtainedCode)) {
offerCodes.add(managedObtainedCode);
}
}
}
});
}
}
});
}
I know this question might seem duplicated, I've read like ten other threads about this same thing, but I cannot find the problem
I have this method in my activity:
public void saveResponse(final Response studentResponse, final Content content)
fb.getReference("...").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(final DataSnapshot dataSnapshot) {
new Thread(new Runnable() {
#Override
public void run() {
Map<String, Object> responseMap = new HashMap<>();
responseMap.put("end_date", studentResponse.end_date);
responseMap.put("start_date", studentResponse.start_date);
responseMap.put("time", studentResponse.time);
responseMap.put("points", studentResponse.points);
responseMap.put("max_points", studentResponse.max_points);
responseMap.put("selected_options", studentResponse.selected_options);
if (!TextUtils.isEmpty(studentResponse.free_text))
responseMap.put("free_text", studentResponse.free_text);
DataSnapshot contentRef = dataSnapshot.child("/sections/" + currentSection + "/sections/" + currentSubsection + "/contents/" + content.id);
final int oldPoints = contentRef.hasChild("points") ? contentRef.child("points").getValue(int.class) : 0;
contentRef.getRef().setValue(responseMap);
contentRef.getRef().setPriority(ServerValue.TIMESTAMP);
DataSnapshot subSectionRef = dataSnapshot.child("/sections/" + currentSection + "/sections/" + currentSubsection);
long subSectionPoints = (subSectionRef.hasChild("points") ? subSectionRef.child("points").getValue(long.class) : 0) + studentResponse.points - oldPoints;
subSectionRef.child("points").getRef().setValue(subSectionPoints);
int indexOf = currentContents.indexOf(content) + 1;
if(indexOf > 0 && indexOf < currentContents.size()) {
CourseContent content = currentContents.get(indexOf);
subSectionRef.child("currentPosition").getRef().setValue(content.order);
}
DataSnapshot sectionRef = dataSnapshot.child("/sections/" + currentSection);
long sectionPoints = (sectionRef.hasChild("points") ? sectionRef.child("points").getValue(long.class) : 0) + studentResponse.points - oldPoints;
sectionRef.child("points").getRef().setValue(sectionPoints);
long coursePoints = (dataSnapshot.hasChild("points") ? dataSnapshot.child("points").getValue(long.class) : 0) + studentResponse.points - oldPoints;
dataSnapshot.child("points").getRef().setValue(coursePoints);
dataSnapshot.getRef().setPriority(MAX_SAFE_INTEGER - coursePoints);
int completed = 0;
for (DataSnapshot sect : dataSnapshot.child("sections").getChildren()) {
for (DataSnapshot subSect : sect.child("sections").getChildren()) {
int currPos = subSect.hasChild("currentPosition") ? subSect.child("currentPosition").getValue(int.class) : 0;
completed += currPos;
}
}
double progress = totalContents > 0 ? (double) completed / (double) totalContents : 0;
dataSnapshot.child("progress").getRef().setValue(progress);
}
}.start();
}
...
});
}
in a click handler I call this method, and then I change the fragment (with custom animations).
The thing is, the fragment transition is not smooth, it freezes a little, if I comment everything inside the runnable then it runs smooth. I've tried also with an AsyncTask and the same happens.
Inside the runnable, I'm just querying the dataSnapshot and its children, and setting some values (dataSnapshot.child("item").getRef().setValue(x))
Another strange thing is that if I put a breakpoint inside run(), it also works smooth.
every time you call onDataChange method will create a new thread,and the strange thing: " if I put a breakpoint inside run(), it also works smooth."
may be you should check out whether there are too many thread created.
I think the problem is with the logic of your method.
The listener onDataChange() is active and it will respond to any change of the data. I mean if you change data inside the onDataChange() method, it will be called each time you set values with (dataSnapshot.child("item").getRef().setValue(x))), so, it is similar to do a "recursive" call without exit.
In order to fix this problem, you should obtain the key of what you want to change in the on click event and just use
mDatabase = FirebaseDatabase.getInstance().getReference();
mDatabase.child("key").child("item").setValue(x);
Check https://firebase.google.com/docs/database/android/read-and-write for more info
A spawned thread inherits the priority of the thread that created it. Try lowering the priority of your worker thread to prevent it from competing with the UI thread:
#Override
public void onDataChange(final DataSnapshot dataSnapshot) {
new Thread(new Runnable() {
#Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
...
}).start();
In my app, I am synchronizing all my data via realm transactions in the background thread. However, occasionally when I try to retrieve a recently synchronized object in the UI thread, I get a null pointer exception. I have written a routine that seems to be a fix to this small latency problem, but it feels like a "hacky" solution. Any guidance would be greatly appreciated. I have posted a simplified version of the routine below.
private void attemptToEnterActivity(){
// Retrieve the object's key.
String key = Application.getInstance().getRecentlySyncedPrimaryKey();
// Retrieve the realm object with the key.
Realm realm = Realm.getDefaultInstance();
Object object = realm.where(Object.class)
.equalTo("key", key)
.findFirst();
if (object != null) {
new NavigationController(this).activity_start(new Intent(this, Activity.class));
} else {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
attemptToEnterActivity();
}
}, 200);
}
}
Instead of guessing when the object will be fetched and stored, the suggestion is to use RealmChangeListener. See doc here for more information.
In your case, you can also use Realm's async query like:
RealmResults<Object> results = realm.where(Object.class)
.equalTo("key", key).findAllAsync();
results.addChnageListener(newRealmChangeListener<RealmResults<Object>>() {
#Override
public void onChange(RealmResults<Object> objects) {
if (objects.size() > 0) {
Object object = objects.first();
// ....
}
}
});
I have filled realm database. Everything works well.
The application has a button, pressing it starts IntentService. It is very simple for test
code here
public class HNotificationService extends IntentService {
public HNotificationService() {
super("HNotificationService");
}
#Override
protected void onHandleIntent(Intent intent) {
Log.d("HNotificationService", "onHandleIntent");
Realm realm = Realm.getDefaultInstance();
RealmResults<HPartner> mResPartner = realm.where(HPartner.class).findAll();
final HPartner hPartner = mResPartner.get(0);
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
hPartner.setName("test");
}
});
realm.close();
}
}
After this code is executed, the database becomes empty! I do not see any errors in the log.
Note. Cleaning the database occurs only when I want to record something.
I will be glad of any help
EDIT1
Inside application class. I use this code
RealmConfiguration config = new RealmConfiguration.Builder(this).build();
Realm.setDefaultConfiguration(config);
EDIT2
yes you are right database is not deleted!
Log.e("HNotificationService", "empty"); - It is never called, and it's good
Realm realm = Realm.getDefaultInstance();
RealmResults<HPartner> mResPartner = realm.where(HPartner.class).findAll();
if (mResPartner.size() > 0) {
Log.e("HNotificationService", "don't empty!");
}
final HPartner hPartner = mResPartner.get(0);
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
hPartner.setName("test");
}
});
mResPartner = realm.where(HPartner.class).findAll();
if (mResPartner.size() == 0) {
Log.e("HNotificationService", "empty");
}
realm.removeAllChangeListeners();
realm.close();
Another reason. When I go to a different screen I'm calling code
mResPartners = Realm.getDefaultInstance().where(HPartner.class).findAllAsync();
mResPartners.removeChangeListeners();
mResPartners.addChangeListener(mPartnersListener);
And mPartnersListener is never called. It is very strange.
if don't run IntentService. mPartnersListener always called
EDIT 3
mPartnersListener = new RealmChangeListener<RealmResults<HPartner>>() {
#Override
public void onChange(RealmResults<HPartner> realmResult) {
mResPartners.removeChangeListeners();
mClusterManager.clearItems();
mGoogleMap.clear();
if (realmResult != null && realmResult.size() > 0) {
mListPartners = realmResult;
getLoaderManager().restartLoader(LMarkerIcons.ID_LOADER, null, FrAroundMeMap.this);
}
}
};
I think, I find out the problem !You remove allChangelisteners in your IntentService, after logs in this part.
mResPartner = realm.where(HPartner.class).findAll();
if (mResPartner.size() == 0) {
Log.e("HNotificationService", "empty");
}
realm.removeAllChangeListeners();
realm.close();
and then you do new query, add NEW changeListener and want get notifications from him, but after send notification, listener removed by code above.
adding changelistener to async fetched realmResult - correct, should work fine!
So remove this block in IntentSevice
realm.removeAllChangeListeners();
Also your listener looks very strange, see coments in your onChange
mPartnersListener = new RealmChangeListener<RealmResults<HPartner>>() {
#Override
public void onChange(RealmResults<HPartner> realmResult) {
// here U remove listener BEFORE do your stuff and
// onChange will never called again
// If U`ll put log here U find out, that listener was called
// simple delete block -> mResPartners.removeChangeListeners();
mResPartners.removeChangeListeners();
mClusterManager.clearItems();
mGoogleMap.clear();
if (realmResult != null && realmResult.size() > 0) {
mListPartners = realmResult;
getLoaderManager().restartLoader(LMarkerIcons.ID_LOADER, null, FrAroundMeMap.this);
}
}
};
Something that bugs me in this code is that you should obtain the latest version of the realm object inside the transaction
#Override
protected void onHandleIntent(Intent intent) {
Log.d("HNotificationService", "onHandleIntent");
Realm realm = Realm.getDefaultInstance();
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
RealmResults<HPartner> mResPartner = realm.where(HPartner.class).findAll();
final HPartner hPartner = mResPartner.get(0);
hPartner.setName("test");
}
});
realm.close();
}
Other than that, I would try replacing the async() query with the sync version.
mResPartners = Realm.getDefaultInstance().where(HPartner.class).findAll();
Also worth noting the fact that Realm.getDefaultInstance().where( this construct means you'll never be able to close the Realm on the UI thread.
EDIT: Are you sure
mResPartners.removeChangeListeners();
Doesn't cause any problems in your change listener?