IntentService onHandleIntent is running on main thread - android

I decided to use intent service for a task involving looping big number of results, calculating a summary result and inserting it back in firestore.
This is how i am calling my intent service from activity.
onDataSnapShotListenerForPointsAndRating = db.collection("PointsAndRating")
.addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(#Nullable QuerySnapshot queryDocumentSnapshots,
#Nullable FirebaseFirestoreException e) {
Intent serviceIntent = new Intent(PlayWithMrMathActivitySinglePlayer.this,
MyIntentService.class);
serviceIntent.putExtra("SENDER", "Mr. Math");
serviceIntent.putExtra("emailAddress", emailAddress);
serviceIntent.putExtra("OwnID", OwnID);
Log.d(TAG, "Hello MyIntentService: I am Mr Math and i am starting service");
**startService(serviceIntent);**
}
});
This is how I am doing that big loop work:
#Override
protected void onHandleIntent(Intent intent) {
Log.d(TAG, "onHandleIntent: inside onHandleIntent ");
String sender = intent.getStringExtra("SENDER");
emailAddress = intent.getStringExtra("emailAddress");
OwnID = intent.getStringExtra("OwnID");
Log.d(TAG, "onHandleIntent: inside onHandleIntent sender = " + sender);
Log.d(TAG, "onHandleIntent: inside onHandleIntent emailAddress = " + emailAddress);
Log.d(TAG, "onHandleIntent: inside onHandleIntent OwnID = " + OwnID);
Log.d(TAG, "onDataSnapShotListenerForPointsAndRating: ");
Log.d(TAG, "onHandleIntent: currentThread = " + Thread.currentThread().getId());
db.collection("PointsAndRating")
.orderBy("gmq", Query.Direction.DESCENDING)
.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot>task) {
Long myWorldRank = new Long(0);
Log.d(TAG, "onComplete: onDataSnapShotListenerForPointsAndRating = " + task.getResult().size());
if (task.isSuccessful()) {
Log.d(TAG, "onHandleIntent InonComplete : If currentThread = " + Thread.currentThread().getId());
for (QueryDocumentSnapshot document: task.getResult()) {
Log.d(TAG, document.getId() + "= onDataSnapShotListenerForPointsAndRating =## => " + document.getData());
myWorldRank = myWorldRank + 1;
if (document.getId().equals(emailAddress)) {
break;
}
***Log.d(TAG, "onHandleIntent InonComplete : For Loop currentThread = " + Thread.currentThread().getId());***
} // end of for loop
Log.d(TAG, "onDataSnapShotListenerForPointsAndRating: Rank = " + myWorldRank);
}
final Map<String, Object> myWorldRankUpdateNugget = new HashMap<>();
myWorldRankUpdateNugget.put("myWorldRank", myWorldRank);
db.collection("PointsAndRating")
.whereEqualTo("userID", OwnID)
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document: task.getResult()) {
db.collection("PointsAndRating")
.document(document.getId())
.set(myWorldRankUpdateNugget, SetOptions.merge())
.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
Log.d(TAG, "onDataSnapShotListenerForPointsAndRating: WORLD RANK UPDATED ");
}
});
} // end of for loop
}
Log.d(TAG, "onHandleIntent: EXIT currentThread = " + Thread.currentThread().getId());
}
});
}
});
} // end of onhandle
and i see this in logs.
06-28 17:48:34.068 11076-11154/com.udiversity.myapplication D/MyIntentService: onHandleIntent: currentThread = 3867
06-28 17:49:36.118 11076-11076/com.udiversity.myapplication D/PlayWithMrMathActivitySinglePlayer: onCreate: currentThread = 1
06-28 17:49:36.118 11076-11076/com.udiversity.myapplication D/PlayWithMrMathActivitySinglePlayer: OnSubmitPOC: currentThread Back Home OnCreate
06-28 17:49:36.118 11076-11106/com.udiversity.myapplication D/PlayWithMrMathActivitySinglePlayer: doInBackground: currentThread = 3837
06-28 17:49:36.148 11076-11076/com.udiversity.myapplication D/PlayWithMrMathActivitySinglePlayer: onProgressUpdate currentThread = 1
06-28 17:49:36.158 11076-11076/com.udiversity.myapplication D/PlayWithMrMathActivitySinglePlayer: onProgressUpdate EXIT 1 currentThread = 1
06-28 17:49:48.328 11076-11076/com.udiversity.myapplication D/MyIntentService: onHandleIntent InonComplete : If currentThread = 1
06-28 17:49:48.328 11076-11076/com.udiversity.myapplication D/MyIntentService: onHandleIntent InonComplete : For Loop currentThread = 1
06-28 17:49:48.328 11076-11076/com.udiversity.myapplication D/MyIntentService: onHandleIntent InonComplete : For Loop currentThread = 1
06-28 17:49:48.328 11076-11076/com.udiversity.myapplication D/MyIntentService: onHandleIntent InonComplete : For Loop currentThread = 1
06-28 17:49:48.328 11076-11076/com.udiversity.myapplication D/MyIntentService: onHandleIntent InonComplete : For Loop currentThread = 1
06-28 17:49:48.338 11076-11076/com.udiversity.myapplication D/MyIntentService: onHandleIntent InonComplete : For Loop currentThread = 1
06-28 17:49:48.338 11076-11076/com.udiversity.myapplication D/MyIntentService: onHandleIntent InonComplete : For Loop currentThread = 1
06-28 17:49:48.338 11076-11076/com.udiversity.myapplication D/MyIntentService: onHandleIntent InonComplete : For Loop currentThread = 1
06-28 17:49:48.338 11076-11076/com.udiversity.myapplication D/MyIntentService: onHandleIntent InonComplete : For Loop currentThread = 1
06-28 17:49:48.338 11076-11076/com.udiversity.myapplication D/MyIntentService: onHandleIntent InonComplete : For Loop currentThread = 1
06-28 17:49:48.338 11076-11076/com.udiversity.myapplication D/MyIntentService: onHandleIntent InonComplete : For Loop currentThread = 1
06-28 17:49:48.338 11076-11076/com.udiversity.myapplication D/MyIntentService: onHandleIntent InonComplete : For Loop currentThread = 1
06-28 17:49:48.338 11076-11076/com.udiversity.myapplication D/MyIntentService: onHandleIntent InonComplete : For Loop currentThread = 1
Why this loop inside onHandleIntent->db.Collection("").get().onComplete() is running on main thread ?
public class MyIntentService extends IntentService {
private static final String TAG = "MyIntentService";
FirebaseFirestore db = FirebaseFirestore.getInstance();
db is my local class database instance.

At the beginning of onHandleIntent you use Thread.currentThread().getId() in order to get the thread's ID. It is logged as 3867. Inside the onComplete method you log again the thread ID and you get 1.You also log the thread ID of the nested onComplete, but it doesn't look like it has been added to the provided logs.
The thread IDs of the should-be-different threads are different and thus the code is being executed asynchronously. I'm unsure of what error do you see, but if what worries you is that the ID number is 1, according to the documentation the ID of a thread is unique only during its lifetime when a thread is terminated it may be reused again.
Actually in the provided logs we can see that the thread ID 1 is being used by PlayWithMrMathActivitySinglePlayer but is terminated right before being used by MyIntentService.
Apart from that I don't see anything strange. What is making you believe that there's an error on the code or its execution?

Related

For loop don't execute all the code before continue

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.

why the id of thread changed

i don't understand why i get different Id for the same thread here is the output
i know the id field in any programming language must stay the same for future reference
PS:this code is inside a bound service class i know the bound service is on the main thread but why id of the "message thread" changed 3 times from -1 to 21947 to 41045
*logcat output:
onCreate: thread id = 1 name =main
first is alive true thread id -1 thread name =servicemessanger
sec is alive true thread id 21947 thread name =servicemessanger
handleMessage: thread id= 41045name = servicemessanger*
code :
#Override
public void onCreate() {
Log.i(TAG, "onCreate: thread id = "+ Thread.currentThread().getId()+" name =" +Thread.currentThread().getName() );
super.onCreate();
HandlerThread thread = new HandlerThread("servicemessanger");
thread.start();
Log.i(TAG, " first is alive "+thread.isAlive() + " thread id "+ thread.getThreadId()+ " thread name ="+thread.getName());
mHandler = new Handler(thread.getLooper()){
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.i(TAG, "handleMessage: thread id= "+Thread.currentThread().getId()+"name = " + Thread.currentThread().getName());
}
};
mHandler.sendEmptyMessage(0);
Log.i(TAG, " sec is alive "+thread.isAlive() + " thread id "+ thread.getThreadId()+ " thread name ="+thread.getName());
}
thank you,

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);
}

