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()));
}
});
}
更多Retrofit詳情:
Retrofit 2.0 自定義Converter、
Retrofit用法詳解
更多RxJava詳情:
給 Android 開發者的 RxJava 詳解、
深入淺出RxJava(一:基礎篇)
演示
樣例: