Android 使用 Volley 的应用获取竞争条件

Android App using Volley get race condition

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

提供这些 API 的第三方服务希望 "beacon" 包含在我发出的每个 API 请求中。

"beacon" 是一个 "long integer",它必须是唯一的并且对于每个请求都是递增的。

问题是: 我正在触发其中的几个请求,但我不知道这些请求中的哪一个会先完成,所以我 运行 进入了竞争状态:第二个请求在第一个请求使第一个请求无效之前快速结束!

单击按钮时将执行以下操作:

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 请求之前跟踪信标或保持 "execution order" 分离的最佳方法是什么,即使我们谈论的是异步请求?

我的粗略解决方案是当我完全确定第一个请求已完成时,在 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();
            }
        }
    });
}

您没有添加启动 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 的默认方法,而是创建线程池大小为 1 的自己的 RequestQueue,则可以解决此问题。这样,就没有2个并发请求,请求将按派发顺序发送。

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

或许可以考虑使用1个以上的请求队列,并且只对依赖这个特殊约束的请求使用这个特殊的请求队列。

希望这对您有所帮助。