Android开发中常用网络框架例举综合
本篇简单总结各个框架的使用,以Get请求、JSON解析、图片加载为例。
分别使用6个框架HttpURLConnection、HttpClient、AndroidAsyncHttp、Volley、OkHttp、Retrofit2(排序根据提出的时间和学习的顺序)
1.HttpURLConnection
HttpURLConnection是较为基础的网络框架,是Android6.0以后版本默认的底层网络。
(同步的,需要自己封装线程)
static String host_address = "http://10.0.2.2:8080/listdata/";
/**
* 根据指定的Url来获取byte数据
*/
public static byte[] getData(String path) {
path = host_address + path;
byte[] data = null;
URL url = null;
try {
// 1.创建url对象
url = new URL(path);
// 2.开启链接引用
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
// 3.设置链接事件
urlConnection.setReadTimeout(5000);
urlConnection.setConnectTimeout(5000);
urlConnection.setRequestMethod("GET");
// 4.连接成功获取数据
if (urlConnection.getResponseCode() == 200) {
InputStream is = urlConnection.getInputStream();
data = BytesUtil.getBytes(is);
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return data;
}
/**
* 获取字符串
*/
public static String getString(String path) {
byte[] data = getData(path);
try {
return new String(data, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return "";
}
/**
* 获取bitmap
*/
public static Bitmap getBitmap(String path) {
byte[] data = getData(path);
Bitmap sBitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
return sBitmap;
}
/**
* 设置对应imageview的bitmap,注意listview的复用问题
*/
public static void setViewImage(final ImageView view, final String path) {
// 打上标记防止复用view时造成图片加载混乱
view.setTag(path);
new Thread() {
public void run() {
final Bitmap bitmap = getBitmap(path);
view.post(new Runnable() {
public void run() {
//核对标记的一致
if (view.getTag().equals(path))
view.setImageBitmap(bitmap);
}
});
}
}.start();
}
2.HttpClient
HttpClient是Apache提供的已经高度封装的网络请求端,在使用中他可以看作是一个网络客户端。
(同步的,需要自己封装线程,支持回调处理)
添加jar包:
在sdk路径中 ~\platforms\android-23\optional\org.apache.http.legacy.jar
static String host_address = "http://10.0.2.2:8080/listdata/";
/**
* 请求网络获取数据
*/
public static byte[] getData(String path) {
byte[] execute = null;
// 1.一个默认的httpclient
DefaultHttpClient client = new DefaultHttpClient();
// 2.创建一个get请求httpGet
HttpGet httpGet = new HttpGet(host_address + path);
try {
// 3.httpclient执行这个请求
HttpResponse response = client.execute(httpGet);
// 4.判断响应状态
int responseCode = response.getStatusLine().getStatusCode();
if (responseCode == HttpStatus.SC_OK) {
// 5.HttpEntity响应内容
HttpEntity entity = response.getEntity();
// 6.响应体中获取流对象
execute = BytesUtil.getBytes(entity.getContent());
}
} catch (IOException e) {
e.printStackTrace();
}
return execute;
}
//String解析,图片解析,方式与HttpUrlConnection相同
其在使用时的流程为下:
3.Android-Async-Http
AsyncHttp是一个支持异步的框架,其内部进行的封装使得用起来非常简单,但让代码结构看起来很差,会让网络设计的代码与UI部分混在一起。注意Asnyc-Http与AsyncTask<Params, Progress, Result>并非同一个事物,初学者在这里会混淆。
添加Module:
compile ‘com.loopj.android:android-async-http:1.4.9’
// 在应用中应全局使用一个Client避免不必要的重复
AsyncHttpClient sClient = new AsyncHttpClient();
// 使用非常简单,仅仅调取sClient的get方法即可实现一个网络请求
// 通过添加处理回调的方式实现异步
sClient.get(host_address + url,new AsyncHttpResponseHandler() {
// 连接成功时的回调,responseBody即为响应流中的数据
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
mListData.addAll(JsonUtil.parse(ListData.class, new String(responseBody)).list);
mInnerAdapter.notifyDataSetChanged();
}
// 连接失败的回调
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
error.printStackTrace();
}
});
AsyncHttp已经提供了很多Hanlder供开发者使用,默认的有:
4.Volley
Volley是官方提供的网络解析框架,其底层使用了HttpURLConnection。其具备AsyncHttp和ImageLoader的特点,支持异步加载并采用回调,支持网络缓存,并提供支持缓存管理的ImageLoader,但是缓存需要我们自己完成,下面有使用样例。
添加Module:
compile ‘eu.the4thfloor.volley:com.android.volley:2015.05.28’
在使用前需要初始化一个Volley的处理队列
//他必须在Application中声明
public class MyApplication extends Application {
private static RequestQueue mRequestQueue;
@Override
public void onCreate() {
super.onCreate();
// 为volley声明一个队列
mRequestQueue = Volley.newRequestQueue(this);
}
public static RequestQueue getRequestQueue() {
return mRequestQueue;
}
}
1.像AsyncHttp那样拿到数据再解析再处理,Volley中请求需要被封装为一个Request对象,并在其中实现对Response的处理封装。
static String host_address = "http://10.0.2.2:8080/listdata/";
/**
* 使用自定义request的方式去获取json数据,不会有乱码
*/
public static void getData(String url, final List<ListData.Data> dataList, final BaseActivity.InnerAdapter adapter) {
// 1.使用自定义实现Request,参数中null为ErrorListener可以不处理
Request<String> request = new Request<String>(Request.Method.GET, host_address + url, null) {
// 2.网络数据解析过程,通过Response的Api可以获取一个默认的封装
protected Response<String> parseNetworkResponse(NetworkResponse response) {
byte[] data = response.data;
// 3.Entry是指定缓存类型中的条目,注意这里如果不想缓存可取消缓存
return Response.success(new String(data), HttpHeaderParser.parseCacheHeaders(response));
}
// 3.解析结果的处理
protected void deliverResponse(String response) {
ListData parse = JsonUtil.parse(ListData.class, response);
dataList.addAll(parse.list);
adapter.notifyDataSetChanged();
}
};
// 取消缓存
// request.setShouldCache(false);
sRequestQueue.add(request);
}
2.使用默认的StringRequest处理json数据
public static void getString(String url, final List<ListData.Data> dataList, final BaseActivity.InnerAdapter innerAdapter) {
// 使用默认的StringRequest需要添加响应成功的监听和失败的监听
sRequestQueue.add(
new StringRequest(Request.Method.GET, host_address + url, new Response.Listener<String>() {
// 重写该方法,实现对String结果的处理
public void onResponse(String response) {
ListData parse = JsonUtil.parse(ListData.class, response);
dataList.addAll(parse.list);
innerAdapter.notifyDataSetChanged();
}
}, new Response.ErrorListener() {
// 重写该方法实现对异常情况的处理
public void onErrorResponse(VolleyError error) {
error.printStackTrace();
Log.e("tag", "error");
}
}) {
//重写StringRequst中文字解析的过程,修复文字乱码的问题
protected Response<String> parseNetworkResponse(NetworkResponse response) {
String parsed;
try {
// 源码中的文字编码是获取自网络响应头中的,与U8不同时会造成乱码
// parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
parsed = new String(response.data, "UTF-8");
} catch (UnsupportedEncodingException e) {
parsed = new String(response.data);
}
return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
}
});
}
3.使用默认的ImageRequest加载图片
/**
* 使用与StringRequest相同,但低效率,很慢,画质差
*/
public static void getViewImage_request(final ImageView imageView, final String url) {
// 设置标记进行校验,防止listview复用造成图片错位
imageView.setTag(url);
sRequestQueue.add(new ImageRequest(host_address + url, new Response.Listener<Bitmap>() {
@Override
public void onResponse(Bitmap response) {
// 校验标记
if (imageView.getTag().equals(url))
imageView.setImageBitmap(response);
}
}, 0, 0, Bitmap.Config.ARGB_8888, new Response.ErrorListener() {
public void onErrorResponse(VolleyError error) {
// empty
}
}));
}
4.采用ImageLoader的方式来管理图片加载
ImageLoader(RequestQueue queue, ImageCache imageCache) // ImageLoader的构造方法
ImageLoader需要提供一个imagecache作为缓存处理的方式,但imagecache中是如何处理缓存的由我们来实现,这里采用lrucache集合的形式在内存中缓存,当然也可以使用其他想要的缓存方式。
private static ImageLoader.ImageCache sImageCache;
private static LruCache<String, Bitmap> sLruCache;
private static ImageLoader sImageLoader;
public static void getViewImage_loader(String url, ImageView imageView) {
// volley没有实现默认的缓存,需要我们自己实现
// 1.初始化一个默认大小的内存缓存
if (sLruCache == null) {
sLruCache = getLruCache();
}
// 2.将对应的bitmap缓存存取方式进行封装
if (sImageCache == null)
sImageCache = new ImageLoader.ImageCache() {
@Override
public Bitmap getBitmap(String url) {
return sLruCache.get(url);
}
@Override
public void putBitmap(String url, Bitmap bitmap) {
sLruCache.put(url, bitmap);
}
};
// 3.根据已经创建好的imageCache和requestQueue来初始化一个新的imageLoader
if (sImageLoader == null)
sImageLoader = new ImageLoader(MyApplication.getRequestQueue(), sImageCache);
// 4.为对应的imageView生成一个默认的imageListener,设置初始图片和失败图片
ImageLoader.ImageListener listener = ImageLoader.getImageListener(imageView, R.mipmap.ic_launcher, R.mipmap.ic_launcher);
// 5.使用ImageLoader的get请求这个加载,内部仍然是ImageRequest
sImageLoader.get(host_address + url, listener);
}
/**
* 获取一个LruCache,最近最少使用算法也是较为容易理解的一种算法
*/
private static LruCache getLruCache() {
long maxMemory = Runtime.getRuntime().maxMemory();
long cacheSize = maxMemory / 8;
sLruCache = new LruCache<String, Bitmap>((int) (cacheSize / 1024)) {
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes() * value.getHeight() / 1024;
}
};
return sLruCache;
}
5.OkHttp
OkHttp是现在较为流行的网络框架之一,他与HttpClient的相似之处在于都可将网络看作一个Client,并通过Client执行一个封装的request再从执行的结果Response中获取数据。
但不同的是,OkHttp中Client并非直接execute而是newCall返回一个Call对象,在Call对象中我们可以使用同步的方式处理或者异步的方式处理,并且可以控制这条请求的开关call.cancel()。
添加Module:
compile ‘com.squareup.okhttp:okhttp:2.0.0’
static String host_address = "http://10.0.2.2:8080/listdata/";
public static OkHttpClient mClient = new OkHttpClient();
/**
* OKHttp用法与HttpClient类似
*/
public static byte[] getData(String url) throws IOException {
// 1.通过Builder建造 Request
Request.Builder builder = new Request.Builder();
// 2.链式编程设置url和请求方式
builder.url(host_address + url).
get();
// 3.建造出指定的request
Request request = builder.build();
// 4.获取request已经实现了CallFactory工厂,使用其进行生产
Call call = mClient.newCall(request);
// 5.执行对应的call获取响应对象(同步)
Response response = call.execute();
// 6.判断响应状态
if (response.code() == 200) {
// 7.获取响应体
ResponseBody body = response.body();
// 8.直接获取相应体中数据,也可以获取流对象,只有输入流
return body.bytes();
}
return null;
}
//String解析,图片解析,方式与HttpUrlConnection相同
异步方式,通过给call添加回调来处理结果
call.enqueue(new Callback() {
public void onFailure(Request request, IOException e) {
// empty
}
public void onResponse(Response response) throws IOException {
// TODO
}
});
OkHttp的执行流程:
6.Retrofit2
Retrofit经常与OkHttp搭配使用,其采用注解的方式简化网络请求代码的书写(此处有坑),并支持常规的callback方式,也支持observer的RxJava模式。(反正就是很牛,因为是初学所以本篇只是简单使用了一下,体现出怎么解析json数据、图片加载,并与前面几种方式形成对比)。
添加Module:
compile 'com.squareup.retrofit2:retrofit:2.1.0'//Retrofit2所需要的包 compile 'com.squareup.retrofit2:converter-gson:2.1.0'//ConverterFactory的Gson依赖包 compile 'com.squareup.retrofit2:converter-scalars:2.1.0'//ConverterFactory的String依赖包 compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0' //RxJavaCallAdapter依赖包 compile 'io.reactivex:rxandroid:1.2.1' // RxAndroid依赖包
1.配置一个全局的Retrofit,这一点与前面几个方法很相似
private static Retrofit sRetrofit;
private static Retrofit getRetrofit() {
// 1.初始化一个建造者
Retrofit.Builder builder = new Retrofit.Builder();
// 2.添加主机地址
builder.baseUrl(HttpUrl.parse("http://10.0.2.2:8080"))
// .client(mOkHttpClient)可以使用指定的OkHttpClient
// 3.添加数据转换工厂
.addConverterFactory(ScalarsConverterFactory.create())// 处理基本数据类型
.addConverterFactory(GsonConverterFactory.create())// 处理gson数据类型
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())// 添加回调的适配器,RxJava的回调形式(即非callback的形式);
// 4.生成配置好的retrofit
return builder.build();
}
2.将对应的服务器api接口封装为本地接口,这里方法返回Call对象方便使用和理解,Observable对象是RxJava中使用到的,但也可以使用Call的形式进行处理
public interface ListDataService {
@GET("/listdata/{datapath}")
Call<ListData> getListData(@Path("datapath") String path);
// (坑!)BUG:这里采用了切割,因为Retrofit将链接中的"/"转义为"%2F"造成请求失败 错误码400
@GET("/listdata/app/{package}/{icon}")
Observable<ResponseBody> getBitmap(@Path("package") String packageName, @Path("icon") String icon);
}
3.将本地接口生成对应的实例
private static ListDataService sService;
/**
* 根据已经构造的服务,获取api的接口服务实例
*/
public static ListDataService getService() {
// 1.获取retrofit
if (sRetrofit == null)
sRetrofit = getRetrofit();
// 2.使用retrofit创建对应api的接口
if(sService == null)
sService = sRetrofit.create(ListDataService.class);
return sService;
}
4.使用接口中的方法
使用返回的Call对象,采用异步的方式执行,当然也可以使用同步的方式(与OkHttp中的execute)。
Call对象的用法OkHttp中的Call对象相同。
解析Json数据
public static void getListData(String path, Callback<ListData> callback) {
// 1.检查是否有sService
if (sService == null)
sService = getService();
// 2.调用接口方法,获取Call
Call<ListData> listdata = sService.getListData(path);
// 3.执行Call并为其添加回调
listdata.enqueue(callback);
// 同步
// listdata.execute().body();
}
解析图片数据,这里使用了RxJava的回调形式
public static void setViewImage(String path, final ImageView view) {
// 1.检查是否有sService
if (sService == null)
sService = getService();
// 切割字符串避免"/"被转义
String[] split = path.split("/");
String packageName = split[1];
String icon = split[2];
// 2.实现retrofit的图片加载
Observable<ResponseBody> bitmap = sService.getBitmap(packageName, icon);
bitmap.subscribeOn(Schedulers.io()) // 设置注册观察者的线程
.observeOn(AndroidSchedulers.mainThread())// 设置观察者响应的线程,要添加RxAndroid
.subscribe(new Observer<ResponseBody>() { // 注册观察者
@Override
public void onCompleted() {
// 在所有的next事件都被执行了之后
Log.i("RxJava","onCompleted");
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
}
@Override
public void onNext(ResponseBody responseBody) {
view.setImageBitmap(BitmapFactory.decodeStream(responseBody.byteStream()));
}
});
}
演示
样例: