【问题标题】:How to wait until an async method finishes using Futures?如何等到异步方法使用 Futures 完成?
【发布时间】:2015-11-26 20:10:54
【问题描述】:

我在 Android 中有一个异步的基于 Retrofit 的 API 调用,需要等待数据库调用,直到 API 调用完成,这样我才能确定正确的数据被输入到数据库中。

我读到您可以使用 Futures 来完成此任务,但是在我当前的实现中,我得到了一个空指针异常。

API方法如下:

public Future<Void> postPrintMode(String authorization, final int userid, String deviceuid, final Map payload){
    api.postPrintMode(authorization, userid, deviceuid, payload, new Callback<PrintMode>() {

        @Override
        public void success(PrintMode printMode, Response response) {

            if (printMode.get_id() != 0) {
                dbOps.writePrintMode(userid, printMode);
                bus.getBus().post(new EVTNewPrintMode(printMode));
            }
        }

        @Override
        public void failure(RetrofitError retrofitError) {
            retrofitError.printStackTrace();
            APIUtils.showAPIResponseBody(retrofitError);
        }
    });

    return null;
}

这里是我要确保在继续读取数据库结果之前执行异步代码的块。

Future<Void> f = APIExec.getInstance().postPrintMode(IConstants.authorization, IConstants.userId, IConstants.deviceUid, payload);
    // here I get the null pointer exception
    f.get();
    // the code below needs to be executed after the postPrintMode(...) async method;
    DBPrintMode printMode = APIDBOps.getInstance().readPrintModeByPrintModeID(6);
    assertNotNull("Print Mode does not exist", printMode);

【问题讨论】:

  • 我不认为Future 是你想要的。 (例如,您需要一个同步调用来在可运行文件中进行改造)。您的模式更适合阻塞队列、倒计时锁或信号量
  • 你说得对,我使用了 CountDownLatch 并全局存储了倒计时信号,并在异步方法成功或失败时递减。
  • stored globally the countdown signal 你可以在你的方法中声明它为final并返回它。

标签: android asynchronous retrofit concurrent.futures


【解决方案1】:

您可以让调用public Future&lt;Void&gt; postPrintMode 方法的类实现new Callback&lt;PrintMode&gt; 接口。之后,您可以从中获取 postPrintMode 并将对自身的引用传递给方法。

这是一个粗略的例子(代码未经测试)

class Foo implements Callback<PrintMode> {

        Future<Void>  f;

        public Foo(){
            f = APIExec.getInstance().postPrintMode(IConstants.authorization, IConstants.userId, IConstants.deviceUid, this);
        }

        @Override
        public void success(PrintMode printMode, Response response) {

            if (printMode.get_id() != 0) {
                dbOps.writePrintMode(userid, printMode);
                bus.getBus().post(new EVTNewPrintMode(printMode));
            }
            if (f != null){
                f.get();
                // the code below needs to be executed after the postPrintMode(...) async method;
                DBPrintMode printMode = APIDBOps.getInstance().readPrintModeByPrintModeID(6);
                assertNotNull("Print Mode does not exist", printMode);
            }
        }

        @Override
        public void failure(RetrofitError retrofitError) {
            retrofitError.printStackTrace();
            APIUtils.showAPIResponseBody(retrofitError);
        }

}

【讨论】:

    【解决方案2】:

    如下创建一个 AsyncTaskThread 类,

    public class AsyncTaskThread extends AsyncTask<Void, Void, Void> {
    
    Context context;
    Handler myHandler;
    
    public AsyncTaskThread( Context activityContext, Handler handler ) {
        this.context = activityContext;
        this.myHandler = handler;
    }
    
    @Override
    protected void onPreExecute() {
        // before starting thread you can pre process few things here if needed
    }
    
    @Override
    protected Void doInBackground(Void... params) {
    

    // 在这里做任何你想做的事,比如调用你的 API 并返回你的结果 返回空值; }

    @Override
    protected void onPostExecute(Void result) {
        super.onPostExecute(result);
        // after doIn Background this method is called which will set the meesage object and give it back to handler
        Message message = new Message();
        message.obj = result;
        myHandler.sendMessage(message);
    }
    

    }

    将此异步类称为,

    new AsyncTaskThread(this, new MyHandler()).execute();
    

    并且您必须将此处理程序类放在您放在上面的类中,根据您在句柄中获得的结果,您可以执行进一步的操作,

        private class MyHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
        }
    }
    

    【讨论】:

    • 谢谢,但这意味着我需要为每个 API 调用编写一个额外的异步任务,基本上将我的异步方法包装到异步任务中。我不想仅仅为了能够运行单元测试而将我的代码库更改为这样的扩展。为测试现有代码而需要的顺序执行。
    • 嗯,这不会有太大的变化,因为您只需要创建一个单独的异步类,将 API 调用放在 doInBackground() 方法中,然后在处理程序中,您就可以继续进行 DB 调用,无论如何,选择是你的,因为我实际上并不了解实际要求
    • 我已经使用 CountDownLatch 完成了这个挑战,并在全局范围内存储了信号。一旦异步方法完成,我会这样做: AppMemory.doneSignal.countDown();如果成功 AppMemory.apiCallResult = true;
    猜你喜欢
    • 2013-08-19
    • 2013-02-15
    • 1970-01-01
    • 2013-02-05
    • 1970-01-01
    • 1970-01-01
    • 2019-05-31
    • 2019-10-19
    • 2014-08-09
    相关资源
    最近更新 更多