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.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 创建。
共有 0 条评论