Android网络请求发展简史和RxJava+Retrofit+OkHttp实践
来源:互联网 发布:科锐c语言 编辑:程序博客网 时间:2024/06/08 13:28
Android开发网络使用小结
概述
Android 作为与IOS并驾齐驱的一个智能手机平台,在将近十年的时间内有了长足的发展,而这两大平台之所以能PK掉当年盛极一时的诺基亚及其使用的塞班系统,基于网络的丰富的功能功不可没。做了几年Android开发后,今天把Android的网络使用小结一下。
Android 网络请求推荐使用和发展历史
- 2.2之前:HttpClient
- 2.3之后:HttpURLConnection
- 2013年Google IO大会后:Google官方团队推出的volley
- OkHttp出现以后:OkHttp
- Android6.0以后Google官方Api移除HttpClient(继续使用HttpClient及基于其封装的网络库会出异常)
- 现在:推荐retrofit+OkHttp
- 我负责的项目中实际使用的网络层封装为rxJava+retrofit+OkHttp,简述一下:
- rxjava:负责订阅回调,将请求回调切到主线程、中间回调拦截处理:统一异常处理等;
- Retrofit:网络请求框架,配合OkHttp使用,使得网络请求更方便、更强大;
- OkHttp:与HttpClient和HttpURLConnection类似,最底层实际处理Http请求。
1 网络请求的原始方式
1.1 HttpClient
Apache公司提供的库,提供高效的、最新的、功能丰富的支持HTTP协议工具包,支持HTTP协议最新的版本和建议,是个很不错的开源框架,封装了Http的请求,参数,内容体,响应等,拥有众多API。
最原始的网络请求方式,很强大但API很复杂,2.3之后Google官方便更推荐使用HttpURLConnection。在此不做展开。
1.2 HttpURLConnection
Sun公司提供的库,也是Java的标准类库java.net中的一员,但这个类什么都没封装,用起来很原始,若需要高级功能,则会显得不太方便,比如重访问的自定义,会话和cookie等一些高级功能。
在Android平台,Android2.2版本之前,HttpURLConnection还不太完善,存在一些问题,最好的请求方式是HttpClient,Android2.3版本之后,HttpURLConnection功能趋于完善,成为官方推荐的网络请求方式,很多开源网络库的封装也是选择在2.3版本后网络请求最终用HttpURLConnection的方式,例如Google官方推出的网络框架Volley。
具体HttpClient和HttpURLConnection的区别可以参考这篇文章:HttpClient和HttpURLConnection的区别(点击查看)
HttpURLConnection 请求网络代码示例:
/** * 基于HttpURLConnection的Http请求的工具类 * */public class HttpUtils { private static final int TIMEOUT_IN_MILLIONS = 5000; public interface CallBack { void onRequestComplete(String result); } /** * 异步的Get请求 * * @param urlStr * @param callBack */ public static void doGetAsyn(final String urlStr, final CallBack callBack) { new Thread() { public void run() { try { String result = doGet(urlStr); if (callBack != null) { callBack.onRequestComplete(result); } } catch (Exception e) { e.printStackTrace(); } } ; }.start(); } /** * 异步的Post请求 * * @param urlStr * @param params * @param callBack * @throws Exception */ public static void doPostAsyn(final String urlStr, final String params, final CallBack callBack) throws Exception { new Thread() { public void run() { try { String result = doPost(urlStr, params); if (callBack != null) { callBack.onRequestComplete(result); } } catch (Exception e) { e.printStackTrace(); } } }.start(); } /** * Get请求,获得返回数据 * * @param urlStr * @return * @throws Exception */ public static String doGet(String urlStr) { URL url = null; HttpURLConnection conn = null; InputStream is = null; ByteArrayOutputStream baos = null; try { url = new URL(urlStr); conn = (HttpURLConnection) url.openConnection(); conn.setReadTimeout(TIMEOUT_IN_MILLIONS); conn.setConnectTimeout(TIMEOUT_IN_MILLIONS); conn.setRequestMethod("GET"); conn.setRequestProperty("accept", "*/*"); conn.setRequestProperty("connection", "Keep-Alive"); if (conn.getResponseCode() == 200) { is = conn.getInputStream(); baos = new ByteArrayOutputStream(); int len = -1; byte[] buf = new byte[128]; while ((len = is.read(buf)) != -1) { baos.write(buf, 0, len); } baos.flush(); return baos.toString(); } else { throw new RuntimeException(" responseCode is not 200 ... "); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (is != null) is.close(); } catch (IOException e) { } try { if (baos != null) baos.close(); } catch (IOException e) { } conn.disconnect(); } return null; } /** * 向指定 URL 发送POST方法的请求 * * @param url 发送请求的 URL * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 * @return 所代表远程资源的响应结果 * @throws Exception */ public static String doPost(String url, String param) { PrintWriter out = null; BufferedReader in = null; String result = ""; try { URL realUrl = new URL(url); // 打开和URL之间的连接 HttpURLConnection conn = (HttpURLConnection) realUrl .openConnection(); // 设置通用的请求属性 conn.setRequestProperty("accept", "*/*"); conn.setRequestProperty("connection", "Keep-Alive"); conn.setRequestMethod("POST"); conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); conn.setRequestProperty("charset", "utf-8"); conn.setUseCaches(false); // 发送POST请求必须设置如下两行 conn.setDoOutput(true); conn.setDoInput(true); conn.setReadTimeout(TIMEOUT_IN_MILLIONS); conn.setConnectTimeout(TIMEOUT_IN_MILLIONS); if (param != null && !param.trim().equals("")) { // 获取URLConnection对象对应的输出流 out = new PrintWriter(conn.getOutputStream()); // 发送请求参数 out.print(param); // flush输出流的缓冲 out.flush(); } // 定义BufferedReader输入流来读取URL的响应 in = new BufferedReader( new InputStreamReader(conn.getInputStream())); String line; while ((line = in.readLine()) != null) { result += line; } } catch (Exception e) { e.printStackTrace(); } // 使用finally块来关闭输出流、输入流 finally { try { if (out != null) { out.close(); } if (in != null) { in.close(); } } catch (IOException ex) { ex.printStackTrace(); } } return result; }}
2 网络请求的进阶模式
2.1 Volley
Google官方于2013年 Google IO大会上推出的 网络访问框架,现已经不推荐使用。了解可以参考这篇文章:Volley 源码解析
2.2 其他封装的网络请求框架
与volley类似的第三方封装的网络请求框架有FinalHttp、android-async-http等,基本都是基于HttpClient和HttpURLConnection封装的,本质上没有太大区别,每个库封装的成都和使用的便利性和灵活性不同。
3 网络请求的时尚模式(RxJava+Retrofit+OkHttp实践)
3.1 OkHttp
OkHttp是一款优秀的HTTP框架,它支持get请求和post请求,支持基于Http的文件上传和下载,支持加载图片,支持下载文件透明的GZIP压缩,支持响应缓存避免重复的网络请求,支持使用连接池来降低响应延迟问题。
OkHttp的入门和集成请参考这篇官方教程:OkHttp 官方教程解析 - 彻底入门 OkHttp 使用
3.2 与Retrofit结合的OkHttp
使用OkHttp的项目多半会配合同是 出的Retrofit,两者相结合,更加强大。
而我在实际开发中使用的网络框架是rxJava+Retrofit+OkHttp的结合。
不废话,直接上代码:
先看调用示例:
步骤一:在ApiService中添加接口
* 1 写上请求网络的方法名rxGetTest、接口的返回参数的Json反序列化出的Bean对象WeatherInfo;
* 2 @GET参数上配置上这个接口具体请求Url的后缀,如@GET(“data/sk/101091001.html”);
实际请求的url为 http://www.weather.com.cn/data/sk/101091001.html
retrofit中配置的BaseUrl为http://www.weather.com.cn/ 所以此处只写去除Baseurl的后缀即可。
package com.bailiangjin.httprequest.rxokdemo;import com.bailiangjin.httprequest.net.rxretrofitokhttp.design.BaseData;import com.bailiangjin.httprequest.rxokdemo.model.PostInfo;import com.bailiangjin.httprequest.rxokdemo.model.WeatherInfo;import java.util.Map;import retrofit2.http.FieldMap;import retrofit2.http.FormUrlEncoded;import retrofit2.http.GET;import retrofit2.http.POST;import rx.Observable;/** * 网络请求Service 方法汇总 将调用的方法在此处进行rx绑定 * Created by bailiangjin on 2017/2/15. */public interface ApiService { @GET("data/sk/101091001.html") Observable<WeatherInfo> rxGetTest(); @GET("query?type=yuantong&postid=200382770316") Observable<PostInfo> rxGetPostInfo(); /** * Post方式请求需要添加 FormUrlEncoded标识 * @param map * @return */ @FormUrlEncoded @POST("/") Observable<BaseData<PostInfo>> rxPostTest(@FieldMap Map<String, String> map);}
第二部 请求网络 代码如下,是不是很爽:
“`
//get调用方式示例 RxRequestHelper.requestNotDealCommonError(getWeatherApiService().rxGetTest(), new CommonResponseSubscriber() {
@Override
public void onNext(WeatherInfo weatherInfoBaseData) {
//拿到回调的数据具体处理
}
});
//post调用方式示例
/**
* post示例 不使用公有异常处理 post只写上了使用方式 具体测试请使用自己的接口测试
* @param subscriber
*/
Map
对于上面代码中用到的getWeatherApiService() 其实是一个ApiService对象,是通过如下代码生成的,如果BaseUrl一致 只用一个ApiService即可,写好一次,以后每次添加接口只走上面两部即可,如果请求的接口BaseUrl不只一个那也没关系,添加一行代码即可添加一个新的BaseUrl的Retrofit,是不是很强大,其实这主要是借助了枚举,具体可以看工程中的代码,在此不展开讨论了。
package com.bailiangjin.httprequest.rxokdemo;
import com.bailiangjin.httprequest.net.rxretrofitokhttp.tools.RetrofitCollection;
import retrofit2.Retrofit;
/**
* 天气Service
* Created by bailiangjin on 2017/2/16.
*/
public enum WeatherApiService {
INSTANCE;
private ApiService apiService;
private Retrofit retrofit;
WeatherApiService() { retrofit = RetrofitCollection.WEATHER_INSTANCE.getRetrofit(); apiService = retrofit.create(ApiService.class);}public ApiService getApiService() { return apiService;}public String getBaseUrl() { return retrofit.baseUrl().toString();}
}
其他核心代码如下:
package com.bailiangjin.httprequest.net.rxretrofitokhttp.tools;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import okhttp3.CacheControl;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.logging.HttpLoggingInterceptor;
/**
*
* 最底层的 OKHttpClient
* Created by bailiangjin on 2017/2/16.
*/
public enum MyOkHttpClient {
INSTANCE;private OkHttpClient okHttpClient;MyOkHttpClient() { okHttpClient = new OkHttpClient.Builder() //.cache(cache) //禁用okhttp自身的的缓存 .connectTimeout(10, TimeUnit.SECONDS) .readTimeout(10, TimeUnit.SECONDS) .writeTimeout(10, TimeUnit.SECONDS) .retryOnConnectionFailure(true) .addInterceptor(new MyInterceptor()) .addInterceptor(new HttpLoggingInterceptor()) .build();}public OkHttpClient getOkHttpClient() { return okHttpClient;}static class MyInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request.Builder builder = chain.request().newBuilder(); //添加header //CommonNetUtils.addHeader(builder); //修改请求为只从网络中读数据 Request request = builder .cacheControl(CacheControl.FORCE_NETWORK).build(); return chain.proceed(request); }}
}
package com.bailiangjin.httprequest.net.rxretrofitokhttp.tools;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
/**
* Retrofit 集合 可以通过添加枚举元素的方式 方便地添加 不同 root url的 retrofit
* @author bailiangjin 2017-02-16
*/
public enum RetrofitCollection {
WEATHER_INSTANCE(“http://www.weather.com.cn/“),
EXPRESS_INSTANCE(“http://www.kuaidi100.com/“);
private Retrofit retrofit;
RetrofitCollection(String baseUrl) { retrofit = new Retrofit.Builder() .client(MyOkHttpClient.INSTANCE.getOkHttpClient()) .baseUrl(baseUrl) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) //也可以添加自定义的RxJavaCallAdapterFactory .build();}public Retrofit getRetrofit() { return retrofit;}
}
package com.bailiangjin.httprequest.net.rxretrofitokhttp.tools;
import com.bailiangjin.httprequest.net.rxretrofitokhttp.design.BaseData;
import com.bailiangjin.httprequest.net.rxretrofitokhttp.design.CommonErrors;
import rx.Observable;
import rx.Subscriber;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
/**
* 网络请求的RxHelper类 功能 1、将网络请求的回调绑定到主线程 2、进行统一的异常处理 可根据需要选择
* Created by bailiangjin on 2017/2/16.
*/
public class RxRequestHelper {
/** * 绑定回调到mainthread 并统一处理异常 * @param observable * @param subscriber * @param <T> * @return */public static <T> Observable requestDealCommonError(Observable<BaseData<T>> observable, Subscriber<BaseData<T>> subscriber) { mapCommonErrors(observable); setSubscribeToAndroidMainThread(observable, subscriber); return observable;}/** * 绑定回调到mainthread 不统一处理异常 * @param observable * @param subscriber * @param <T> */public static <T> void requestNotDealCommonError(Observable<T> observable, Subscriber<T> subscriber) { setSubscribeToAndroidMainThread(observable, subscriber);}/** * 将回调切换到MainThread * @param observable * @param subscriber * @param <T> */private static <T> void setSubscribeToAndroidMainThread(Observable<T> observable, Subscriber<T> subscriber) { observable.subscribeOn(Schedulers.io()) .unsubscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(subscriber);}/** * 异常统一处理 * @param observable * @param <T> */private static <T> void mapCommonErrors(Observable<BaseData<T>> observable) { observable.map(new CommonErrors<T>());}
}
“`
篇幅问题,文章里只贴了部分代码,想实际体验请clone我的github项目代码具体运行:Github网络请求代码链接:AndroidHttpRequest (点击查看)
3.3 其他基于OkHttp的开源库
其实OkHttp并非一个网络框架,他的属性和功能与HttpClient和HttpURLConnection类似,都是Http请求网络的具体方式,当OkHttp广泛使用后,也会有很多基于OkHttp封装的可能更方便的第三方框架,这里就不在展开,感兴趣的可以Google一下。
本文所有代码都可到我的github项目AndroidHttpRequest中查看。
更多精彩文章推荐:
Android Activity 全局管理 终极解决方案
Android BaseAdapter的极简封装
- Android网络请求发展简史和RxJava+Retrofit+OkHttp实践
- Android网络请求发展简史和RxJava+Retrofit+OkHttp实践
- Android okHttp网络请求之Retrofit+Okhttp+RxJava组合
- Android okHttp网络请求之Retrofit+Okhttp+RxJava组合
- Android okHttp网络请求之Retrofit+Okhttp+RxJava组合
- Android Retrofit+rxjava+okhttp请求网络的基本用法
- Retrofit和RxJava加OkHttp网络请求进行二次封装
- 网络请求框架 Rxjava+ReTrofit+okHttp+MVP
- rxjava+Retrofit+okhttp 实战网络请求
- 封装Retrofit + okhttp + rxjava网络请求框架
- Android okHttp 实战(五):okHttp网络请求之Retrofit+Okhttp+RxJava组合
- android 网络框架学习(okhttp,retrofit,rxjava)
- kotlin for android----------MVP模式下(OKHttp和 Retrofit+RxJava)网络请求的两种实现方式
- Android OkHttp Retrofit RxJava
- RxJava+Retrofit+okhttp实践结合
- RxJava+Retrofit+OkHttp深入浅出-终极封装二(网络请求)
- RxJava+Retrofit+OkHttp组合在网络请求中的简单配置
- 打造属于自己的网络请求框架(OKHttp+Retrofit+RxJava)
- 344. Reverse String
- 文章标题
- VS 2010 MFC 制作的双色球模拟软件 之 dlg.cpp
- C#简单的函数调用
- HDU 5999 The Third Cup is Free
- Android网络请求发展简史和RxJava+Retrofit+OkHttp实践
- Debug:error:default argument given for parameter
- 喵哈哈村的括号序列(初学栈)
- 上下文
- sqlite学习笔记
- Jquery选择器
- Java类加载机制与Tomcat类加载器架构
- win10系统安装Android studio过程
- BestCoder Round #92 题解 (待续)