简介:我们发现目前android网络开发框架越来越多,其实任何一个网络框架都能满足我们的应用开发需求,但是到底哪一个更好,接下来我们从源码角度来分析,到底哪一个更适合我们。
比较:首先我们通过以下几个方面的比较来分析一下(connect实现,线程池管理,可扩展性等)
第一方面:首先是connect的实现
volley | okhttp | Retrofit | AsyncHttp | XUtils |
HttpURLConnectionImpl HttpsURLConnectionImpl | HttpURLConnectionImpl HttpsURLConnectionImpl | HttpURLConnectionImpl HttpsURLConnectionImpl | DefaultHttpClient(httpclient) | HttpURLConnectionImpl HttpsURLConnectionImpl |
可以看到除了AsyncHttp以外,其他几种框架的connect实现都是相同的,而且可以确切的说用的都是okhttp的实现,
到这里也许有人会提出疑问,命名各个框架都是有自己的connect,为什么都用的是volley的呢?
下面从源码角度来分析一下。
以volley为例分析,其他都是相同的,
/**
* Create an {@link HttpURLConnection} for the specified {@code url}.
*/
protected HttpURLConnection createConnection(URL url) throws IOException {
return (HttpURLConnection) url.openConnection();
}
可以看到都是通过URL的openConnection打开的连接,
到URL.java中看一下:
public URLConnection openConnection(Proxy proxy) throws IOException {
if (proxy == null) {
throw new IllegalArgumentException("proxy == null");
}
return streamHandler.openConnection(this, proxy);
}
下面只要知道streamHandler是谁可以了,
void setupStreamHandler() {
.... //忽略掉无用代码
// Fall back to a built-in stream handler if the user didn't supply one
if (protocol.equals("file")) {
streamHandler = new FileHandler();
} else if (protocol.equals("ftp")) {
streamHandler = new FtpHandler();
} else if (protocol.equals("http")) {
try {
String name = "com.android.okhttp.HttpHandler"; //可以看到这里就会实例化okhttp内部的类,接下来就是按照okhttp内部的实现
streamHandler = (URLStreamHandler) Class.forName(name).newInstance();
} catch (Exception e) {
throw new AssertionError(e);
}
} else if (protocol.equals("https")) {
try {
String name = "com.android.okhttp.HttpsHandler";
streamHandler = (URLStreamHandler) Class.forName(name).newInstance();
} catch (Exception e) {
throw new AssertionError(e);
}
} else if (protocol.equals("jar")) {
streamHandler = new JarHandler();
}
if (streamHandler != null) {
streamHandlers.put(protocol, streamHandler);
}
}
从上面可以看出来,无论任何框架,只要调用了Url.connect(),在系统源码中自动会使用okhttp的connection去处理
这里面需要特殊提出来Retrofix,他与其他的有所不同,是完全使用okhttp实现的,
/**
* The factory used to create {@linkplain okhttp3.Call OkHttp calls} for sending a HTTP requests.
* Typically an instance of {@link OkHttpClient}.
*/
public okhttp3.Call.Factory callFactory() {
return callFactory;
}
下面说一下特殊的AnysnHttp,一款老大哥级的网络框架,也是我使用的第一个网络框架,他的底层实现以来httpclient,这个不知道具体什么原因,已经被android废弃了,所以我也不再过多介绍了,简单的列一下代码
AsyncHttpRequest.java
private void makeRequest() throws IOException {
if (isCancelled()) {
return;
}
// Fixes #115
if (request.getURI().getScheme() == null) {
// subclass of IOException so processed in the caller
throw new MalformedURLException("No valid URI scheme was provided");
}
if (responseHandler instanceof RangeFileAsyncHttpResponseHandler) {
((RangeFileAsyncHttpResponseHandler) responseHandler).updateRequestHeaders(request);
}
HttpResponse response = client.execute(request, context);
if (isCancelled()) {
return;
}
// Carry out pre-processing for this response.
responseHandler.onPreProcessResponse(responseHandler, response);
if (isCancelled()) {
return;
}
// The response is ready, handle it.
responseHandler.sendResponseMessage(response);
if (isCancelled()) {
return;
}
// Carry out post-processing for this response.
responseHandler.onPostProcessResponse(responseHandler, response);
}
通过上面的比较,真正连接工作的地方实际上是相同的,所以性能相差是不大的,但是由于其他调用的是okhttp的类,而且是反射等方式,所以okhttp理论上应该是优势最大的,但经过笔者测试,几乎没有差别。
第二方面 :下面比较各个内部连接池的实现。
首先OkHttp
/external/okhttp/okhttp/src/main/java/com/squareup/okhttp/Dispatcher.java
public synchronized ExecutorService getExecutorService() { if (executorService == null) { executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false)); } return executorService; }
可以看到是一个无限大的core_size=0的线程池来管理所有的网络请求
再看volley
RequestQueue.java
mDispatchers = new NetworkDispatcher[threadPoolSize];
public class NetworkDispatcher extends Thread
可以看到volley简单粗暴的就是new出来几个线程,
再来看xutils
HttpTask.java
private static final PriorityExecutor HTTP_EXECUTOR = new PriorityExecutor(5, true);
public PriorityExecutor(int poolSize, boolean fifo) {
BlockingQueue<Runnable> mPoolWorkQueue =
new PriorityBlockingQueue<Runnable>(MAXIMUM_POOL_SIZE, fifo ? FIFO_CMP : FILO_CMP);
mThreadPoolExecutor = new ThreadPoolExecutor(
poolSize,
MAXIMUM_POOL_SIZE,
KEEP_ALIVE,
TimeUnit.SECONDS,
mPoolWorkQueue,
sThreadFactory);
}
可以看到仍然使用的是线程池,只是core_max是5,就是说会始终维持线程,
再来看AnysnHttp
在AsyncHttpClient.java 中
protected ExecutorService getDefaultThreadPool() {
return Executors.newCachedThreadPool();
}
可以看到使用的是cache的线程池,来进行管理的。可以理解为与okhttp线程池管理方式相同,
最后retrofit,因为retrofit底层完全用的是okhttp,所以他和okhttp使用的线程管理方式也是一样的,不再介绍。
最后总结一下上面所说的三种线程池管理方式。
其实很难说哪个好哪个不好,但是volley的方式过于暴力,
1.网络请求频率比较低,仍然有多个线程占用,这是一种浪费,
2.当大量的网络请求到来时,线程也没有办法增加,造成请求阻塞,但是他有个好处就是不会因为大量网络请求到来的时候,引起oom
对于使用线程池的okhttp和xutils,各有各自的优势,而且都是允许用户自定义线程池的,所以总体来说各有优势。
今天先从整体架构角度来分析一下,
稍后会对各个细节做进一步的比较。