Rock with Android


网络请求

移动应用和Ajax很重的前端网站类似,都是用Api和后端打交道,很少有纯粹的单机版App,那种一般都是工具,算不上移动互联网的应用。

想访问网络,首先要在AndroidManifest.xml中加入访问网络的权限,另外注意:网络操作要异步进行,不能阻塞UI渲染线程!

        <uses-permission android:name="android.permission.INTERNET" />
        

安卓框架本身里面有两个用来做网络访问的常用库,感兴趣的同学可以看Android’s HTTP Clients。这里只说结论,对应安卓2.2之前来讲HttpClient要优于HttpURLConnection,而从2.3向后的版本,HttpURLConnection是比较推荐的选择。

HttpClient

推荐一个基于Apache的HttpClient的封装库,loopj/android-async-http,大家可以试用下。因为loopj的这个库是封装了异步操作的,如果需要自己做异步并且做更多的定制的话,还是要自己来做。不过这个库里提供的RequestParams和SimpleMultipartEntity这两个类很不错,支持处理普通的和multipart上传的form,比较轻便。我一般把用这两个拿出来和自己定制的HttpClient配合用。下面说下HttpClient需要注意的一些点:

  1. 开启GZIP的支持,这样能节省些网络传输的数据量:
        httpClient.addRequestInterceptor(new HttpRequestInterceptor() {
            public void process(HttpRequest request, HttpContext context) {
                if (!request.containsHeader(HEADER_ACCEPT_ENCODING)) {
                    request.addHeader(HEADER_ACCEPT_ENCODING, ENCODING_GZIP);
                }
            }
        });
    
        httpClient.addResponseInterceptor(new HttpResponseInterceptor() {
            public void process(HttpResponse response, HttpContext context) {
                final HttpEntity entity = response.getEntity();
                final Header encoding = entity.getContentEncoding();
                if (encoding != null) {
                    for (HeaderElement element : encoding.getElements()) {
                        if (element.getName().equalsIgnoreCase(ENCODING_GZIP)) {
                            response.setEntity(new InflatingEntity(response.getEntity()));
                            break;
                        }
                    }
                }
            }
        });
            
  2. 因为很多应用是用的OAuth2认证,并且使用https协议来传输数据的,那么如果你的后端网站的证书配置有问题,或者在某些被改动的ROM或机型上会报https的证书异常。可以使用信任所有证书的方法避免这一事情,但是前提是你知道后端网站值得信赖!
        public static void setTrustAllFactory(HttpClient httpClient){
            try{
                KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
                trustStore.load(null, null);
                SSLSocketFactory sf = new MySSLSocketFactory(trustStore);
                sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
                setSSLSocketFactory(httpClient, sf);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
            

    具体这个问题可以参考android-making-https-request,一个典型的MySSLSocketFactory如下。

    class MySSLSocketFactory extends SSLSocketFactory {
        SSLContext sslContext = SSLContext.getInstance("TLS");
    
        public MySSLSocketFactory(KeyStore truststore)
                throws NoSuchAlgorithmException, KeyManagementException,
                KeyStoreException, UnrecoverableKeyException {
            super(truststore);
    
            TrustManager tm = new X509TrustManager() {
                public void checkClientTrusted(X509Certificate[] chain,
                        String authType) throws CertificateException {
                }
    
                public void checkServerTrusted(X509Certificate[] chain,
                        String authType) throws CertificateException {
                }
    
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
            };
    
            sslContext.init(null, new TrustManager[] { tm }, null);
        }
    
        @Override
        public Socket createSocket(Socket socket, String host, int port,
                boolean autoClose) throws IOException, UnknownHostException {
            return sslContext.getSocketFactory().createSocket(socket, host, port,
                    autoClose);
        }
    
        @Override
        public Socket createSocket() throws IOException {
            return sslContext.getSocketFactory().createSocket();
        }
    
    }
            
  3. 因为移动应用很多时候是走3G/2G的移动网络,所以超时时间需要设长一点,比如20秒。

HttpURLConnection

推荐基于URLConnection的开源项目:kevinsawicki/http-request,具体使用可以直接看项目主页的介绍。

注意在2.2及以下使用时,需要做下面的处理:

        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.GINGERBREAD) {
            HttpRequest.keepAlive(false);
        }
        

具体见HttpURLConnection的“Avoiding Bugs In Earlier Releases”一节,以及issues#2939

目录上一章异步与AyncTask