Call Async task class inside for loop

I am sending data to web server using asynctask task class. So I am taking all value from SQLite in one array list and in for loop I'm calling asynctask class to send data but the problem is loop finishes its execution and send only last value from array list to the web service. But the web service is calling according to for loop.
Any suggestion would be helpful.
Following is my code:
for (int i = 0; i < outletMasterList.size(); i++) {
outletMaster = outletMasterList.get(i);
outlet_Code = outletMaster.getOutletCode();
outlet_UID = outletMaster.getOutletUID();
finalValue = AppConstant.IMEI_NUMBER;
System.out.println("Sync i : : : : " + i + " " + respose_count);
new CommonAsyncTaskWithoutProgressBar(Sync.this, callbackInterface)
.execute("0",
AppConstant.NAMESPACE,
AppConstant.METHOD_NAME,
AppConstant.ACCESS_KEY,
AppConstant.Set_OutletFromHH,
AppConstant.OutletSyncParameters, finalValue);
}

Parse.com Android public void done does not return my ArrayList properly

I want to fill an ArrayList with the results I get back from a Parse query. When I get the results I add them to the ArrayList and print the ArrayList size to the console to make sure the results are added, which is succesful, but when I return the ArrayList it's empty. Can anyone explain to me why this happens?
public ArrayList<ParseObject>findAllGroupByUserId(ParseUser userId){
//TODO hier uit db halen alle groupen van user
final ArrayList<ParseObject> groups = new ArrayList<>();
ParseQuery<Group_user> query = ParseQuery.getQuery("Group_user");
query.whereEqualTo("user_id", userId);
query.findInBackground(new FindCallback<Group_user>() {
#Override
public void done(List<Group_user> objects, ParseException e) {
if (e == null) {
for (Group_user group : objects) {
Log.e("SUCCESS", group.getObjectId() + " , " + group.getGroup_id().getObjectId());
ParseObject g = new Group();
groups.add(g);
}
System.out.println(groups.size() + " :Done method"); //THIS RETURNS 2
} else {
Log.e("ERROR", "message: " + e);
}
Log.e("SUCCESS", "we have " + groups.size() + " results");
}
});
System.out.println(groups.size() + " :return"); // THIS RETURNS 0
return groups;
}
Because findInBackground() runs asynchronously on a different thread. You need to execute your remaining logic from the done() call back to get the populated array.
Think of it like this:
Thread 1 -> invokes findInBackground() -> thread one is running -----------> group is empty until Thread 2 finishes
Thread 2 spawned -> reaches out to server and gets query results -> invokes done call back on Thread 1 (now you have the data ready)
So I'm assuming that Group_user is a subclass of ParseObject that you've already defined. Since the findInBackground is async, you should change logic of the calling of the function to async too. Instead of returning list of objects like you were before, do all the logic in the done function of the query, no need to return.
public void findAllGroupByUserId(ParseUser userId) {
ParseQuery<Group_user> query = ParseQuery.getQuery("Group_user");
query.whereEqualTo("user_id", userId);
query.findInBackground(new FindCallback<Group_user>() {
#Override
public void done(List<Group_user> groups, ParseException e) {
if (e == null && groups != null) {
for (Group_user group : groups) {
// perform all logic here
}
} else {
Log.e("Find Callback", "Oh no! Query failed!");
}
}
});
}

Categories

Resources