For loop don't execute all the code before continue - android

for (int i = 1; i < 3; i++) {
list.add(new Integer(i));
}
Collections.shuffle(list);
for (int i = 0; i < 2; i++) {
int r = list.get(i);
numeroRandom = Integer.toString(r);
Log.d("DEBUG", "teste random :" + list.get(i));
Log.d("DEBUG", "numeroRandomGerado = " + numeroRandom);
final DatabaseReference questaoRef = fireBaseRef.child("questoes");
Log.d("DEBUG", "Loop Stop here");
questaoRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.d("DEBUG", "teste2");
if (dataSnapshot != null) {
Questoes questoes = dataSnapshot.child("id").child(numeroRandom).getValue(Questoes.class);
Log.d("DEBUG", "teste3");
Questoes objetoQuestao = new Questoes();
objetoQuestao.setQuestao(questoes.getQuestao());
objetoQuestao.setOpcaoA(questoes.getOpcaoA());
objetoQuestao.setOpcaoB(questoes.getOpcaoB());
objetoQuestao.setOpcaoC(questoes.getOpcaoC());
objetoQuestao.setOpcaoD(questoes.getOpcaoD());
objetoQuestao.setResultado(questoes.getResultado());
listaQuestoes.add(objetoQuestao);
Log.d("DEBUG", "tamanho = " + listaQuestoes.size());
}
callBack.onCallback(listaQuestoes);
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
});
For some reason my loop, stops on Log.d("DEBUG", "Loop Stop here") and back to the start and execute 2 times the rest of code.
this is the Log i get from the code, for some reason stops on the 3° debug and restarts and execute everything all over
teste random :1
numeroRandomGerado = 1
Loop stop here
teste random :2
numeroRandomGerado = 2
Loop stop here - teste 1
teste2
tamanho = 1
callback
teste2
teste3
When the correct would be like this:
teste random :1
numeroRandomGerado = 1
Loop stop here - teste 1
teste2
teste3
tamanho = 1
callBack
teste random :2
numeroRandomGerado = 2
Loop stop here
teste2
teste3
tamanho = 2
callBack

this is because the onDataChange(DataSnapshot dataSnapshot) listener is where the execution becomes asynchronous - while you expect synchronous execution. possibly even because it might have another listener registered for .child("questoes"), while the previously registered listener has not yet returned. try setting breakpoints and step into it, to see in which order the execution actually happens. adding Thread.sleep(2000); after registering the listener, most likely should make it work, as you'd expect it to work.

Related

Insert data to firebase -Android - and check specific properties

I need to insert lesson object to firebase, so I put here the onData change section of code.
First of all I get data snapshot and insert the lessons that I have in firebase, after that I scan the List of Lessons and check:
if the date and time exist in the firebase in any Lesson so I do something else I insert the lesson object to firebase .
The main problem is :
when I insert the details of the lesson and press add, the lesson enter to the firebase twice minimum, and if I try another insertion the program enter to infinite loop .
will be happy for any help !
ArrayList<Lesson> existLesson=new ArrayList<>();
List<String> keys = new ArrayList<>();
int counter=0;
public void getLessons(DataSnapshot dataSnapshot){
//insert the lessons to "existLesson" arrayList
for (DataSnapshot keyNode : dataSnapshot.getChildren()) {
keys.add(keyNode.getKey());
Lesson lesson = keyNode.getValue(Lesson.class);
existLesson.add(lesson);
Log.i(tag, "data : " + lesson.getSubject());
}//for
}
int flag=1;
#Override
public void addLesson(final String subject, final String topic, final String date, final String time) {
mDatabase.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
getLessons(dataSnapshot);
//Check if date and time is busy
for (Lesson lessonToCheck : existLesson) {
if (lessonToCheck.getDate().equals(date) && lessonToCheck.getTime().equals(time)) {
flag = 0;
} else {
flag = 1;
}
}//for
if (flag == 0) {
Toast.makeText(LessonDetails.this, "date exist", Toast.LENGTH_SHORT).show();
// Check empty lessons
nearestLessons(existLesson, date, time);
} else {
if (flag == 1) {
String id = mDatabase.push().getKey();
Lesson lesson = new Lesson(subject, topic, date, time, id); //create lesson
Toast.makeText(LessonDetails.this,
subject + " - " + topic + " - " + date + " - " + time, Toast.LENGTH_SHORT).show();
mDatabase.child(id).setValue(lesson);
} //add lesson to DB
} //else
Log.i(tag,"end");
} //onDataChange
When you call you're adding a listener to the data at. This listener will immediately read the data and call your onDataChange, and then continues to listen for updates to the data.
For each update to the data, it calls your onDataChange again. And since you're updating the data inside onDataChange, this ends in an endless loop of setValue->onDataChange->setValue->onDataChange->...
To fix this, you'd typically use addListenerForSingleValueEvent instead, as this only gets the value once and doesn't continue listening for changes.
So something like:
mDatabase.addForListenerValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
getLessons(dataSnapshot);
//Check if date and time is busy
for (Lesson lessonToCheck : existLesson) {
if (lessonToCheck.getDate().equals(date) && lessonToCheck.getTime().equals(time)) {
flag = 0;
} else {
flag = 1;
}
}//for
if (flag == 0) {
Toast.makeText(LessonDetails.this, "date exist", Toast.LENGTH_SHORT).show();
// Check empty lessons
nearestLessons(existLesson, date, time);
} else {
if (flag == 1) {
String id = mDatabase.push().getKey();
Lesson lesson = new Lesson(subject, topic, date, time, id); //create lesson
Toast.makeText(LessonDetails.this,
subject + " - " + topic + " - " + date + " - " + time, Toast.LENGTH_SHORT).show();
mDatabase.child(id).setValue(lesson);
} //add lesson to DB
} //else
Log.i(tag,"end");
} //onDataChange
})
Note that, since you're updating the data based on its current value, there's a chance that another user may be doing the same operation at almost the same time. If this can lead to conflicting updates in your use-case, consider using a transaction which combines the read and write from your code into a single (repeatable) operation.

This code generates five columns with 6 childs but according to this code it should generate six columns. What is the problem?

This is the code which should generate 6 columns but instead generating 5.
Please point out any logical error in the code due to which desired output cannot be obtained.
sem1Data= new String[18];
sem2Data= new String[18];
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int stdA = 0, stdB = 0;
int counter = 0;
int colCounter = 1;
LabName = editText.getText().toString();
setLabRef = database.getReference("/" + LabName);
String colName;
for (int i = 0; i <6; i++) {
colName = "col" + colCounter;
//Toast.makeText(Activity1.this, colCounter, Toast.LENGTH_SHORT).show();
if ( colCounter%2 == 0) {
for ( ; stdB<18; stdB++)
{
if(counter<6)
{
setLabRef.child(colName).child(String.valueOf(counter)).setValue(sem2Data[stdB]);
counter++;
}
else
{
counter=0;
colCounter++;
break;
}
}
}
else {
for ( ; stdA<18; stdA++)
{
if (counter<6){
setLabRef.child(colName).child(String.valueOf(counter)).setValue(sem1Data[stdA]);
counter++;
}
else {
counter=0;
colCounter++;
break;
}
}
}
}
}
});
The expected result should be 6 generated columns in firebase but instead only five are generated.
firebase screenshot
In first loop change validation block to i <=6 or i < 7
for( int i = 0; i <= 6; i++)
UPD:
if(counter<7)
I'm not sure what is the purpose of your code but here is my opinion , it might help you to clear things out.
if you will add the line Log.d(TAG, "Log track : stdB : " + stdB +" stdA : " +stdA); At the beginning of your for-loop , you will see that the values of stdA and stdB never set to zero in they just keep increasing according to the logic of your code , actually the both end with the values :
Log track : stdB : 12 stdA : 18
now , if you will look at the case where colCounter = 5 , i = 4 the value of stdA is 12
.
Thats mean that the for-loop at the else section will run (colCounter mod 2 is not zero) only 6 times and by that will not execute:
else
{
counter=0;
colCounter++;
break;
}
and by that you are missing the increase of colCounter from 5 to 6 .

