StackOverFlowError with recursive and callback in Android - android

ArrayList<User> users = User.getAll();
int n = users.size();
private void dowload(int i){
if(i >= n) return;
Sync.download(i, new OnDownloadListener(){
public void done(Data data){
data.save();
download(i+1);
}
});
}
I have the function download with recursive when i run download(0) the StackOverFlowError was thrown. I have a user list, each user have a few data info and it's downloaded from internet. The user list is stored in my database, i will load all user and call download user data as below, i need to download partials, ex download done for user data 1 and next to user data 2... etc. Have someone give me a suggest to resolve it. Thanks.

Related

Room Update the data only if it changed from the server

I have a function which runs every 30 seconds, in which it fetches the latest data from the server and stores locally.
Currently, I'm deleting all the existing rows and reinserting all the data again, but I think this way is not the efficient one, I should be updating the data only if the local data and server data defers.
So how can I do that?
Here's what I'm doing:
DatabaseDao:
#Dao
public interface GeneralDatabaseDao {
#Query("DELETE FROM table_tables")
int deleteTables();
#Insert(onConflict = OnConflictStrategy.REPLACE)
void insertRestaurantTablesData(TablesModel... TablesModels);
}
Repository:
public LiveData<List<TablesModel>> getTablesData(int mLocationID) {
mTablesList = new MutableLiveData<>();
LiveData<TablesModel> mTablesData = mTableDataSource.getTablesData();
Observer<TablesModel> mObserver = tableModels -> {
mExecutors.diskIO().execute(() -> {
//Completed: delete old table data if there are conflicts.
if (tableModels != null) {
mDatabaseDao.deleteTables();
mDatabaseDao.insertTablesFromServer(tableModels.getTables());
} else {
Log.e(LOG_TAG, "Nothing: ");
}
});
Log.e("Handlers", "repository getTablesData");
};
if (!mTablesData.hasObservers()) {
mTablesData.observeForever(mObserver);
}
return mDatabaseDao.getTablesData(mLocationID);
}
I know I need to compare all the local rows to the data which I got from the server and then update only changed data, But for that, I need to query local data and check row by row in a loop and then update. I'm fine with doing that but is there any other efficient way to that?
No, there's no alternative. Moreover, if you delete and insert again, you will activate the UI refresh each time. Since you used live data to update UI only when it is needed, you need to update the database only when it is strictly necessary.
With the code you write, each time you check web service, UI will be refresh. Better check each time if there are rows to change and modify them.

How to stop execution of piece of code in android until data is retrieved from Firebase?

I am trying to perform some task based on the data retrieved from Firebase.
for (inti=0;i<dateList.size();i++)
{
attendanceDateRef= attendanceRef.child(dateList.get(i));
attendanceClassRef= attendanceDateRef.child(ViewAttendanceSelectClassActivity.selectedClass);
attendanceClassRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshotdataSnapshot) {
for (DataSnapshotdsp : dataSnapshot.getChildren())
{
attendanceList.add(dsp.getValue(String.class));
Log.e("attendanceListValues",dsp.getValue(String.class));
}
}
#Override
public void onCancelled(DatabaseErrordatabaseError) {
}
});
}
intindex=1;
for (inti=1;i<=attendanceList.size();i++)
{
if(i%4==0)
{
fullDateRangeList.add(dateList.get(index));
index++;
}
else
{
fullDateRangeList.add(dateList.get(index));
}
}
Log.e("fullDateRangeList",String.valueOf(fullDateRangeList.size()));
Log.e("attendanceList",String.valueOf(attendanceList.size()));
above code is written on the OnClick event of a Button , when I click on the Button following output is generated on logcat:
fullDateRangeList:0
attendanceList:0
attendanceListValues:Value1
attendanceListValues:Value2
attendanceListValues:Value3
attendanceListValues:Value4
.
.
.
attendanceListValues:ValueN
from the above output it looks like second loop is executing before data is retrieved from Firebase and that is why size of fullDateRange and attendanceList is 0.
is there any way i can prevent second loop from executing until data is stored in attendanceList?
You cannot stop that method or make it wait until you get the all the data from your database. This is the behaviour of an asynchronous method. You need to change the logic of your code a little bit by declaring and using that data only inside the onDataChange() method, otherwise it will be always empty.
Also there is another approach. If you want, you can dive into the asynchronous world and use my answer from this post.

Collection was modified in a foreach loop

I'm trying to get notifications from a bluetooth device sending records, and periodically update the UI to display the records. The while loop below sits in its own thread to handle UI updates while the rest of the module takes care of other tasks. gattCallback is an instance of a BluetoothGattCallback class that adds to a list of received records and returns that list when getHistory() is called.
My problem is that when I hit the foreach line, after so many iterations I get an error:
System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
As far as I can tell, history isn't being updated here or anywhere else in my code so I'm confused by the error. I specifically retrieve a copy of the record history through getHistory() to avoid modifying it during the foreach. Can anyone suggest what might be causing it or any tips to find out?
It might be relevant that this has only caused issues since switching to a Moto E4 on Android 7.1.1 from a Moto G Play on Android 6.0.1.
// Periodically check to see what needs updating
while (!finishedDisplayThread)
{
// See if there are any new records to display
int count;
List<Record> history = gattCallback.getHistory();
if (history == null)
{
count = 0;
}
else
{
count = history.Count;
}
// Only update the display if it has changed
if(count != prevCount)
{
prevCount = count;
List<string> recordList = new List<string>();
if (history == null)
{
recordList = new List<string>();
recordList.Add("No history.");
}
else
{
foreach (Record record in history)
{
recordList.Add(record.ToRow());
}
}
//Update the display
RunOnUiThread(() =>
{
ListAdapter = new ArrayAdapter<string>(this,
Resource.Layout.ListItemLayout,
recordList);
recordCountText.Text = "" + count;
});
}
Thread.Sleep(100);
}
I specifically retrieve a copy of the record history through getHistory() to avoid modifying it during the foreach.
Are you certain that you're getting a copy? Imagine this implementation:
public List<Record> getHistory() {
return history;
}
This will not return a copy of history, but a reference directly to history itself. If two different places call this method, any changes to one of the returned values will affect the other returned value.
If you want a copy, you have to do something like this:
public List<Record> getHistory() {
return new ArrayList<>(history);
}

