1、概述
Goolge IO 2013推出了Volley框架,在这之前,我们在程序中需要和网络通信的时候,大体使用的东西莫过于AsyncTaskLoader,HttpURLConnection,AsyncTask,HTTPClient(Apache)等,Google I/O 2013上,Volley发布了。Volley是Android平台上的网络通信库,能使网络通信更快,更简单,更健壮。
这是Volley名称的由来: a burst or emission of many things or a large amount at once
在Google IO的演讲上,其配图是一幅发射火弓箭的图,有点类似流星。
2、内容简介
本文依然接着上文HTTPS访问的问题,因为Volley并不支持HTTPS访问,所以如果项目中需要用到Volley并且设计到HTTPS的访问问题就需要自己进行封装了,本文就是在此基础上产生的。文章主要对Volley进行了网络请求的封装和Volley使用HTTPS请求的封装。
3、框架使用示例
Volley的具体的使用请自行学习,本文是对其使用的封装。
首先我们来看看具体使用示例
使用前需要配置清单文件
<pre name="code" class="html"><application
android:name=".application.VolleyApplication"
...
</application>
-
普通http的get请求 StringRequest - HTTP/GET
private void stringRequestGetHttpExample(){ DataRequester.withHttp(this) .setUrl(HTTP) .setMethod(DataRequester.Method.GET) .setStringResponseListener(new DataRequester.StringResponseListener() { @Override public void onResponse(String response) { Toast.makeText(MainActivity.this, "HTTP/GET,StringRequest successfully.", Toast.LENGTH_SHORT).show(); tvResult.setText(response); } }) .requestString(); }
-
普通http的post请求 StringRequst HTTP/POST
private void stringRequestPostHttpExample(){ HashMap<String, String> body = new HashMap <String, String>() ; body.put( "name", "xxx" ); body.put( "age", "20" ); DataRequester.withHttp(this) .setUrl(HTTP) .setBody(body) .setMethod(DataRequester.Method.POST) .setStringResponseListener(new DataRequester.StringResponseListener() { @Override public void onResponse(String response) { Toast.makeText(MainActivity.this, "HTTP/POST,StringRequest successfully.", Toast.LENGTH_SHORT).show(); tvResult.setText(response); } } ) .requestString(); }
-
HTTPS默认证书访问的json数据的get请求 JsonRequst HTTP/POST
private void jsonRequestGetHttpsExample(){ DataRequester.withDefaultHttps(this) .setUrl(HTTPS) .setJsonResponseListener(new DataRequester.JsonResponseListener() { @Override public void onResponse(JSONObject response) { try { String s = response.getString("data"); tvResult.setText(s); Toast.makeText( MainActivity.this, "HTTPS/GET, JsonRequest successfully.", Toast.LENGTH_SHORT).show(); }catch (Exception e){ e.printStackTrace(); } } } ) .setResponseErrorListener(new DataRequester.ResponseErrorListener() { @Override public void onErrorResponse(VolleyError error) { tvResult.setText( error.getMessage() ); } }) .requestJson(); }
-
HTTPS默认证书访问的json数据的post请求 JsonRequst HTTP/POST
private void jsonRequestPostHttpsExample(){ JSONObject json = new JSONObject(); try{ json.put( "name", "xxx" ); json.put( "age", "20" ); }catch (Exception e){ e.printStackTrace(); } DataRequester.withDefaultHttps(this) .setUrl(HTTPS) .setBody(json) .setJsonResponseListener(new DataRequester.JsonResponseListener() { @Override public void onResponse(JSONObject response) { try { String data = response.getString("data"); tvResult.setText( data); Toast.makeText(MainActivity.this, "HTTPS/POST, JsonRequest successfully.", Toast.LENGTH_SHORT).show(); }catch (Exception e){ e.printStackTrace(); } } } ) .setResponseErrorListener(new DataRequester.ResponseErrorListener() { @Override public void onErrorResponse(VolleyError error) { tvResult.setText(error.getMessage()); } }) .requestJson(); }
-
自定义证书使用
DataRequester.withSelfCertifiedHttps(this) .setUrl(HTTPS) .setJsonResponseListener(new YouJsonRequestListener ()) .requestJson();
OK,具体的使用基本上就是上述方式了,下面正式开始介绍代码的实现
4、自定义Application
首先来看看这个自定义的Applicaion的做了那些事情,代码如下
public class VolleyApplication extends Application { private static VolleyApplication mInstance ; /** 创建http请求队列 */ private RequestQueue mRequestQueueWithHttp ; /** 创建自定义证书的Https请求队列 */ private RequestQueue mRequestQueueWithSelfCertifiedSsl ; /** 创建默认证书的Https请求队列 */ private RequestQueue mRequestQueueWithDefaultSsl ; @Override public void onCreate() { super.onCreate(); mInstance = this ; } /**通过单例模式获取对象*/ public static VolleyApplication getInstance(){ return mInstance ; } /** * 获取http请求队列 * @return */ public RequestQueue getRequestQueueWithHttp(){ if(mRequestQueueWithHttp == null){ //创建普通的request mRequestQueueWithHttp = Volley.newRequestQueue(getApplicationContext()); } return mRequestQueueWithHttp ; } /** * 获取默认证书https请求队列 * @return */ public RequestQueue getRequestQueueWithDefaultSsl(){ if(mRequestQueueWithDefaultSsl == null){ Network network = new BasicNetwork(new HurlStack()); Cache cache = new DiskBasedCache(getCacheDir(),1024 * 1024) ; mRequestQueueWithDefaultSsl = new RequestQueue(cache,network) ; mRequestQueueWithDefaultSsl.start(); SSLCertificateValidation.trustAllCertificate(); } return mRequestQueueWithDefaultSsl ; } /** * 获取自定义证书请求队列 * @return */ public RequestQueue getRequestQueueWithSelfCertifiedSsl(){ if(mRequestQueueWithSelfCertifiedSsl == null){ SSLSocketFactory sslSocketFactory = SelfSSLSocketFactory.getSSLSocketFactory(getApplicationContext()); Network network = new BasicNetwork(new HurlStack(null,sslSocketFactory)); Cache cache = new DiskBasedCache(getCacheDir(),1024 * 1024) ; mRequestQueueWithSelfCertifiedSsl = new RequestQueue(cache,network) ; mRequestQueueWithSelfCertifiedSsl.start(); HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { // 当URL的主机名和服务器的标识主机名不匹配默认返回true return true; } }); } return mRequestQueueWithSelfCertifiedSsl ; } }
从上面的代码中我们能够很容易的看出来具体的做了哪些事情,首先通过单例的形式提供了对象。通过getRequestQueueWithHttp()方法提供普通的http访问的请求队列。通过getRequestQueueWithDefaultSsl()提供了默认证书的请求队列,例如请求https://www.baidu.com的时候就可以使用该方法获取请求队列。当然如果我们需要自定义证书让客户端和服务端进行交互的话就需要通过getRequestQueueWithSelfCertifiedSsl()来获取请求队列了。各个方法的具体的实现参考代码即可。
5、证书配置
public class CertificateConfig { public static final String KEY_STORE_TYPE_BKS = "BKS"; public static final String keyStoreFileName = "client.bks"; public static final String keyStorePassword = "123456" ; public static final String trustStoreFileName = "server.cer"; public static final String trustStorePassword = "123456"; }
该类中主要是配置证书的一些信息,证书的具体生成方式参考HTTPS认证
6、信任所有证书类的实现SSLCertificateValidation
首先直接来看代码
public class SSLCertificateValidation { /** * 信任所有证书 */ public static void trustAllCertificate() { try { //设置TLS方式 SSLContext sslc = SSLContext.getInstance("TLS"); //new一个自定义的TrustManager数组,自定义类中不做任何实现 TrustManager[] trustManagers = {new NullX509TrustManager()}; sslc.init(null,trustManagers,null); HttpsURLConnection.setDefaultSSLSocketFactory(sslc.getSocketFactory()); HttpsURLConnection.setDefaultHostnameVerifier(new NullHostnameVerifier()); } catch (Exception e) { e.printStackTrace(); } } //自定义类实现X509TrustManager接口,但方法不做实现 private static class NullX509TrustManager implements X509TrustManager { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } } //自定义类实现HostnameVerifier接口,但方法verify直接返回true,默认信任所有 private static class NullHostnameVerifier implements HostnameVerifier { public boolean verify(String hostname, SSLSession session) { return true; } } }
该类没有什么好解释的,就是自定义一些类实现证书接口,但是不做任何的方法实现,默认就可以,因为需要信任所有的证书。当然如果需要自定义证书就要做实现了。
下面就来看看自定义证书所做的事情。
7、自定义SelfSSLSocketFactory工厂类
啥也不说了直接上代码
public class SelfSSLSocketFactory { /** * 获取SSLSocketFactory * @param context * @return */ public static SSLSocketFactory getSSLSocketFactory(Context context) { try { return setCertificates(context, context.getAssets().open(CertificateConfig.trustStoreFileName)) ; } catch (IOException e) { e.printStackTrace(); } return null ; } /** * 产生SSLSocketFactory * @param context * @param certificates * @return */ private static SSLSocketFactory setCertificates(Context context,InputStream... certificates){ try{ CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(null); int index = 0; for (InputStream certificate : certificates){ String certificateAlias = Integer.toString(index++); keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate)); try{ if (certificate != null) certificate.close(); } catch (IOException e){ e.printStackTrace() ; } } //取得SSL的SSLContext实例 SSLContext sslContext = SSLContext.getInstance("TLS"); TrustManagerFactory trustManagerFactory = TrustManagerFactory. getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(keyStore); //初始化keystore KeyStore clientKeyStore = KeyStore.getInstance(CertificateConfig.KEY_STORE_TYPE_BKS); clientKeyStore.load(context.getAssets().open(CertificateConfig.keyStoreFileName), CertificateConfig.keyStorePassword.toCharArray()); KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); keyManagerFactory.init(clientKeyStore, CertificateConfig.trustStorePassword.toCharArray()); sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom()); return sslContext.getSocketFactory() ; } catch (Exception e){ e.printStackTrace(); } return null ; } }
在代码中我们可以看到该类的左右就是提供SSLSocketFactory对象,主要方法就是setCertificates()方法,该方法需要传人Context对象和服务端证书的流对象,在其中将服务端证书和客户端证书库进行读取和设置,设置完成后返回生成的SSLSocketFactory对象。返回的对象在什么地方进行使用呢?还记得我们的自定义Application类吗,自定义的的Application类中提供了获取自定义证书的请求队列,在其中需要该类提供的SSLSocketFactory对象。
至此辅助类都介绍完了,下面来看看最重要的一个类
8、网络请求类DataRequester
该类内容较多,所有的请求都是通过该类完成配置和将请求加入到请求队列中的。下面就看看该的代码吧,虽然代码较多,但是逻辑很简单
public class DataRequester { private static final String TAG = "DataRequester"; /** * 创建请求类型枚举 */ private enum Type{ HTTP, HTTPS_DEFALT, HTTPS_SELF_CERTIFIED; } public enum Method { GET, POST } /** String数据请求成功返回接口 */ public interface StringResponseListener extends Response.Listener<String>{} /** Json数据请求成功返回接口 */ public interface JsonResponseListener extends Response.Listener<JSONObject>{} /** JsonArray数据请求成功返回接口 */ public interface JsonArrayResponseListener extends Response.Listener<JSONArray>{} /** 请求失败返回接口 */ public interface ResponseErrorListener extends Response.ErrorListener{} private StringResponseListener mStringResponseListener ; private JsonResponseListener mJsonResponseListener ; private JsonArrayResponseListener mJsonArrayResponseListener ; private ResponseErrorListener mResponseErrorListener ; /** 请求队列 从application中获取*/ private RequestQueue mRequestQueue ; /**请求地址*/ private String url ; /**请求方式*/ private Method method ; private JSONObject jsonBody; private Map<String,String> mapBody; private Map<String,String> headers; private String strBody; private DataRequester(Context context,Type type){ if(type == Type.HTTP){ mRequestQueue = VolleyApplication.getInstance().getRequestQueueWithHttp() ; } if(type == Type.HTTPS_DEFALT){ mRequestQueue = VolleyApplication.getInstance().getRequestQueueWithDefaultSsl() ; } if(type == Type.HTTPS_SELF_CERTIFIED){ mRequestQueue = VolleyApplication.getInstance().getRequestQueueWithSelfCertifiedSsl() ; } } /** * 普通http请求 * @param context * @return */ public static DataRequester withHttp(Context context){ return new DataRequester(context,Type.HTTP) ; } /** * 默认证书HTTPS请求 * @param context * @return */ public static DataRequester withDefaultHttps(Context context){ return new DataRequester(context,Type.HTTPS_DEFALT) ; } /** * 自定义证书HTTPS请求 * @param context * @return */ public static DataRequester withSelfCertifiedHttps(Context context){ return new DataRequester(context,Type.HTTPS_SELF_CERTIFIED) ; } /** * 设置请求url * @param url * @return */ public DataRequester setUrl(String url){ this.url = url ; return this ; } /** * 设置请求方式 * @param method * @return */ public DataRequester setMethod(Method method){ this.method = method ; return this ; } /** * 设置JsonObject请求体 * @param body * @return */ public DataRequester setBody(JSONObject body) { this.jsonBody = body; return this; } /** * 设置String请求体 * @param body * @return */ public DataRequester setBody(String body) { this.strBody = body; return this; } /** * 设置Map请求体 * @param stringRequestParam * @return */ public DataRequester setBody (Map<String,String> stringRequestParam){ this.mapBody = stringRequestParam; return this; } /** * 设置String请求成功监听 * @param mStringResponseListener * @return */ public DataRequester setStringResponseListener(StringResponseListener mStringResponseListener) { this.mStringResponseListener = mStringResponseListener; return this; } /** * 设置Json请求成功返回监听 * @param mJsonResponseListener * @return */ public DataRequester setJsonResponseListener(JsonResponseListener mJsonResponseListener) { this.mJsonResponseListener = mJsonResponseListener; return this; } /** * 设置JsonArray请求成功监听 * @param mJsonArrayResponseListener * @return */ public DataRequester setJsonArrayResponseListener (JsonArrayResponseListener mJsonArrayResponseListener){ this.mJsonArrayResponseListener = mJsonArrayResponseListener; return this; } /** * 设置请求出错监听 * @param mResponseErrorListener * @return */ public DataRequester setResponseErrorListener(ResponseErrorListener mResponseErrorListener) { this.mResponseErrorListener = mResponseErrorListener; return this; } /** * String请求 */ public void requestString(){ StringRequest request = null ; if(Method.GET == method){ request = new StringRequest( Request.Method.GET, url, mStringResponseListener, mResponseErrorListener); } if(Method.POST == method){ request = new StringRequest( Request.Method.POST, url, mStringResponseListener, mResponseErrorListener){ @Override protected Map<String, String> getParams() throws AuthFailureError { return mapBody ; } @Override public Map<String, String> getHeaders() throws AuthFailureError { if (headers != null){ return headers; }else { return super.getHeaders(); } } }; } mRequestQueue.add(request) ; } /** * Json请求 */ public void requestJson() { JsonObjectRequest request = new JsonObjectRequest( url, jsonBody, mJsonResponseListener, mResponseErrorListener ); if (jsonBody != null) { LogUtils.d(TAG, request.getBody().toString()); } mRequestQueue.add( request ); } /** * JsonArray请求 */ public void requestJsonArray() { JsonArrayRequest request = new JsonArrayRequest (url, mJsonArrayResponseListener, mResponseErrorListener ); mRequestQueue.add( request ); } /** 图片显示的控件 */ private ImageView mImageView ; /** 下载图片的缓存 */ private ImageLoader.ImageCache mCache = null ; /** 下载过程过程中显示的图片 */ private int mDefaultImage ; /** 下载失败后显示的图片 */ private int mFailImage ; /** 图片的宽度 */ private int maxWidth = 0 ; /** 图片的高度 */ private int maxHeight = 0; /** * 设置图片显示的控件 * @param iv * @return */ public DataRequester setImageView(ImageView iv){ this.mImageView = iv ; return this ; } /** * 设置下载图片过程中显示的图片 * @param defaultImage * @return */ public DataRequester setDafaultImage(int defaultImage){ this.mDefaultImage = defaultImage ; return this ; } /** * 设置下载失败时显示的图片 * @param failImage * @return */ public DataRequester setFailImage(int failImage){ this.mFailImage = failImage ; return this ; } /** * 设置图片的缓存 * @param cache * @return */ public DataRequester setImageCache(ImageLoader.ImageCache cache){ this.mCache = cache ; return this ; } /** * 设置下载图片的最大宽度,默认为0,可以不设置 * @param width * @return */ public DataRequester setImageWidth(int width){ this.maxWidth = width ; return this ; } /** * 设置下载图片的最大高度,默认为0,可以不设置 * @param height * @return */ public DataRequester setImageHeight(int height){ this.maxHeight = height; return this ; } /** * 请求图片 */ public void requestImage(){ if(mCache == null){ mCache = new ImageLoader.ImageCache() { @Override public Bitmap getBitmap(String url) { return null; } @Override public void putBitmap(String url, Bitmap bitmap) { } } ; } ImageLoader imageLoader = new ImageLoader(mRequestQueue, mCache); ImageLoader.ImageListener listener = ImageLoader.getImageListener(mImageView,mDefaultImage, mFailImage); imageLoader.get(url,listener, 200, 200); } }
首先看看该类,在类的开始定义了两个枚举Type和Method,Type定义的是连接服务器的方式,如HTTP,默认HTTPS,自定义HTTPS。Method定义的是请求方式POST和GET方式两种。因为这两种是最常用的两种,所以暂时只定义了这两种请求方式,如果需要其他的请求方式可以再添加,这里就不做额外的添加了,有需要的可以自己尝试添加即可。
在类中还定义了一些请求回调的接口
/** String数据请求成功返回接口 */
public interface StringResponseListener extends Response.Listener<String>{}
/** Json数据请求成功返回接口 */
public interface JsonResponseListener extends Response.Listener<JSONObject>{}
/** JsonArray数据请求成功返回接口 */
public interface JsonArrayResponseListener extends Response.Listener<JSONArray>{}
/** 请求失败返回接口 */
public interface ResponseErrorListener extends Response.ErrorListener{}
如果服务器返回的是String类型的内容则可以通过StringResponseListener获取到服务器返回的内容
如果服务器返回的是Json字符串类型的内容则可以通过JsonResponseListener获取到服务器返回的内容
如果服务器返回的是JsonArray类型的内容则可以通过JsonArrayResponseListener获取到服务器返回的内容
如果请求出错或失败则可以通过ResponseErrorListener获取请求失败的原因。
类中还有对应的设置请求方式和URL还有设置参数等方法,设置完成后需要对用的request方法,在request方法中调用Vollley的方法设置成功回调和将请求加入到对应的请求队列方式中。该请求队列通过自定义的Application获取。
9、总结
该封装还是很多不足的,比如在设置证书的时候我采用的是之前的博客中生成证书和设置证书的方式做的,如果有需要特殊的则需要自己完成SelfSSLSocketFactory类中的setCertificates()。