Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java - Android : Thread being called (run) twice

I would like some help regarding Java - Android MultiThreading

While learning to develop my app in a multi-threading way in order to take advantage of the ever-growing multi-core devices market share (most devices are quad core now, some even octo-core), I ran in a situation where my threads are either being calling twice or running twice.

I just don't why and how.

[EDIT 3] Alright, I narrowed down the issue : I called the AsyncTask from the onResume() method. Although my app did not lost focus (which would mean a call to onPause() then back to onResume() upon return of focus in which case my threads would be run twice) during the tests, I solved the issue by moving away the call to FetchFriendsList to another place. So far so good, but since in my tests the app did not loose focus or perhaps it did but I could not witness it (!), I think there is another reason behind so I'd say my problem is not entirely solved ... at least for the moment. It does work though. Perhaps I did solve the issue but I do not know how :( [end of EDIT 3]

I am implementing last Facebook SDK and I am using it to fetch the end-user friends list, which seems to do the work. Since I am running this operation in an AsyncTask, I am not using request.executeAsync(). Instead I am using request.executeAndWait(). Facebook JavaDoc does state that this method must only be used if I am not in a the Main UI Thread which is my case otherwise I would get a NetworkOnMainThreadException.

Anyway, this is where the weird behavior is happening.

private final ArrayList<GraphUser> userFriendsList = new ArrayList<GraphUser>(); 
public final void fetchFriendsList() {
    if (this.session != null && this.session.isOpened()) {
        final Request requestUserFriendsList = Request.newMyFriendsRequest(
                this.session, new Request.GraphUserListCallback()                                         
                    public final void onCompleted(final List<GraphUser> users, final Response response) {
                        if (users != null && users.size() > 0) {
                            Log.v("Retrieved Friends List -> ", String.valueOf(users.size()));
                            userFriendsList.addAll(users);
                        }
                    }
                }
        );
        if (this.asyncFlag)
            requestUserFriendsList.executeAsync();
        else
            requestUserFriendsList.executeAndWait();
    }
}

In my case, asyncFlag is set to false because I need to do stuff synchronously in that specific order :

  1. Fetch User Friends List (not on the Main (UI) Thread)
  2. Save friends list on device (separate new thread)
  3. Save friends list on a server (separate new thread)

Following this pattern, the line userFriendsList.addAll(users); is called twice. In the logcat, the Log.vis showed twice as well, and finally looking with the debugger, the content of the user friends list is made of duplicates.

But that's not all ... step 2 and 3 are indeed two separate threads which are both created and spawned within the same method : public final void asyncSaveFacebookFriendsList().

And guess what, this method is even called twice !

just why ?

At the beginning I was calling the method for step 2 and 3 like this :

[...]
userFriendsList.addAll(users);
asyncSaveFacebookFriendsList(); // it was private before
[...]

This is where the issue started as both line were running twice.

So I thought, alright, I'll call it later like this :

[...]
fetchFriendsList();
asyncSaveFacebookFriendsList(); // it is now public
[...]

But the issue remains still. If I don't call public final void asyncSaveFacebookFriendsList(), then nothing is run twice.

Why does this issue happen ? Is there something I did not get in Java Threads ?

I do not think this is somehow related to the Facebook SDK because following the same pattern (and doing it also at the same time), I have the same issues when fetching and storing the end-user Twitter friends list.

So I do believe I am doing something wrong. Does someone have any idea in what possible case a thread is called twice ?

Note : all threads are started this way : thread.start(). I am not using any ThreadPool nor the ExecutorService.

In case you need more background context : Content of AsyncTask : (no need to wonder why Void and Long, I remove the irrelevant code related to it)

private final class FetchFriendsLists extends AsyncTask<Long, Integer, Void> {
        protected final Void doInBackground(final Long... params) {
            if (params[0] != Long.valueOf(-1)) {
                [...]
                twitterAPI.fetchUserFriendsList();
                publishProgress(1, -1);
            }
            if (params[1] == Long.valueOf(0)) {
                [...]
                facebookAPI.fetchFriendsList();
                publishProgress(-1, 0);
            }
            return null;
        }

        protected final void onProgressUpdate(Integer... flags) {
            super.onProgressUpdate(flags);
            if (flags[0] != -1)
                twitterAPI.asyncSaveFacebookFriendsList();
            if (flags[1] == 0)
                facebookAPI.asyncSaveFacebookFriendsList();
        }
    }

As you can see, I start step 2 and 3 in onPublishProgress() which runs on the Main UI Thread. Brefore it was in the doInBackground() method : the issue happens in both cases!

[EDIT] After further test, it would seem any kind of code is in fact running twice. I created a simple method called test in which in print a counter. The counter incremente twice as well !

like image 718
Mackovich Avatar asked Nov 10 '22 07:11

Mackovich


1 Answers

Why you use onProgressUpdate?¿?

onProgressUpdate(Progress...), [...]. This method is used to display any form of progress in the user interface while the background computation is still executing. For instance, it can be used to animate a progress bar or show logs in a text field.

This is used not at the finish of the petition, but when progress increased.
Read this:
http://developer.android.com/reference/android/os/AsyncTask.html

You need to use:

 protected void onPostExecute(Long result) {
like image 176
Sulfkain Avatar answered Nov 15 '22 05:11

Sulfkain