在Java中处理异步回调

3
我的OpenFeint方法之一用于恢复排行榜数据,需要异步回调到一个方法,该方法应修改我的本地变量(使用一个很棒的hack)。我的问题现在是,一旦回调被调用,执行就会继续进行,并且由于分数值尚未更改,它将返回nullPointer。有没有办法使所有内容同步或从主函数返回回调值?
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;
}

回调函数定义:http://m.the9.com/ioshelp/Android_zh/doc/com/openfeint/api/resource/Leaderboard.GetUserScoreCB.html
3个回答

0
如果您的方法可以在异步回调执行期间阻塞,则可以创建一个类似于 CountDownLatch 的对象,例如:
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;
    ...
}

这会不会让我的应用程序停顿一秒钟?我需要这个方法尽可能快地运行,因为它是游戏的一部分。 - MLProgrammer-CiM
如果在UI线程上执行getScoreLeaderBoard函数,那么是的,它会等待onSuccess事件发生。 - zapl
这是一个答案,但可能不会是最优解 :/ 不过我还是试一试。 - MLProgrammer-CiM

0

而不是

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

让它变成这样

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

将处理getScoreLeaderBoard返回值的代码放在(onSuccess中)或从中调用它的方法。

这样你就不必等待和阻塞线程了。

您还可以让Activity实现Leaderboard.GetUserScoreCB,然后就像拥有另一个onCreateonClick等方法一样。

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

我尝试使用你的想法,但使用第二个方法参数long来进行更改,但执行速度仍然太快,导致我收到了nullPointer。 - MLProgrammer-CiM
它不是太快 - 即使异步线程什么也不做,只是将结果写入您的变量,也不可能立即获得结果。这是因为调用l.getUserScore的线程会一直执行,直到返回未更改的变量。(¹)有可能调度程序在您按下return之前停止执行调用线程并让另一个线程执行,但这取决于纯粹的运气。您唯一可以确保不返回null的方法是显式地停止调用线程的执行(这就是cdl.countDown()所做的)。 - zapl
听起来你需要重新设计一下,如果这些解决方案不起作用的话。如果你的本地/包装器/处理程序调用链确实需要将分数返回给它,也许你能做的最好的就是使用最近缓存的分数,并希望有一种方法通知API有一个更新的分数,一旦你的异步回调执行并且分数已经改变。如果没有,我会寻找其他人如何使用API的示例,或者寻找Feint开发者论坛... - Rob I
OpenFeint论坛大部分时间都很冷清,我总是在SO上询问之前先通过谷歌搜索答案:P 让我感到奇怪的是这以前还没有做过,并且Android API没有像iOS那样方便的同步函数。我可能最终会在加载期间获取分数,缓存新的高分并从那里开始处理,就像你说的那样。 - MLProgrammer-CiM

0
重写AsyncTask的onPostExecute方法,在其中执行您需要的操作。

它没有 :( http://m.the9.com/ioshelp/Android_en/doc/com/openfeint/api/resource/Leaderboard.GetUserScoreCB.html - MLProgrammer-CiM
onSuccess 无法为像 getScoreboard 这样的上层方法返回值,这就是为什么我使用本地变量的原因。onSuccess 需要一些时间来运行,同时执行会继续并到达返回部分。 - MLProgrammer-CiM

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接