最近在搞微信支付的SDK开发,微信这个开放平台真是说不清,怎么感觉比地图和语音的SDK差的有点多,问题一大推,demo中的网络请求用的是apache的httpClient这个类来实现https请求,并且做了keystore验证,想到以前用的都是jdk自带的HttpURLConnection这个类来做网络请求,并且Android studio上大都用OkHttp了,试了一下,OkHttp确实简单又好用,而且也支持get,post带参数,Json,字节流,文件等,还添加了表单,分块等方式,在对OkHttp进一步封装后网络请求的使用甚至一行代码就可以解决,大大优化了代码效率,比如github上这个OkHttp开源框架https://github.com/hongyangAndroid/okhttp-utils,当然我们还是要通过API对OkHttp的细节掌握了。
我们以前一般使用的HttpUrlConnection和HttpClient这两个类来封装我们的网络工具类,实现的细节就不多说了,大家都很常用,比如下面的代码实现带参数的post方式的https请求。
public static String post(String httpsUrl, String params) {
String result = null;
try {
URL url = new URL(httpsUrl); // 解析httpsUrl,生成url对象
SSLContext sslctxt = SSLContext.getInstance("TLS"); // 为请求通TLS协议,生成SSLContext对象
// 初始化SSLContext
sslctxt.init(null, new TrustManager[] { new MyX509TrustManager() },
new java.security.SecureRandom());
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(sslctxt.getSocketFactory());
conn.setHostnameVerifier(new MyHostnameVerifier());
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.setUseCaches(false);
conn.connect();
conn.getOutputStream().write(params.getBytes());
conn.getOutputStream().flush();
conn.getOutputStream().close();
int respCode = conn.getResponseCode();
Log.d(TAG, "ResponseCode=" + respCode);
if(respCode == 200) {
InputStream input = conn.getInputStream();
result = toString(input);
input.close();
}
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (KeyManagementException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return result;
}
下面是微信支付demo的网络工具类,截取一段代码,getNewHttpClient里面做了keystore加载和证书的参数设置,唯一需要注意的是jar包最好用最新的,不然会报错误。
public static byte[] httpGet(final String url) {
if (url == null || url.length() == 0) {
Log.d(TAG, "httpGet, url is null");
return null;
}
HttpClient httpClient = getNewHttpClient();
Log.d(TAG, "httpClient;" + httpClient);
HttpGet httpGet = new HttpGet(url);
try {
HttpResponse resp = httpClient.execute(httpGet);
Log.d(TAG, "resp;" + resp);
if (resp.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
Log.d(TAG, "httpGet fail, status code = " + resp.getStatusLine().getStatusCode());
return null;
}
return EntityUtils.toByteArray(resp.getEntity());
} catch (Exception e) {
Log.d(TAG, "httpGet exception, e = " + e.getMessage());
e.printStackTrace();
return null;
}
}
这里再说一下http和https的区别,后者其实就是在前者的基础上加了一层安全套接字SSL,https服务器所使用的根证书是自签名的。如果设备的信任证书列表中不包含此签名机构,就会连接失败。出现这样的问题,一般有两种解决方案:1一种是让客户端信任所有的服务器证书,这种方法安全性则差一些,但实现相对简单。另一种是在发起https连接之前将服务器证书加到httpclient的信任证书列表中,这个相对来说比较复杂一些,但容易出错,第一种使用方式就是最上面的这两行代码,实现也比较简单:
SSLContext sslctxt = SSLContext.getInstance("TLS"); // 为请求通TLS协议,生成SSLContext对象
sslctxt.init(null, new TrustManager[] { new MyX509TrustManager() },new java.security.SecureRandom());// 初始化SSLContext
第二种方式就是微信支付Demo中的,在发起https请求的时候把keystore签名作为证书并在服务器端验证,具体代码:
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
Log.d(TAG, "trustStore: " + new String(trustStore.toString()));
SSLSocketFactory sf = new SSLSocketFactoryEx(trustStore);
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
.....
public SSLSocketFactoryEx(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
super(truststore);
TrustManager tm = new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws java.security.cert.CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws java.security.cert.CertificateException {
}
};
sslContext.init(null, new TrustManager[] { tm }, null);
}
下面主要说OkHttp的使用,首先要支持Android2.3和jdk1.7以上,官方文档https://github.com/square/okhttp/wiki/Recipes,其实官方文档已经说的很详细了,我们照着来写例子就行了,不得不说这种文档确实是干活,唯一就是英文,不过既然选择了这个行业,这点困难还是要克服的,慢慢积累吧,总的来说OkHttp实现了几乎实现了和HttpURLConnection一样的API ,我们参照上面的两种例子进行OkHttp的封装,看代码确实简洁了一些,要注意最新的OkHttp采用FormBody来实现带参数的Post请求,原来的用的是
FormEncodingBuilder这个类,总不敢接起来就三步,第一,new一个Builder来设置证书,同时实例化OkHttpClient,第二,FormBody设置参数,Request完成请求,最后一步OKHttpClient对象返回response,判断结果输出,当然这只是POST方式提交键值对,还有POST提交文件、响应头、流、表单,还有OkHttpGet的get方式,仔细参考API文档,多写一些测试代码相信大家也会掌握,
public static String post(String httpsUrl, String params) {
String result = null;
SSLContext sslctxt;
try {
// 为请求通TLS协议,生成SSLContext对象
// 初始化SSLContext
sslctxt = SSLContext.getInstance("TLS");
sslctxt.init(null, new TrustManager[] { new MyX509TrustManager() },new java.security.SecureRandom());
OkHttpClient.Builder builder = new Builder();
builder.sslSocketFactory(sslctxt.getSocketFactory());
builder.hostnameVerifier(new MyHostnameVerifier());
OkHttpClient client = new OkHttpClient();
FormBody formBody = new FormBody.Builder()
.add("plat", params)
.build();
Request request = new Request.Builder().url(httpsUrl).post(formBody).build();
Response response = client.newCall(request).execute();
if(response.isSuccessful()) {
return response.body().toString();
} else {
throw new IOException("Unexpected code " + response);
}
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (KeyManagementException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
说了这么多,谷歌为什么推出OkHttp,还是有原因的,OkHttp 处理了很多网络疑难杂症:会从很多常用的连接问题中自动恢复。如果您的服务器配置了多个IP地址,当第一个IP连接失败的时候,OkHttp会自动尝试下一个IP。OkHttp还处理了代理服务器问题和SSL握手失败问题。相信大家以前在用HttpUrlConnection和HttpClient的时候也遇到了很多问题,因此为了BUG少一点,还是尽快使用OkHttp,毕竟在Android studio上使用还是很方便的,最后推荐一个封装OkHttp的jar包,前面也提到了
https://github.com/hongyangAndroid/okhttp-utils,说不定用完这个后你自己也可以写一个适合项目的网络请求模块,也可以锻炼一下封装代码的能力,欢迎交流。
本文参考http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0106/2275.html。