Deleting objects in RealmResults somehow triggers a insertion change listener

I'm running a JobService every x hours to cleanup duplicates (objects created within 5 seconds).
First I load every MyContainer (sorted or not makes no difference for triggering insertions) and subsequently every object with matching fields and a timestamp within 5secs. Found objects are deleted
//Simplified for this question
public static void doCleanUp(Context context) {
Realm realm = context.mRealm;
realm.executeTransaction(realm1 -> {
RealmResults<MyContainer> allContainer = realm1.where(MyContainer.class)
.findAllSorted("timestamp", Sort.ASCENDING);
// implicit snapshot
for (MyContainer item : allContainer) {
if (item.isValid()) {
RealmResults<MyContainer> toDelete = realm1.where(MyContainer.class)
.notEqualTo("rowId", item.getRowId())
.equalTo("name", item.getName())
.equalTo("path", item.getPath())
.lessThan("timestamp", item.getTimestamp() + 5000)
.findAll();
if (toDelete.size() > 0) {
Log.i(TAG, "RealmCleanupJobService: deleteing objects " + toDelete.size());
toDelete.deleteAllFromRealm();
}
}
}
});
}
In another service a changeListener is listening, however it is only reacting on insertions:
private void setupRealmCollectionListener() {
mThreadPool = Executors.newCachedThreadPool();
mAllContainer = getRealm().where(MyContainer.class).findAllSorted("timestamp", Sort.DESCENDING);
mAllContainer.addChangeListener((myContainers, changeSet) -> {
if (changeSet == null) return;
OrderedCollectionChangeSet.Range[] insertions = changeSet.getInsertionRanges();
for (OrderedCollectionChangeSet.Range range : insertions) {
for (int i = 0; i < range.length; i++) {
MyContainer ac = getRealm().copyFromRealm(myContainers.get(range.startIndex + i));
mThreadPool.execute(() -> {
// ...
});
}
}
});
}
Deletion works as expected. However, some insertions appear:
How do i combat this and why insertions are appearing? I thought about some workarounds, like removing and readding the listener.
Tested with Realm 3.7.2 and 3.5.0
Android Studio 3.0 Beta 5
CompileSdk 26, Target 25

Stop rxJava observable chain execution on disposing

While debugging rxJava network call in an app i came across to a situation, that if we dispose or clear disposal object returned by subscription of a chain of observables then only first observable gets disposed not other chained observables by flatMap.
Have a look at following demo code snippet:
CompositeDisposable testCompositeDisposal = new CompositeDisposable();
private void testLoadData() {
Disposable disposable = Observable.create(sbr -> {
for (int i = 0; i < 5; i++) {
Thread.sleep(3000);
Log.w("Debug: ", "First: " + i);
sbr.onNext(true);
}
sbr.onComplete();
}).subscribeOn(Schedulers.io()).flatMap(value -> Observable.create(sbr -> {
for (int i = 0; i < 5; i++) {
Thread.sleep(3000);
Log.w("Debug: ", "Second: " + i);
sbr.onNext(true);
}
sbr.onComplete();
})).doOnNext(value -> {
Log.w("Debug: ", "doONNext");
}).doOnDispose(()-> {
Log.w("Debug: ", "doOnDispose: observable has been disposed");
}).subscribe();
testCompositeDisposal.add(disposable);
}
#Override
public void onStop() {
super.onStop();
testCompositeDisposal.clear();
}
output:
W/Debug:: First: 0
W/Debug:: doOnDispose: observable has been disposed // I dispose Observable chain here.
W/Debug:: First: 1
W/Debug:: First: 2
W/Debug:: First: 3
W/Debug:: First: 4
As you can just see in above log output that when i dispose given rxJava observable chain only first observable stops emitting items.
I want to stop all observable those are chained.
What is the idiomatic way to solve this issue?
Two things:
flatMap may pre-consume items from upstream (up to 16 on android);
Second and more applicable to your use-case, before you call onNext you should check whether the observer is disposed (via .isDisposed()) and abort when that happens.
Also, the second flatMap gets terminated (actually it never gets called). The first one continues.
EDIT
private void testLoadData() {
Disposable disposable = Observable.create(sbr -> {
for (int i = 0; i < 5; i++) {
if(sbr.isDisposed()) return; // this will cause subscription to terminate.
Thread.sleep(3000);
Log.w("Debug: ", "First: " + i);
sbr.onNext(true);
}
sbr.onComplete();
}).subscribeOn(Schedulers.io()).flatMap(value -> Observable.create(sbr -> {
for (int i = 0; i < 5; i++) {
Thread.sleep(3000);
Log.w("Debug: ", "Second: " + i);
sbr.onNext(true);
}
sbr.onComplete();
})).doOnNext(value -> {
Log.w("Debug: ", "doONNext");
}).doOnDispose(()-> {
Log.w("Debug: ", "doOnDispose: observable has been disposed");
}).subscribe();
testCompositeDisposal.add(disposable);
}

Strange java.lang.ArrayIndexOutOfBoundsException

I have a strange problem in my application
Some users send me the error:
java.lang.ArrayIndexOutOfBoundsException
i , myself , have never experienced that error. Here is the code:
for(int i =0; i<length*2; i+=2) {
if(charsLeft[i/2]==1)
temp[i]=word[i/2];//IT HAPPENS HERE
....
length = word.length;
charsLeft = new int[length];
temp = new String[length*2];
When a users goes back to home screen , the application saves and later loads the data this way:
public Bundle saveState(Bundle bundle) {
synchronized (surfaceHolder) {
if (bundle != null) {
bundle.putStringArray("word",game.word.word);
bundle.putIntArray("charsLeft",game.word.charsLeft);
bundle.putInt("length",game.word.word().length());
bundle.putStringArray("temp",game.word.temp);
bundle.putCharArray("characters", game.word.characters);
...
}
}
return bundle;
}
public synchronized void restoreState(Bundle bundle) {
synchronized (surfaceHolder) {
mPaused = true;
if (bundle != null) {
game.word.word = bundle.getStringArray("word");
game.word.length = bundle.getInt("length");
game.word.init();
game.word.charsLeft = bundle.getIntArray("charsLeft");
game.word.temp = bundle.getStringArray("temp");
game.word.characters = bundle.getCharArray("characters");
...
}
}
}
Does anyone see where it goe
EDIT
full loop
for(int i =0; i<(length)*2; i+=2) {
if(charsLeft[i/2]==1)
temp[i]=word[i/2];
else
temp[i]="_";
temp[i+1]=" ";
}
Example word[0] = a word[1] = n .....
->"answer"
the loop diveds the word in spaces and the letter or if the letter isnt guessed yet by a _
ex. a n s _ e _
EDIT:
I think I found the error :
A thread was calling the loop while an other thread could reassign word to another value , and if the loop was called before the value of length was changed then you have an error.
I changed the loop to this
temp = new String[word.length*2];
for(int i =0; i<(word.length*2); i+=2) {
if(charsLeft[i/2]==1)
temp[i]=word[i/2];
Now lets hope its fixed
Forget your loop if all you want to do is divide the word by spaces or blanks. Just using split:
String[] words = word.split( "\s|_" );
That will spit the word into an array of words on either a space or an underscore.

Categories

Resources