Android,Volley请求,响应会阻塞主线程。

11
在使用Volley处理大型响应时出现了一些问题:
String url = AppHelper.DOMAIN + "/service/pages/profile_update.json";

this.infoTextView.setText(getString(R.string.profile_info_updating));

final StringRequest stringRequest = new StringRequest(Request.Method.POST, url,
        new Response.Listener<String>() {
            @Override
            public void onResponse(String response) {
                try {
                    JSONObject json = new JSONObject(response);

                    if (json.getBoolean("success")) {
                        // manage JSON object here
                    } else {
                        Toast.makeText(ProfileActivity.this,
                                getString(R.string.connection_problem_server),
                                Toast.LENGTH_LONG).show();
                    }
                } catch (JSONException e) {
                    ProfileActivity.this.infoTextView.setText(
                            getString(R.string.profile_info_updating_error));

                    e.printStackTrace();
                }
            }
        }, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
        ProfileActivity.this.infoTextView.setText(
                getString(R.string.profile_info_updating_error));

        if (error.networkResponse != null && error.networkResponse.statusCode == 401) {
            Toast.makeText(ProfileActivity.this,
                    getString(R.string.connection_problem_permission),
                    Toast.LENGTH_LONG).show();
        }

        new android.os.Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                if (ProfileActivity.this.swipeRefreshLayout != null) {
                    ProfileActivity.this.swipeRefreshLayout.setRefreshing(false);
                }
            }
        }, 1000);

        error.printStackTrace();
    }
}) {
    @Override
    protected Map<String, String> getParams() {
        Map<String, String> params = new HashMap<>();
        params.put("auth_token", ProfileActivity.this.defaultUser.getAuthenticationToken());
        return params;
    }

    @Override
    public Map<String, String> getHeaders() throws AuthFailureError {
        Map<String, String> params = new HashMap<>();
        params.putAll(super.getHeaders());
        params.put("Accept-Encoding", "gzip,deflate");
        return params;
    }

    @Override
    protected Response<String> parseNetworkResponse(NetworkResponse response) {
        StringBuilder output = new StringBuilder();
        try {
            GZIPInputStream gStream = new GZIPInputStream(new ByteArrayInputStream(response.data));
            InputStreamReader reader = new InputStreamReader(gStream);
            BufferedReader in = new BufferedReader(reader, 16384);

            String read;

            while ((read = in.readLine()) != null) {
                output.append(read).append("\n");
            }
            reader.close();
            in.close();
            gStream.close();
        } catch (IOException error) {
            error.printStackTrace();
            return Response.error(new ParseError());
        }

        return Response.success(output.toString(), HttpHeaderParser.parseCacheHeaders(response));
    }
};

stringRequest.setRetryPolicy(new RetryPolicy() {
    @Override
    public int getCurrentTimeout() {
        // 40 seconds
        return 40000;
    }

    @Override
    public int getCurrentRetryCount() {
        return DefaultRetryPolicy.DEFAULT_MAX_RETRIES;
    }

    @Override
    public void retry(VolleyError error) throws VolleyError {
        throw error;
    }
});

Volley.newRequestQueue(this).add(stringRequest);

这段代码会阻塞主线程,导致应用程序冻结。

此外,我设置了一些头部值以允许gzip响应和处理数据的代码。但这不是引起意外行为的原因。只有当onResponse(String response)开始时,应用程序才会冻结。

我该怎么做才能避免这种情况?


1
这可能与您在UI线程上执行JSON解析有关,而不是在parseNetworkResponse中执行。 - EpicPandaForce
代码看起来没问题.. 你能暴露一下这个网络请求周围的其他代码吗? - Dexto
2个回答

21

onResponse和onErrorResponse在UI线程上调用,因此在这些方法内执行任何重操作都会使应用程序反应不佳。我猜你正在尝试在onResponse()中解析响应,这是不正确的。

你需要将解析逻辑移动到parseNetworkResponse中,因为这是在后台线程中调用的方法。有关更多详细信息,请参见以下链接:

https://developer.android.com/training/volley/request-custom.html


1
如果调用请求来自IntentService,那么onResponse()会在UI线程上执行吗? - Bugs Happen
1
不管调用线程是什么,onResponse() 方法都将在 UI 线程上被调用。 - Arpit Ratan

0
如果有帮助到某人,可以尝试在onResponse方法内创建一个新的线程,在该线程内执行数据解析。希望这个答案对你有用。

1
你好,正如4年前被接受的回答所解释的那样,响应处理应该在parseNetworkResponse中完成,而这已经在工作线程上运行了。 - Eugen Pechanec

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