【问题标题】:Android App using Volley get race condition使用 Volley 的 Android 应用程序获取竞争条件
【发布时间】:2015-05-14 18:23:41
【问题描述】:

我正在开发一个 Android 移动应用程序,该应用程序本身最重要的功能之一就是能够与第三方 API 服务通信。

提供这些 API 的第三方服务希望在我发出的每个 API 请求中都包含一个“信标”。

“信标”是一个“长整数”,对于每个请求,它必须是唯一且递增的。

问题是: 我正在发出几个这样的请求,但我不知道这些请求中的哪一个会首先完成,所以我遇到了竞争条件:第二个请求在第一个请求使第一个请求无效之前很快结束!

当一个按钮被点击时,将执行以下动作:

public void fireRequests(View view)
{
    long first_beacon = System.nanoTime();
    fireFirstRequest(view, first_beacon);

    long second_beacon = System.nanoTime();
    fireSecondRequest(view, second_beacon);
}

我以正确的方式使用 Volley,设置回调等。示例:

fireFirstRequest 方法:

public void fireFirstRequest(View view, long beacon)
{
    final ThirdPartyLib api_lib = new ThirdPartyLib(getActivity());

    api_lib.doOperationA(beacon, new ThirdPartyLib.MyOwnCallback()
    {
        @Override
        public void update(JSONObject jsonObject)
        {
            try
            {
                JSONObject result = jsonObject.getJSONObject("response");
                /* my code */
                Log.d("doOperationA", result)
            }
            catch (JSONException e)
            {
                e.printStackTrace();
            }
        }
    });
}

fireSecondRequest 方法:

public void fireSecondRequest(View view, long beacon)
{
    final ThirdPartyLib api_lib = new ThirdPartyLib(getActivity());

    api_lib.doOperationB(beacon, new ThirdPartyLib.MyOwnCallback()
    {
        @Override
        public void update(JSONObject jsonObject)
        {
            try
            {
                JSONObject result = jsonObject.getJSONObject("response");
                /* my code */
                Log.d("doOperationB", result)
            }
            catch (JSONException e)
            {
                e.printStackTrace();
            }
        }
    });
}

这里是执行日志:

03-12 14:26:56.252  18769-18769/it.example.app D/Volley: queued doOperationA
03-12 14:26:58.124  18769-18769/it.example.app D/Volley: queued doOperationB
03-12 14:26:59.433  18769-18769/it.example.app D/App: doOperationB: {
    "error": false,
    "payload": {
        "foo": "bar"
    }
}
03-12 14:27:04.181  18769-18769/it.example.app D/App: doOperationA: {
    "error": true,
    "errorMessage": "invalid beacon"
    "payload": {}
}

问题是:在触发 API 请求之前跟踪信标或保持“执行顺序”分离(即使我们谈论的是异步请求)的最佳方式是什么?

当我完全确定第一个请求已完成时,我的粗略解决方案是在 fireFirstRequest() 的回调中调用 fireSecondRequest()。 我知道,这是杀死令人敬畏的异步请求世界的最佳方式。

修改动作:

public void fireRequests(View view)
{
    long first_beacon = System.nanoTime();
    fireFirstRequest(view, first_beacon);
}

fireFirstRequest 修改方法,带final查看参数:

public void fireFirstRequest(final View view, long beacon)
{
    final ThirdPartyLib api_lib = new ThirdPartyLib(getActivity());

    api_lib.doOperationA(beacon, new ThirdPartyLib.MyOwnCallback()
    {
        @Override
        public void update(JSONObject jsonObject)
        {
            try
            {
                JSONObject result = jsonObject.getJSONObject("response");
                /* my code */
                Log.d("doOperationA", result)

                /* fire second request */
                // EDIT
                fireSecondRequest(view, System.nanoTime());
            }
            catch (JSONException e)
            {
                e.printStackTrace();
            }
        }
    });
}

【问题讨论】:

    标签: android api asynchronous callback android-volley


    【解决方案1】:

    您没有添加启动 Volley RequestQueue 的代码部分,但我假设您正在使用以下方式创建默认方式:

    RequestQueue requestQueue = Volley.newRequestQueue(context, stack);
    

    当你这样做时,你会得到一个请求队列,默认情况下允许 4 个并发请求。您可以通过查看此方法用于创建请求队列的构造函数来了解这一点:

    private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
    
    ...
    
    public RequestQueue(Cache cache, Network network) {
        this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
    }
    

    如果不使用默认方法创建 RequestQueue,而是创建自己的 RequestQueue,线程池大小为 1,则可以解决此问题。这样,不会有 2 个并发请求,并且请求将按照发送顺序发送。

    当然,这样做的不利之处在于这会大大降低您的应用程序的速度。如果所有请求都必须等到前一个请求完成,这会在您的应用程序中造成严重的瓶颈。

    也许考虑使用超过 1 个请求队列,并且仅将这个特殊的请求队列用于依赖于这个特殊约束的请求。

    希望这会有所帮助。

    【讨论】:

    • 感谢您的回答。你觉得我自己的解决方案怎么样?第一个请求完成后调用第二个请求?
    • 这很公平,但似乎有点麻烦,并且会导致您进入所谓的“回调地狱”,其中一半的代码嵌套在回调中。使用此方法,任何依赖此信标的请求都可以简单地使用与您应用程序的其余请求不同的请求队列,并且您可以简单地忘记这个问题的存在。
    • 根据您的回答,我假设使用不同的请求队列意味着排队进入第二个队列的请求将不会得到处理,直到所有排队进入第一个队列的请求都完成。这对吗?在您看来,什么是已完成的请求?一个有自己响应的请求还是一个简单地启动的请求?抱歉这些愚蠢的问题
    • 一个请求在负责处理它的线程完成时被认为完成。这包括实际请求,以及未委托给不同(通常是主)线程的请求的任何前/后处理。
    猜你喜欢
    • 2022-12-14
    • 2019-11-20
    • 1970-01-01
    • 2011-12-12
    • 2019-02-10
    • 2015-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多