Recover Data Firestore Collection

I have a service scheduling screen.
But there is a problem:
I need to check if the date and time the user is trying to schedule is available or reserved.
Structure DB:
Companies
-Company ID (Document)
--name
--phone
---Schedules (Collection)
------Event1
--------Hour: 08:30
--------Date: 01/01/2018
------Event2
--------Hour: 09:00
--------Date: 05/01/2018
------Event3
--------Hour: 10:30
--------Date: 01/002/2018
I access Scheduling data with this code:
String dateExample = "01/01/2018"
String hourExample = "08:30"
FirebaseFirestore mDB = FirebaseFirestore.getInstance();
CollectionReference mDBCompaniesSchedules = (CollectionReference) mDB.collection("Companies").document(mId_Company).collection("Schedules")
.get()
.addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot documentSnapshots) {
for (Schedules document : documentSnapshots.toObjects(Schedules.class)) {
String dtSchedules = document.getSchedules_date();
String hourSchedules = document.getSchedules_hour();
if ( dtSchedules.equals(dateExample) && hourSchedules.equals(hourExample) ){
//Execute a "Toast" and closes the operation
} else {
//Call up the scheduling function.
startScheduling();
}
}
}
})
Process:
I need to run this code and go through all the documents in that collection. I need to check and analyze whether the date and time of the schedule already exists.
If there is: Show a Toast and block.
If there is no: Executing a specific function for the schedule record ("startScheduling()").
Problem:
When the data exists (it will only be once) then it will work.
When there is no data, it falls into the ELSE loop. And it is executing several times the same function "startScheduling();".
I need some way to go through this collection and when I do not find any results, the function "startScheduling (), be executed only once.
This how a for loop works. It will continue iterate till the last element to see if the condition is true or not. With other words, your if-else statement is triggered for every iteration in the loop. It means that, if the condition is true it will go with the if part, if the condition is false it will go with else part, for each and every element.
There are two ways in which you can solve this. One would be to break the loop once the condition was fulfilled. But this means that will iterate till it gets that element. Second, would be to change the logic of your code. Use first the if statement and second iterate.
Edit: The best option in this case would be to query your database using whereEqualTo() method.
Query query = db
.collection("Companies")
.document(mId_Company)
.collection("Schedules")
.whereEqualTo("dtSchedules", dateExample)
.whereEqualTo("hourSchedules", hourExample);
In which dateExample and hourExample are the actual values with which you want to compare.
To count the number of documents in a Collection, please use the following code:
public void onSuccess(QuerySnapshot documentSnapshots) {
if(documentSnapshots.size() == 0) {
startScheduling();
}
for (Schedules document : documentSnapshots.toObjects(Schedules.class)) {
String dtSchedules = document.getSchedules_date();
String hourSchedules = document.getSchedules_hour();
}
}

Android Parse SDK issue

I'm using parse to store my data and do a lot of queries while using my program.
The issue is that after about +/-20 similar queries, parse findInBackground() or getFirstInBackground() doesn't return a callback and app stuck at that possition.
My query code:
ParseQuery<OptionCodeDTO> mQuery;
mQuery = ParseQuery.getQuery(OptionCodeDTO.class);
mQuery.whereEqualTo("code", prCode);
mQuery.getFirstInBackground(new GetCallback<OptionCodeDTO>() {
#Override
public void done(OptionCodeDTO optionCodeDTO, ParseException e) {
if (isVisible()) {
if (e == null) {
OptionCode opCode = new OptionCode(optionCodeDTO);
mCodes.push(opCode);
printCodes();
prDescrLayout.setVisibility(View.VISIBLE);
prDescProgress.setVisibility(View.GONE);
mPRLable.setVisibility(View.GONE);
} else {
if (e.getCode() == ParseException.CONNECTION_FAILED) {
mPrDescr.setText(R.string.dtc_lookup_check_network);
} else if (e.getCode() == ParseException.OBJECT_NOT_FOUND) {
mPrDescr.setText(R.string.pr_lookup_code_not_found);
} else {
mPrDescr.setText(R.string.dtc_lookup_other_problems);
}
prDescrLayout.setVisibility(View.VISIBLE);
prDescProgress.setVisibility(View.GONE);
}
}
}
});
First of all, if your app ANRs (application not responding) because of something from UI thread that relies on background threads, that is incorrect architecture.
Probably you have to optimize your app's interaction with Parse. Generally it is a bad practice to make lots of saveInBackground, for example from inside a loop. You can add objects, those need to be saved, to a list and then use ParseObject.saveAllInBackground(objectList)
Also an idea to optimize is to use local storage - android's built in SQLite. For example if your app relies on something being saved to Parse, the logic is like this:
When saving object first you save to local DB and run a saveInBackground method.
When fetching objects you first fetch from your local DB and then run a getInBackground method, which inside a callback persists the information to your local DB.
This way you will make your app usable without internet connection.

Categories

Resources