Android開發中常用網絡框架例舉綜合

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(一:基礎篇)

演示


樣例:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章