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相同

其在使用时的流程为下:

android 框架 网络状态 android常用网络框架_android 框架 网络状态

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供开发者使用,默认的有:

android 框架 网络状态 android常用网络框架_android开发_02

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的执行流程:

android 框架 网络状态 android常用网络框架_RxJava_03

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()));
                    }
                });
    }

演示


样例:

android 框架 网络状态 android常用网络框架_网络_04