查看Volley的源码Volley.java可以看到
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
String userAgent = "volley/0";
try {
String packageName = context.getPackageName();
PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException e) {
}
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
// Prior to Gingerbread, HttpUrlConnection was unreliable.
// See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
Network network = new BasicNetwork(stack);
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
return queue;
}
我们可以参考HurlStack,实现自己的HttpsStack,这样就不用修改Volley源码了
public class HttpsStack implements HttpStack{
private static final String HEADER_CONTENT_TYPE = "Content-Type";
/**
* An interface for transforming URLs before use.
*/
public interface UrlRewriter {
/**
* Returns a URL to use instead of the provided one, or null to indicate
* this URL should not be used at all.
*/
public String rewriteUrl(String originalUrl);
}
private final UrlRewriter mUrlRewriter;
private final SSLSocketFactory mSslSocketFactory;
public HttpsStack() {
this(null);
}
/**
* @param urlRewriter Rewriter to use for request URLs
*/
public HttpsStack(UrlRewriter urlRewriter) {
this(urlRewriter, null);
}
/**
* @param urlRewriter Rewriter to use for request URLs
* @param sslSocketFactory SSL factory to use for HTTPS connections
*/
public HttpsStack(UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory) {
mUrlRewriter = urlRewriter;
mSslSocketFactory = sslSocketFactory;
}
@Override
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
String url = request.getUrl();
HashMap<String, String> map = new HashMap<String, String>();
map.putAll(request.getHeaders());
map.putAll(additionalHeaders);
if (mUrlRewriter != null) {
String rewritten = mUrlRewriter.rewriteUrl(url);
if (rewritten == null) {
throw new IOException("URL blocked by rewriter: " + url);
}
url = rewritten;
}
URL parsedUrl = new URL(url);
HttpURLConnection connection = openConnection(parsedUrl, request);
for (String headerName : map.keySet()) {
connection.addRequestProperty(headerName, map.get(headerName));
}
setConnectionParametersForRequest(connection, request);
// Initialize HttpResponse with data from the HttpURLConnection.
ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
int responseCode = connection.getResponseCode();
if (responseCode == -1) {
// -1 is returned by getResponseCode() if the response code could not be retrieved.
// Signal to the caller that something was wrong with the connection.
throw new IOException("Could not retrieve response code from HttpUrlConnection.");
}
StatusLine responseStatus = new BasicStatusLine(protocolVersion,
connection.getResponseCode(), connection.getResponseMessage());
BasicHttpResponse response = new BasicHttpResponse(responseStatus);
if (hasResponseBody(request.getMethod(), responseStatus.getStatusCode())) {
response.setEntity(entityFromConnection(connection));
}
for (Map.Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
if (header.getKey() != null) {
Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
response.addHeader(h);
}
}
return response;
}
/**
* Checks if a response message contains a body.
*
* @param requestMethod request method
* @param responseCode response status code
* @return whether the response has a body
* @see <a href="https://tools.ietf.org/html/rfc7230#section-3.3">RFC 7230 section 3.3</a>
*/
private static boolean hasResponseBody(int requestMethod, int responseCode) {
return requestMethod != Request.Method.HEAD
&& !(HttpStatus.SC_CONTINUE <= responseCode && responseCode < HttpStatus.SC_OK)
&& responseCode != HttpStatus.SC_NO_CONTENT
&& responseCode != HttpStatus.SC_NOT_MODIFIED;
}
/**
* Initializes an {@link HttpEntity} from the given {@link HttpURLConnection}.
*
* @param connection
* @return an HttpEntity populated with data from <code>connection</code>.
*/
private static HttpEntity entityFromConnection(HttpURLConnection connection) {
BasicHttpEntity entity = new BasicHttpEntity();
InputStream inputStream;
try {
inputStream = connection.getInputStream();
} catch (IOException ioe) {
inputStream = connection.getErrorStream();
}
entity.setContent(inputStream);
entity.setContentLength(connection.getContentLength());
entity.setContentEncoding(connection.getContentEncoding());
entity.setContentType(connection.getContentType());
return entity;
}
/**
* Create an {@link HttpURLConnection} for the specified {@code url}.
*/
protected HttpURLConnection createConnection(URL url) throws IOException {
if (url.toString().contains("https")) {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier(){
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
}
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// Workaround for the M release HttpURLConnection not observing the
// HttpURLConnection.setFollowRedirects() property.
// https://code.google.com/p/android/issues/detail?id=194495
connection.setInstanceFollowRedirects(HttpURLConnection.getFollowRedirects());
return connection;
}
/**
* Opens an {@link HttpURLConnection} with parameters.
*
* @param url
* @return an open connection
* @throws IOException
*/
private HttpURLConnection openConnection(URL url, Request<?> request) throws IOException {
HttpURLConnection connection = createConnection(url);
int timeoutMs = request.getTimeoutMs();
connection.setConnectTimeout(timeoutMs);
connection.setReadTimeout(timeoutMs);
connection.setUseCaches(false);
connection.setDoInput(true);
// use caller-provided custom SslSocketFactory, if any, for HTTPS
if ("https".equals(url.getProtocol()) && mSslSocketFactory != null) {
((HttpsURLConnection) connection).setSSLSocketFactory(mSslSocketFactory);
}
return connection;
}
@SuppressWarnings("deprecation")
/* package */ static void setConnectionParametersForRequest(HttpURLConnection connection,
Request<?> request) throws IOException, AuthFailureError {
switch (request.getMethod()) {
case Request.Method.DEPRECATED_GET_OR_POST:
// This is the deprecated way that needs to be handled for backwards compatibility.
// If the request's post body is null, then the assumption is that the request is
// GET. Otherwise, it is assumed that the request is a POST.
byte[] postBody = request.getPostBody();
if (postBody != null) {
// Prepare output. There is no need to set Content-Length explicitly,
// since this is handled by HttpURLConnection using the size of the prepared
// output stream.
connection.setDoOutput(true);
connection.setRequestMethod("POST");
connection.addRequestProperty(HEADER_CONTENT_TYPE,
request.getPostBodyContentType());
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.write(postBody);
out.close();
}
break;
case Request.Method.GET:
// Not necessary to set the request method because connection defaults to GET but
// being explicit here.
connection.setRequestMethod("GET");
break;
case Request.Method.DELETE:
connection.setRequestMethod("DELETE");
break;
case Request.Method.POST:
connection.setRequestMethod("POST");
addBodyIfExists(connection, request);
break;
case Request.Method.PUT:
connection.setRequestMethod("PUT");
addBodyIfExists(connection, request);
break;
case Request.Method.HEAD:
connection.setRequestMethod("HEAD");
break;
case Request.Method.OPTIONS:
connection.setRequestMethod("OPTIONS");
break;
case Request.Method.TRACE:
connection.setRequestMethod("TRACE");
break;
case Request.Method.PATCH:
connection.setRequestMethod("PATCH");
addBodyIfExists(connection, request);
break;
default:
throw new IllegalStateException("Unknown method type.");
}
}
private static void addBodyIfExists(HttpURLConnection connection, Request<?> request)
throws IOException, AuthFailureError {
byte[] body = request.getBody();
if (body != null) {
connection.setDoOutput(true);
connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getBodyContentType());
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.write(body);
out.close();
}
}
}
生成SSLSocketFactory的方法如下:
public class HTTPSManager {
public static SSLSocketFactory buildSSLSocketFactory(Context context,
InputStream inputStream) {
KeyStore keyStore = null;
try {
keyStore = buildKeyStore(context, inputStream);
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = null;
try {
tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
}
SSLContext sslContext = null;
try {
sslContext = SSLContext.getInstance("TLS");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
try {
sslContext.init(null, tmf.getTrustManagers(), null);
} catch (KeyManagementException e) {
e.printStackTrace();
}
return sslContext.getSocketFactory();
}
private static KeyStore buildKeyStore(Context context, InputStream inputStream)
throws KeyStoreException, CertificateException,
NoSuchAlgorithmException, IOException {
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
Certificate cert = readCert(context, inputStream);
keyStore.setCertificateEntry("ca", cert);
return keyStore;
}
private static Certificate readCert(Context context, InputStream inputStream) {
Certificate ca = null;
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
ca = cf.generateCertificate(inputStream);
Log.d("VolleyActivity","ca=" + ((X509Certificate) ca).getSubjectDN());
} catch (CertificateException e) {
e.printStackTrace();
}
return ca;
}
}
调用示例:
public void initSSL() {
try {
InputStream inputStream = getAssets().open("server.cer");
SSLSocketFactory sslSocketFactory = HTTPSManager.buildSSLSocketFactory(VolleyActivity.this, inputStream);
HttpsStack httpsStack = new HttpsStack(null,sslSocketFactory);
RequestQueue requestQueue = Volley.newRequestQueue(VolleyActivity.this, httpsStack);
StringRequest stringRequest = new StringRequest(Request.Method.POST,URL,
new Response.Listener<String>() {
@Override public void onResponse(String s) {
Log.d(TAG, s);
}
}, new Response.ErrorListener() {
@Override public void onErrorResponse(VolleyError volleyError) {
Log.d(TAG, volleyError.getMessage());
}
});
requestQueue.add(stringRequest);
} catch (IOException e) {
e.printStackTrace();
}
}