3

One of my OpenFeint methods to recover leaderboard data needs an asynchronous callback to a method that is supposed to modify one of my local variables (using a wonderful hack). My problem now is that once the CB is called the execution continues and, as the score value has not changed yet it returns a nullPointer. Any way to make everything synchronize or return the callback value from the main function?

private long getScoreLeaderBoard(String idLeaderBoard) {
    for (Leaderboard l : OpenFeintX.leaderboards) {
        if (l.name == null)
            break;
        if (l.resourceID().equalsIgnoreCase(idLeaderBoard)) {
            final Score s[] = new Score[1];
            l.getUserScore(OpenFeint.getCurrentUser(),
                    new Leaderboard.GetUserScoreCB() {

                        @Override
                        public void onSuccess(Score score) {
                            s[0] = score;
                        }
                    });
            if (s[0] != null) // If user has no score onSuccess get a null
                return s[0].score;
            else
                return 0;
        }
    }

    return 0;
}

Callback definition: http://m.the9.com/ioshelp/Android_en/doc/com/openfeint/api/resource/Leaderboard.GetUserScoreCB.html

MLProgrammer-CiM
  • 17,231
  • 5
  • 42
  • 75

3 Answers3

0

Override the onPostExecute of the AsyncTask and do what you need to do in there

tyczj
  • 71,600
  • 54
  • 194
  • 296
  • It doesn't have one :( http://m.the9.com/ioshelp/Android_en/doc/com/openfeint/api/resource/Leaderboard.GetUserScoreCB.html – MLProgrammer-CiM Apr 27 '12 at 14:10
  • The onSuccess cannot return the value for an upper method like getScoreboard, that's why I am using the local vble. onSuccess takes some time to run, meanwhile the execution continues and gets to the return part. – MLProgrammer-CiM Apr 27 '12 at 14:14
0

If your method can stand to block while the asynchronous callback is executing, you could have it create a CountDownLatch like this:

private long getScoreLeaderBoard(String idLeaderBoard) {
    final CountDownLatch cdl = new CountDownLatch(1);
    ...
                    @Override
                    public void onSuccess(Score score) {
                        s[0] = score;
                        cdl.countDown();
                    }
    ...
        cdl.await();
        return s[0].score;
    ...
}
Rob I
  • 5,627
  • 2
  • 21
  • 28
0

instead of

private long getScoreLeaderBoard(String idLeaderBoard) { /* takes long */ }

make it

private void requestScoreLeaderBoardUpdate(String idLeaderBoard) { /* takes long */ }

and put the code that handled the returnvalue of getScoreLeaderBoard in (or call the method of it from) onSuccess.

That way you don't have to wait and block your thread.

You can also let your Activity implement Leaderboard.GetUserScoreCB and then it's like having another onCreate, onClick, ... like method.

class MyActivity extends Activity implements Leaderboard.GetUserScoreCB {

    private void requestScoreLeaderBoardUpdate(String idLeaderBoard) {
        // ..
        l.getUserScore(OpenFeint.getCurrentUser(), this /* your activity */);
    }

    @Override
    public void onSuccess(Score score) {
        s[0] = score;
        // do anything you want with the new score here
    }
}
zapl
  • 63,179
  • 10
  • 123
  • 154
  • I tried using your idea but using a second method parameter long which is the one being changed, but the execution is still too fast and I get the nullPointer. – MLProgrammer-CiM Apr 27 '12 at 14:44
  • It's not to fast - it is not(¹) possible to get the result immediately even if the asynchronous thread does nothing but write the result into your variable. That is simply because the thread that calls `l.getUserScore` keeps executing until it returns the untouched variable. (¹) There is a chance that the scheduler stops executing the calling thread and lets the other one execute before you hit `return` but that depends on pure luck. The only way you can be sure that you don't return `null` is to explicitly stop the calling thread from executing (which is what `cdl.countDown()` does) – zapl Apr 27 '12 at 16:21
  • Sounds like you need to redesign a bit if these solutions don't work. If your native/wrapper/handler call chain really needs the score to be returned to it, maybe the best you can do is to use the most-recently-cached score, and hopefully there's a way to notify the API that there's an updated score, once your asynchronous callback executes and the score has changed. If not, I would look for examples of how other people use the API, or perhaps look for a Feint developer forum... – Rob I Apr 27 '12 at 16:48
  • OpenFeint forums are like a barren most of the time, and I always bother googling the answer before asking in SO :P Strikes me odd that this has not been done before and that the Android API does not have convenient synchronous functions that iOS has. I may end up getting the score during load, caching new high scores and working with it from there as you said. – MLProgrammer-CiM Apr 28 '12 at 19:31