Some Android Issues

Some Android Issues

最近写了些代码,遇到一些Android的坑…真的坑

1.Android HttpURLConnection response code 204/200
Android中HttpURLConnection 的response code好像并不可靠,譬如服务器明明返回了HTTP 204,然而你调用urlConnection.getResponseCode得到的却是 HTTP 200…(如果用Java SE运行得到的是正常的204)

查看了一下Android源码中isCaptivePortal函数(/frameworks/base/services/core/java/com/android/server/connectivity/NetworkMonitor.java),其中处理到了HTTP response code,

580    /**
581     * Do a URL fetch on a known server to see if we get the data we expect.
582     * Returns HTTP response code.
583     */
584    private int isCaptivePortal() {
585        if (!mIsCaptivePortalCheckEnabled) return 204;
586
587        HttpURLConnection urlConnection = null;
588        int httpResponseCode = 599;
589        try {
590            URL url = new URL("http", mServer, "/generate_204");
591            if (DBG) {
592                log("Checking " + url.toString() + " on " +
593                        mNetworkAgentInfo.networkInfo.getExtraInfo());
594            }
595            urlConnection = (HttpURLConnection) mNetworkAgentInfo.network.openConnection(url);
596            urlConnection.setInstanceFollowRedirects(false);
597            urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
598            urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
599            urlConnection.setUseCaches(false);
600
601            // Time how long it takes to get a response to our request
602            long requestTimestamp = SystemClock.elapsedRealtime();
603
604            urlConnection.getInputStream();
605
606            // Time how long it takes to get a response to our request
607            long responseTimestamp = SystemClock.elapsedRealtime();
608
609            httpResponseCode = urlConnection.getResponseCode();
610            if (DBG) {
611                log("isCaptivePortal: ret=" + httpResponseCode +
612                        " headers=" + urlConnection.getHeaderFields());
613            }
614            // NOTE: We may want to consider an "HTTP/1.0 204" response to be a captive
615            // portal.  The only example of this seen so far was a captive portal.  For
616            // the time being go with prior behavior of assuming it's not a captive
617            // portal.  If it is considered a captive portal, a different sign-in URL
618            // is needed (i.e. can't browse a 204).  This could be the result of an HTTP
619            // proxy server.
620
621            // Consider 200 response with "Content-length=0" to not be a captive portal.
622            // There's no point in considering this a captive portal as the user cannot
623            // sign-in to an empty page.  Probably the result of a broken transparent proxy.
624            // See http://b/9972012.
625            if (httpResponseCode == 200 && urlConnection.getContentLength() == 0) {
626                if (DBG) log("Empty 200 response interpreted as 204 response.");
627                httpResponseCode = 204;
628            }
629
630            sendNetworkConditionsBroadcast(true /* response received */, httpResponseCode == 204,
631                    requestTimestamp, responseTimestamp);
632        } catch (IOException e) {
633            if (DBG) log("Probably not a portal: exception " + e);
634            if (httpResponseCode == 599) {
635                // TODO: Ping gateway and DNS server and log results.
636            }
637        } finally {
638            if (urlConnection != null) {
639                urlConnection.disconnect();
640            }
641        }
642        return httpResponseCode;
643    }

 
从第625行可以看出,Android源码中判断当response code=200且content-length为0时,手动将response code置为204。嗯,我也这样做,然后该问题就解决了。

除此之外,Android中HttpURLConnection总之和Java SE中运行不大一致,譬如如果你是POST请求,却设置了urlConnection.setDoOutput(false);将会影响响应头。

但是,至于为什么出现这些情况,暂时并没细究,改日有空再看看Android相关源码。

2.Android 21+ Network bind with using vpn cause operating not permitted exception

Android 21+因为开始支持网络多连接,因此在android.net.ConnectivityManager中新增了绑定网络到当前线程或者绑定到具体socket的方法,
API21 connectivityManager.setProcessDefaultNetwork(network)
API23 connectivityManager.bindProcessToNetwork(network)然而使用时,
对于某type为ConnectivityManager.TYPE_WIFI的network调用以上方法绑定后,手机再连接VPN时,网络请求将会出现java.net.SocketException “operating not permitted”翻阅Android API文档发现,Android 21中新增了一种网络类型VPN,想必与此有关,继续翻阅Android源码,发现了Android在处理网络类型时,把vpn作为优先处理的网络类型,一旦连接了vpn,系统将会将其作为当前活跃网络。此时类型为wifi的network对象将会被限制socket创建。

发表评论

电子邮件地址不会被公开。