【问题标题】:How to properly close HttpURLConnection on Android如何在 Android 上正确关闭 HttpURLConnection
【发布时间】:2012-08-02 09:35:04
【问题描述】:

我正在使用 HttpURLConnection 来查询后端服务器。请求是 POST。我的代码如下:

InputStream is = null;
HttpURLConnection connection = null;
try {
    connection = (HttpURLConnection)new URL(uri).openConnection();
    connection.setRequestMethod("POST");
    connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=" + ENCODING);
    connection.setDoOutput(true);
    byte[] content = buildFormUrlEncoded(params);
    connection.setRequestProperty("Content-Length", String.valueOf(content.length));

    connection.connect();

    OutputStream os = null;
    try {
        os = connection.getOutputStream();
        os.write(content);
    } finally {
        if (os != null) { os.close(); }
    }

    is = connection.getInputStream();
    handle(is);
} finally {
    if (is != null) { is.close(); }
    if (connection != null) { connection.disconnect(); }
}

但是我收到了这个 StrictMode 错误:

A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks.
java.lang.Throwable: Explicit termination method 'close' not called
    at dalvik.system.CloseGuard.open(CloseGuard.java:184)
    at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:300)
    at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:257)
    at libcore.net.http.HttpConnection.setupSecureSocket(HttpConnection.java:210)
    at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.makeSslConnection(HttpsURLConnectionImpl.java:477)
    at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:432)
    at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:282)
    at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:232)
    at libcore.net.http.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:80)
    at libcore.net.http.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:164)

当我调试代码时,os.close()is.close()connection.disconnect() 被调用。

会因为连接在池中保持活动而发生 StrictMode 吗?

编辑

  • 如果添加了connection.setRequestProperty("connection", "close");,StrictMode 错误就会消失。
  • 如果使用 http URL 而不是 https URL,则 StrictMode 错误消失。
  • 如果使用 BufferedReader,则 StrictMode 错误发生的频率会降低。

为了安全起见,我想保留 https 并保持活动状态以减少握手开销。

编辑 2

看起来这种情况只发生在 android 3.X 和 4.0.X 的情况下。

【问题讨论】:

  • handle(is)的实现是什么
  • 响应是 XML。 handle 做 XML 解析。因此,handle 中的 is 被读取为 EOF

标签: android


【解决方案1】:

我的代码如下所示:

@Override
    protected Boolean doInBackground(Void... params) {
        JSONObject holder = new JSONObject();
        try {
            Resources res = getResources();

            holder.put(res.getString(R.string.str),"");


            URL url = new URL(res.getString(R.string.url));
            String charset = res.getString(R.string.utf);
            HttpURLConnection http = null;

                HttpsURLConnection https = (HttpsURLConnection) url.openConnection();

                http = https;
                http.setRequestMethod(res.getString(R.string.post));
                http.setDoInput(true);
                http.setDoOutput(true);
                http.setRequestProperty(res.getString(R.string.charset), charset);
                http.setRequestProperty(res.getString(R.string.content_type), "application/x-www-form-urlencoded;charset=" + charset);

                String query = String.format("query1=%s&query2=%s&query3=%s&query4=%s", 
                     URLEncoder.encode(res.getString(R.string.qu1), charset), 
                     URLEncoder.encode(res.getString(R.string.qu2), charset),
                     URLEncoder.encode(res.getString(R.string.qu3), charset),
                     URLEncoder.encode(holder.toString(), charset));

                OutputStream output = null;
                try {
                     output = http.getOutputStream();
                     output.write(query.getBytes(charset));
                } finally {
                     if (output != null) try { output.close(); } catch (IOException logOrIgnore) {}
                }
                //InputStream response = http.getInputStream();

                BufferedReader in = new BufferedReader(new InputStreamReader(http.getInputStream()),4800); 
                StringBuffer responseBuffer = new StringBuffer();
                String line;

                while ((line = in.readLine()) != null) { 
                    responseBuffer.append(line);
                }

                in.close();
                answer = new Gson().fromJson(responseBuffer.toString(), Answer.class);
                //s = responseBuffer.toString();
        } catch (Exception e) {
            e.printStackTrace();
            getData();
        }
        return true;
    }

相信你会在那里找到答案。

【讨论】:

  • 你添加了 IOException 吗?你需要用 try-catch 包围 .close()
  • 我关闭了保活,没有更多的资源泄漏。现在我试图找到Android中负责连接池的代码在哪里。我怀疑池中的连接是弱引用,GC 最终确定了它。因为它仍然处于打开状态,所以存在来自 CloseGuard 的 StrictMode 错误。
  • 你为什么要把简单的事情复杂化?我强烈建议在代码中寻找错误,而不是在 Android 中。
  • 我已经在代码中搜索了错误,但我无法找到它。看起来 StrictMode 错误仅在组合 https 和保持活动的情况下才会发生。我没有用于处理 ssl 证书的代码,也没有用于池连接的代码。如果我想看看有什么不同,我必须查看平台来源。
  • 1) 代码已经过时了。 2) 1年后真的有必要吗? 3) 我不记得了……
【解决方案2】:

试试下面的,


关闭流和连接后,将其设为 null。

is=null;os=null;connection=null;

【讨论】:

    猜你喜欢
    • 2013-07-30
    • 1970-01-01
    • 2012-10-08
    • 2012-12-24
    • 1970-01-01
    • 2016-11-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多