android retrofit2.0框架的使用介绍
2017-12-04 05:03
453 查看
准备学习下retrofit框架的使用,然后封装下,现在不懂retrofit都不意思说自己是android开发,所以也学习下,在github上的地址是:https://github.com/square/retrofit
要使用retrofit要引入的库还是挺多的//rxjava引入的包
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
compile 'io.reactivex.rxjava2:rxjava:2.1.7'
//retrofit引入的包
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.google.code.gson:gson:2.2.4'
//返回给我的是一个json解析后的bean
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
//rxjava2+retrofit搭配使用的依赖
compile 'com.squareup.retrofit2:adapter-rxjava:2.3.0'
//okhttp日记拦截器
compile 'com.squareup.okhttp3:logging-interceptor:3.9.1'在这多说一句,像前面三个引入的在rxandroid,retrofit的 github文档上就可以直接copy过来,但是像后面三个我怎么在studio下依赖就是找不到,后面实在没办法,就直接百度了下到他的官网介绍中去找对应的版本了,
![](http://img.blog.csdn.net/20171130034433981?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY29kZXJpbmNoaW5h/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
算是一个小插曲吧.
开发包引入了以后就是学习怎么使用了,首先看下他的文档,肯定是有demo给我们介绍的.
http://square.github.io/retrofit/
上面是他的官网文档,首先看下它给的例子,然后照着他写的抄就行
![](http://img.blog.csdn.net/20171130034751717?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY29kZXJpbmNoaW5h/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
第一步:public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}它定义了一个接口,然后有个方法listRepos,返回的是一个Call对应,还是画图说下容易解释
![](http://img.blog.csdn.net/20171130041646200?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY29kZXJpbmNoaW5h/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
把上面的图翻译下就是这个: /**
* method 表示请的方法,
* path表示路径
* hasBody表示是否有请求体
*/
@HTTP(method = "get", path = "users/{user}", hasBody = false)
这是从网上看到别人写的,觉得对理解起来很不错,就copy过来了,非常感谢,现在对着这个来模仿写一个请求方法
.baseUrl("http://gank.io/")
.addConverterFactory(GsonConverterFactory.create())//支持把请求的json转换bean
.build();第三步:
我们知道get方式请求,我们可以直接把参数拼接在url后面,这样就不用带参数,@GET("api/data/Android/10/1")
Call<Info> getInfoData();还有一种就是如果我们要传的参数有很多,这个时候,就可以使用使用这个注解:@QueryMap
还有一个用于在参数的注解@Query@GET("api/data/")
Call<Info> getInfoData(@Query("Android") String Android, @Query("size") String size, @Query("page") int page);记住如果使用@Query注解作用在参数的时候,url后面不要带参数,请看请求的地址:
![](http://img.blog.csdn.net/20171130142653719?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY29kZXJpbmNoaW5h/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
发现也是把key_value进行拼接的在url后面,所以@Path和@Query注解有啥不同就出现了
@Path是将value值直接放在url后面,用/分割
@Query是将key-value的形式拼接在url后面
@QueryMap是将多个key-value的形式拼接在url后面
当然如果想对参数进行编码的话,他们都提供的有个encode,默认值是为false,如果不对参数进行url编码的话就传true,
@Query还能传递一个数组,相当于是我们Java中的可变参数一样
@GET("api/data/")
Call<Info> getInfoData(@Query ("Android")String...Android );也就是说他的key是一样的,只是传递的value不同而已,http://gank.io/api/data/?Android=Android&Android=java&Android=php
Post请求
现在自己使用tomcat搭建一个简单的服务器,来玩下post请求@POST("aa/HelloServlet")
Call<Person> login(@Field("username")String username, @Field("password") String password);这是用户登录的,传递了二个参数
![](http://img.blog.csdn.net/20171130154953292?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY29kZXJpbmNoaW5h/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
意思是说我们传递的参数没有进行编码,所以post请求一定要加@FormUrlEncoded@FormUrlEncoded
@POST("aa/HelloServlet")
Call<Person> login(@Field("username")String username, @Field("password") String password);再试试就成功了,服务器接受的消息:
![](http://img.blog.csdn.net/20171130155230329?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY29kZXJpbmNoaW5h/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
客户端接收到的
![](http://img.blog.csdn.net/20171130155357461?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY29kZXJpbmNoaW5h/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
如果有多个参数也可以使用@FieldMap
文件下载
首先使用get方法从豆瓣上下载一张图片@GET("/view/photo/m/public/p2505783407.webp")
Call<ResponseBody> download_get();我把下载后的文件存放在sd卡下的:
![](http://img.blog.csdn.net/20171130162550607?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY29kZXJpbmNoaW5h/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
下载成功了我们就到文件管理器中去找下刚才下载的文件
![](http://img.blog.csdn.net/20171130163205413?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY29kZXJpbmNoaW5h/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
刚才使用get方式文件下载,现在使用post方式文件下载也是一样:@POST("/view/photo/m/public/p2505783407.webp")
Call<ResponseBody> download_post();其他代码不变.文件下载好了,开始学习下文件上传,文件上传
先来个简单的单文件上传@Multipart
@POST("/aa/UploadServlet")
Call<ResponseBody> upload_post(@Part MultipartBody.Part file);实现:
![](http://img.blog.csdn.net/20171130182201757?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY29kZXJpbmNoaW5h/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
在这我弄好久,刚开始是豆瓣上下载的图片上传的 https://img1.doubanio.com/view/photo/albumcover/public/p2220219198.webp
他是webp格式的,我下载后在我电脑上打不开,请求也不成功,后来突然想到了换个jpg格式的,一下子就成功了,折腾了1个多小时,我也是晕死.
下载到我的电脑上的:
![](http://img.blog.csdn.net/20171130182446104?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY29kZXJpbmNoaW5h/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
服务端代码,也贴下
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Part part=request.getPart("uploadfile");
PrintWriter out=response.getWriter();
System.out.println("ContentType="+part.getContentType()+"\n");
out.print("ContentType="+part.getContentType()+"\n");
System.out.println("size="+part.getSize()+"\n");
out.print("size="+part.getSize()+"\n");
String name = part.getName();
out.print("name="+name+"\n");
part.write("C://Users/zhouguizhijxhz/zgz1.png");
Gson gson=new Gson();
UpLoad upLoad = new UpLoad();
upLoad.setCode(0);
upLoad.setMsg("success");
response.getWriter().write("success");
}
单个文件上传成功了,但是可能要求你上传文件的同时,还要传递参数,这个也搞定下,很快的。@Multipart
@POST("/aa/UploadServlet")
Call<ResponseBody> mulitupload(@Part MultipartBody.Part file,@QueryMap Map<String,String> map);实现public void upload_params(View view){
OkHttpClient httpClient = new OkHttpClient();
if (BuildConfig.DEBUG) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
httpClient = new OkHttpClient.Builder().addInterceptor(logging).build();
}
Retrofit retrofit = new Retrofit.Builder()
.client(httpClient)
.baseUrl("http://192.168.1.107:8080/")
.build();
File file = new File(getPath(),"zgz3.jpg");
RequestBody requestBody = RequestBody.create(MediaType.parse("multipart/form-data"), file);
MultipartBody.Part body = MultipartBody.Part.createFormData("uploadfile", file.getName(), requestBody);
KSService service = retrofit.create(KSService.class);
Map<String,String> map = new HashMap<>();
map.put("name","zhouugizhi");
map.put("time","2017-11-30");
Call<ResponseBody> call = service.mulitupload(body,map);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
try {
Log.e(TAG,"文件上传成功="+response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.e(TAG,"文件上传失败="+t.getLocalizedMessage());
}
});
}看下服务器接受到传递的参数:
![](http://img.blog.csdn.net/20171130185815963?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY29kZXJpbmNoaW5h/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
name就是我传递的参数,time没有做打印,现在看下客户度的返回什么数据,我把name,time返回给客户端了
![](http://img.blog.csdn.net/20171130185933025?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY29kZXJpbmNoaW5h/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
?是中文乱码了,不管,单上文上传并传参也实现了,多文件上传,由于不会服务器端多文件怎么处理,暂时放一放.
retrofit+rxjava
retrofit内置式支持rxjava操作的,当然需要去设置下,请求方法不再是返回一个Call对象了,而是返回一个被观察者Observable,加上下面这行代码:
addCallAdapterFactory(RxJava2CallAdapterFactory.create())
但是发现我怎么发现我没有这个类呢?,原来是我那个rxjava适配器的包没有使用大神的// compile 'com.squareup.retrofit2:adapter-rxjava:2.3.0'
compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0-RC3'使用下面的使用rxjava方式请求一个get,方法的定义@GET("api/data/{Android}/{size}/{page}")
Observable<ResponseBody> getInfoDataByRxjava(@Path(value = "Android",encoded = true) String Android, @Path(value = "size",encoded = true) String size, @Path(value = "page",encoded = true) int page);实现
![](http://img.blog.csdn.net/20171201160401538?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY29kZXJpbmNoaW5h/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
关于retrofit+rxjava就是这么使用的,只是我们原来单独使用rxjava的是通过操作符create返回一个Observable,现在不用 了在retrofit中,还有就是我们之前是去调异步或者同步的方法去请求网络,使用了额rxjava就不用去掉了,
添加网络日志信息
前面我打印出来的log,如果不做什么处理,是没有这些log的,在retrofit要设置,请求的和返回的log才会打印出来,特别是我们的开发阶段,一定要打log,又可能一个问题,看log就直接解决了,比如后面返回的json数据一个类型出错了,就会导致你转bean抛错误了,在此一定别忘记了添加依赖
compile 'com.squareup.okhttp3:logging-interceptor:3.9.1
关于拦截器都是在OkhttpClient中去设置的,OkHttpClient httpClient = new OkHttpClient();
if (BuildConfig.DEBUG) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
httpClient = new OkHttpClient.Builder().addInterceptor(logging).build();
}它有四个等级,看下源码就知道了.
URL统一追加参数public class AppendUrlParamInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();//拿到发送的请求
HttpUrl httpUrl = request.url().newBuilder().addQueryParameter("version","1.0")
.addQueryParameter("channel","360")
.addQueryParameter("versionCode","10")
.addQueryParameter("deviceId","abasersgdhdjjfs5688")
.build();
//重要一步 使用新的url构建一个Request发送给后台
Request newRequest = request.newBuilder().url(httpUrl).build();
return chain.proceed(newRequest);
}
}添加什么拦截都是实现InterceptorInterceptor接口,然后去里面拿到Request去按需求做你的操作,大部分都是套路代码,写一个遍会了就会了,最后别忘记了把这个拦截器设置给OkHttpClient完整的代码贴下:public void append_url_by_interceptor(View view){
OkHttpClient httpClient = new OkHttpClient();
if (BuildConfig.DEBUG) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
//统一在url后面添加参数
AppendUrlParamInterceptor appendUrlParamInterceptor = new AppendUrlParamInterceptor();
httpClient = new OkHttpClient.Builder().addInterceptor(logging).addInterceptor(appendUrlParamInterceptor).build();
}
Retrofit retrofit = new Retrofit.Builder() .client(httpClient) .baseUrl("http://gank.io") .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build();
KSService service = retrofit.create(KSService.class);
Observable<ResponseBody> observable = service.getInfoDataByRxjava("Android","10",1);
observable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<ResponseBody>(){
@Override
public void accept(ResponseBody responseBody) throws Exception {
String s = responseBody.string();
Log.e(TAG,"请求的结果:"+s);
}
});
}现在查看log,到底有没有成功
![](http://img.blog.csdn.net/20171201165654211?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY29kZXJpbmNoaW5h/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
刚才是用get请求方式,那么post请求方式可以么,试试就知道了,
现在定义一个方法去请求我们服务器@FormUrlEncoded
@POST("aa/HelloServlet")
Observable<ResponseBody> appendUrlParamByInterceptor(@FieldMap Map<String,String> map);实现
![](http://img.blog.csdn.net/20171201171439334?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY29kZXJpbmNoaW5h/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
然后看下客户端的log:
![](http://img.blog.csdn.net/20171201171525100?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY29kZXJpbmNoaW5h/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
可以看到是post请求,然后是第一次url请求地址也出来了,拦截了后,我们追加的请求参数也打印出来了,
Header统一添加参数
还是用刚才的那个请求,统一添加请求头自定义拦截器如下:public class AppendHeaderParamInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();//拿到发送的请求
Headers headers = request.headers().newBuilder().add("token","mytoken").build();
Request newRequest = request.newBuilder().headers(headers).build();
return chain.proceed(newRequest);
}
}实现:
![](http://img.blog.csdn.net/20171201173603673?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY29kZXJpbmNoaW5h/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
现在看客户端的log:
![](http://img.blog.csdn.net/20171201173707989?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY29kZXJpbmNoaW5h/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
无网络统一处理
之前项目无网络都是每个接口手动去判断,如果你做了封装的话还好点,至少只要判断一次就行了,而不是等请求发送出去了以后,回来发现给你一个提示无网络或者什么的,我们在请求之前就统一检查网络并处理
还是和以前一样,自定义拦截器,public class HandleNoNetInterceptor implements Interceptor {
private Context content;
public HandleNoNetInterceptor(Context content) {
this.content = content;
}
@Override
public Response intercept(Chain chain) throws IOException {
if(NetUtils.isNetworkAvalible(content)){
return chain.proceed(chain.request());
}else{
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(content,"网络异常,请检查你的网络",Toast.LENGTH_LONG).show();
}
});
throw new RuntimeException("网络异常了~~~~~");
}
}
}因为我们在发送网络请求的时候,是放在子线程中,所以你不使用handler post这个toast是无法出来的,如果是使用rxjava,他有个onError在这里可以做针对你的需求做处理,
![](http://img.blog.csdn.net/20171201194221995?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY29kZXJpbmNoaW5h/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
缓存设置
也是要自定义拦截器的,public class CacheInterceptor implements Interceptor {
private Context context;
public CacheInterceptor(Context context) {
this.context = context;
}
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
//这是在没网的时候设置缓存
if(!NetUtils.isNetworkAvalible(context.getApplicationContext())){
Request cacheRequest = request.newBuilder()
.cacheControl(CacheControl.FORCE_CACHE)
.build();
return chain.proceed(cacheRequest);
}
Response cacheResponse = chain.proceed(request);
long cacheMaxTime = 1*60*60;//缓存1小时
return cacheResponse.newBuilder()
.header("Cache-Control", "public, only-if-cached, max-stale="+cacheMaxTime)
.removeHeader("Pragma")
.build();
}
}
我这是请求http://www.qq.com,这个缓存必须要服务器支持,如果服务器不支持的话,是会报错的,
HTTP 504 Unsatisfiable Request (only-if-cached)我刚开始就遇到了这个问题,网上查了下.
请求的方法@GET
Observable<ResponseBody> request_qq(@Url String url);最终的实现
![](http://img.blog.csdn.net/20171204034714807?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY29kZXJpbmNoaW5h/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
打开缓存文件;
![](http://img.blog.csdn.net/20171204034755522?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY29kZXJpbmNoaW5h/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
这是需要服务器支持缓存的,如果你服务器不支持缓存,但是你在没网的时候又想缓存的数据,就要自己搞了,
超时,错误重试机制
这个和okHttp一样的,废话本来底层网络就是用okhttp..connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(15,TimeUnit.SECONDS)
.writeTimeout(15,TimeUnit.SECONDS)
.retryOnConnectionFailure(true)还有个多文件上传,还没弄,这个后期补上,就写到这里吧.
要使用retrofit要引入的库还是挺多的//rxjava引入的包
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
compile 'io.reactivex.rxjava2:rxjava:2.1.7'
//retrofit引入的包
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.google.code.gson:gson:2.2.4'
//返回给我的是一个json解析后的bean
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
//rxjava2+retrofit搭配使用的依赖
compile 'com.squareup.retrofit2:adapter-rxjava:2.3.0'
//okhttp日记拦截器
compile 'com.squareup.okhttp3:logging-interceptor:3.9.1'在这多说一句,像前面三个引入的在rxandroid,retrofit的 github文档上就可以直接copy过来,但是像后面三个我怎么在studio下依赖就是找不到,后面实在没办法,就直接百度了下到他的官网介绍中去找对应的版本了,
算是一个小插曲吧.
开发包引入了以后就是学习怎么使用了,首先看下他的文档,肯定是有demo给我们介绍的.
http://square.github.io/retrofit/
上面是他的官网文档,首先看下它给的例子,然后照着他写的抄就行
第一步:public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}它定义了一个接口,然后有个方法listRepos,返回的是一个Call对应,还是画图说下容易解释
把上面的图翻译下就是这个: /**
* method 表示请的方法,
* path表示路径
* hasBody表示是否有请求体
*/
@HTTP(method = "get", path = "users/{user}", hasBody = false)
这是从网上看到别人写的,觉得对理解起来很不错,就copy过来了,非常感谢,现在对着这个来模仿写一个请求方法
public interface KSService { @GET("api/data/{Android}/{size}/{page}") Call<Info> getInfoData(@Path("Android") String Android, @Path("size") String size, @Path("page") int page); }第二步:构建一个Retrofit对象Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://gank.io/")
.addConverterFactory(GsonConverterFactory.create())//支持把请求的json转换bean
.build();第三步:
KSService service = retrofit.create(KSService.class);第四步:
Call<Info> call = service.getInfoData("Android","10",1);//传递的参数第五步:
call.enqueue(new Callback<Info>() { @Override public void onResponse(Call<Info> call, Response<Info> response) { Info info = response.body(); List<Info.ResultsBean> list = info.getResults(); Log.e(TAG,"url="+list.get(0).getUrl()); } @Override public void onFailure(Call<Info> call, Throwable t) { } });上面就是发送一个异步get的请求,同步我不会再讲了,几乎没人用同步去请求网络,可能第一次写感觉乖乖的,但是多写几次就熟练了.
我们知道get方式请求,我们可以直接把参数拼接在url后面,这样就不用带参数,@GET("api/data/Android/10/1")
Call<Info> getInfoData();还有一种就是如果我们要传的参数有很多,这个时候,就可以使用使用这个注解:@QueryMap
@GET("api/data/") Call<Info> getInfoData(@QueryMap Map<String, String> map);在传参的时候传递一个map集合就可以了
Map<String,String> map = new HashMap<>(); map.put("Android","Android"); map.put("size","10"); map.put("page","1"); Call<Info> call = service.getInfoData(map);//传递的参数但有的时候用不了这个,因为这个是把参数拼接在url后面,比如这个url:http://gank.io/api/data/Android/10/1如果使用@QueryMap传递多个参数,最后的请求url是这个:http://gank.io/api/data/?page=1&size=10&Android=Android
还有一个用于在参数的注解@Query@GET("api/data/")
Call<Info> getInfoData(@Query("Android") String Android, @Query("size") String size, @Query("page") int page);记住如果使用@Query注解作用在参数的时候,url后面不要带参数,请看请求的地址:
发现也是把key_value进行拼接的在url后面,所以@Path和@Query注解有啥不同就出现了
@Path是将value值直接放在url后面,用/分割
@Query是将key-value的形式拼接在url后面
@QueryMap是将多个key-value的形式拼接在url后面
当然如果想对参数进行编码的话,他们都提供的有个encode,默认值是为false,如果不对参数进行url编码的话就传true,
@Query还能传递一个数组,相当于是我们Java中的可变参数一样
@GET("api/data/")
Call<Info> getInfoData(@Query ("Android")String...Android );也就是说他的key是一样的,只是传递的value不同而已,http://gank.io/api/data/?Android=Android&Android=java&Android=php
Post请求
现在自己使用tomcat搭建一个简单的服务器,来玩下post请求@POST("aa/HelloServlet")
Call<Person> login(@Field("username")String username, @Field("password") String password);这是用户登录的,传递了二个参数
public void post(View view){ OkHttpClient httpClient = new OkHttpClient(); if (BuildConfig.DEBUG) { HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BODY); httpClient = new OkHttpClient.Builder().addInterceptor(logging).build(); } Retrofit retrofit = new Retrofit.Builder() .client(httpClient) .baseUrl("http://192.168.1.107:8080/") .addConverterFactory(GsonConverterFactory.create())//支持把请求的json转换bean .build(); KSService service = retrofit.create(KSService.class); Call<Person> call = service.login("zhouguizhi","123456"); call.enqueue(new Callback<Person>() { @Override public void onResponse(Call<Person> call, Response<Person> response) { Log.e(TAG,"登录请求成功"); Log.e(TAG,"response="+response.body()); } @Override public void onFailure(Call<Person> call, Throwable t) { } }); }发现报错了,错误日记:
意思是说我们传递的参数没有进行编码,所以post请求一定要加@FormUrlEncoded@FormUrlEncoded
@POST("aa/HelloServlet")
Call<Person> login(@Field("username")String username, @Field("password") String password);再试试就成功了,服务器接受的消息:
客户端接收到的
如果有多个参数也可以使用@FieldMap
文件下载
首先使用get方法从豆瓣上下载一张图片@GET("/view/photo/m/public/p2505783407.webp")
Call<ResponseBody> download_get();我把下载后的文件存放在sd卡下的:
public void download_get(View view){ OkHttpClient httpClient = new OkHttpClient(); if (BuildConfig.DEBUG) { HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BODY); httpClient = new OkHttpClient.Builder().addInterceptor(logging).build(); } Retrofit retrofit = new Retrofit.Builder() .client(httpClient) .baseUrl("https://img1.doubanio.com") .build(); KSService service = retrofit.create(KSService.class); Call<ResponseBody> call = service.download_get(); call.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { InputStream inputStream = response.body().byteStream(); Log.e(TAG,"下载成功="); FileOutputStream fileOutputStream = null; try { fileOutputStream = new FileOutputStream(new File(getPath(),"/zgz2.jpg")); byte[] buffer = new byte[2048]; int len = 0; while ((len = inputStream.read(buffer)) != -1) { fileOutputStream.write(buffer, 0, len); } fileOutputStream.flush(); } catch (IOException e) { Log.e(TAG,"下载过程中失败="+e); }finally { try { if(null!=fileOutputStream){ fileOutputStream.close(); } } catch (IOException e) { e.printStackTrace(); } try { if(inputStream!=null){ inputStream.close(); } } catch (IOException e) { e.printStackTrace(); } } } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { Log.e(TAG,"下载失败="+t.getLocalizedMessage()); } }); }查看日记:
下载成功了我们就到文件管理器中去找下刚才下载的文件
刚才使用get方式文件下载,现在使用post方式文件下载也是一样:@POST("/view/photo/m/public/p2505783407.webp")
Call<ResponseBody> download_post();其他代码不变.文件下载好了,开始学习下文件上传,文件上传
先来个简单的单文件上传@Multipart
@POST("/aa/UploadServlet")
Call<ResponseBody> upload_post(@Part MultipartBody.Part file);实现:
public void upload(View view){ OkHttpClient httpClient = new OkHttpClient(); if (BuildConfig.DEBUG) { HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BODY); httpClient = new OkHttpClient.Builder().addInterceptor(logging).build(); } Retrofit retrofit = new Retrofit.Builder() .client(httpClient) .baseUrl("http://192.168.1.107:8080/") .build(); File file = new File(getPath(),"zgz3.jpg"); RequestBody requestBody = RequestBody.create(MediaType.parse("multipart/form-data"), file); MultipartBody.Part body = MultipartBody.Part.createFormData("uploadfile", file.getName(), requestBody); KSService service = retrofit.create(KSService.class); Call<ResponseBody> call = service.upload_post(body); call.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { try { Log.e(TAG,"文件上传成功="+response.body().string()); } catch (IOException e) { e.printStackTrace(); } } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { Log.e(TAG,"文件上传失败="+t.getLocalizedMessage()); } }); }log:
在这我弄好久,刚开始是豆瓣上下载的图片上传的 https://img1.doubanio.com/view/photo/albumcover/public/p2220219198.webp
他是webp格式的,我下载后在我电脑上打不开,请求也不成功,后来突然想到了换个jpg格式的,一下子就成功了,折腾了1个多小时,我也是晕死.
下载到我的电脑上的:
服务端代码,也贴下
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Part part=request.getPart("uploadfile");
PrintWriter out=response.getWriter();
System.out.println("ContentType="+part.getContentType()+"\n");
out.print("ContentType="+part.getContentType()+"\n");
System.out.println("size="+part.getSize()+"\n");
out.print("size="+part.getSize()+"\n");
String name = part.getName();
out.print("name="+name+"\n");
part.write("C://Users/zhouguizhijxhz/zgz1.png");
Gson gson=new Gson();
UpLoad upLoad = new UpLoad();
upLoad.setCode(0);
upLoad.setMsg("success");
response.getWriter().write("success");
}
单个文件上传成功了,但是可能要求你上传文件的同时,还要传递参数,这个也搞定下,很快的。@Multipart
@POST("/aa/UploadServlet")
Call<ResponseBody> mulitupload(@Part MultipartBody.Part file,@QueryMap Map<String,String> map);实现public void upload_params(View view){
OkHttpClient httpClient = new OkHttpClient();
if (BuildConfig.DEBUG) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
httpClient = new OkHttpClient.Builder().addInterceptor(logging).build();
}
Retrofit retrofit = new Retrofit.Builder()
.client(httpClient)
.baseUrl("http://192.168.1.107:8080/")
.build();
File file = new File(getPath(),"zgz3.jpg");
RequestBody requestBody = RequestBody.create(MediaType.parse("multipart/form-data"), file);
MultipartBody.Part body = MultipartBody.Part.createFormData("uploadfile", file.getName(), requestBody);
KSService service = retrofit.create(KSService.class);
Map<String,String> map = new HashMap<>();
map.put("name","zhouugizhi");
map.put("time","2017-11-30");
Call<ResponseBody> call = service.mulitupload(body,map);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
try {
Log.e(TAG,"文件上传成功="+response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.e(TAG,"文件上传失败="+t.getLocalizedMessage());
}
});
}看下服务器接受到传递的参数:
name就是我传递的参数,time没有做打印,现在看下客户度的返回什么数据,我把name,time返回给客户端了
?是中文乱码了,不管,单上文上传并传参也实现了,多文件上传,由于不会服务器端多文件怎么处理,暂时放一放.
retrofit+rxjava
retrofit内置式支持rxjava操作的,当然需要去设置下,请求方法不再是返回一个Call对象了,而是返回一个被观察者Observable,加上下面这行代码:
addCallAdapterFactory(RxJava2CallAdapterFactory.create())
但是发现我怎么发现我没有这个类呢?,原来是我那个rxjava适配器的包没有使用大神的// compile 'com.squareup.retrofit2:adapter-rxjava:2.3.0'
compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0-RC3'使用下面的使用rxjava方式请求一个get,方法的定义@GET("api/data/{Android}/{size}/{page}")
Observable<ResponseBody> getInfoDataByRxjava(@Path(value = "Android",encoded = true) String Android, @Path(value = "size",encoded = true) String size, @Path(value = "page",encoded = true) int page);实现
public void get_by_rxjava(View view){ OkHttpClient httpClient = new OkHttpClient(); if (BuildConfig.DEBUG) { HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BODY); httpClient = new OkHttpClient.Builder().addInterceptor(logging).build(); } Retrofit retrofit = new Retrofit.Builder() .client(httpClient) .baseUrl("http://gank.io") .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build(); KSService service = retrofit.create(KSService.class); Observable<ResponseBody> observable = service.getInfoDataByRxjava("Android","10",1); observable .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Consumer<ResponseBody>(){ @Override public void accept(ResponseBody responseBody) throws Exception { String s = responseBody.string(); Log.e(TAG,"请求的结果:"+s); tv.setText(s); } }); }log:
关于retrofit+rxjava就是这么使用的,只是我们原来单独使用rxjava的是通过操作符create返回一个Observable,现在不用 了在retrofit中,还有就是我们之前是去调异步或者同步的方法去请求网络,使用了额rxjava就不用去掉了,
添加网络日志信息
前面我打印出来的log,如果不做什么处理,是没有这些log的,在retrofit要设置,请求的和返回的log才会打印出来,特别是我们的开发阶段,一定要打log,又可能一个问题,看log就直接解决了,比如后面返回的json数据一个类型出错了,就会导致你转bean抛错误了,在此一定别忘记了添加依赖
compile 'com.squareup.okhttp3:logging-interceptor:3.9.1
关于拦截器都是在OkhttpClient中去设置的,OkHttpClient httpClient = new OkHttpClient();
if (BuildConfig.DEBUG) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
httpClient = new OkHttpClient.Builder().addInterceptor(logging).build();
}它有四个等级,看下源码就知道了.
public enum Level { /** No logs. */ NONE, /** * Logs request and response lines. * * <p>Example: * <pre>{@code * --> POST /greeting http/1.1 (3-byte body) * * <-- 200 OK (22ms, 6-byte body) * }</pre> */ BASIC, /** * Logs request and response lines and their respective headers. * * <p>Example: * <pre>{@code * --> POST /greeting http/1.1 * Host: example.com * Content-Type: plain/text * Content-Length: 3 * --> END POST * * <-- 200 OK (22ms) * Content-Type: plain/text * Content-Length: 6 * <-- END HTTP * }</pre> */ HEADERS, /** * Logs request and response lines and their respective headers and bodies (if present). * * <p>Example: * <pre>{@code * --> POST /greeting http/1.1 * Host: example.com * Content-Type: plain/text * Content-Length: 3 * * Hi? * --> END POST * * <-- 200 OK (22ms) * Content-Type: plain/text * Content-Length: 6 * * Hello! * <-- END HTTP * }</pre> */ BODY }然后设置给retrofit
Retrofit retrofit = new Retrofit.Builder() .client(httpClient) .baseUrl("http://gank.io") .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build();第2行就是了.body级别的log包含请求头,响应头,以及请求数据都会打印出来。
URL统一追加参数public class AppendUrlParamInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();//拿到发送的请求
HttpUrl httpUrl = request.url().newBuilder().addQueryParameter("version","1.0")
.addQueryParameter("channel","360")
.addQueryParameter("versionCode","10")
.addQueryParameter("deviceId","abasersgdhdjjfs5688")
.build();
//重要一步 使用新的url构建一个Request发送给后台
Request newRequest = request.newBuilder().url(httpUrl).build();
return chain.proceed(newRequest);
}
}添加什么拦截都是实现InterceptorInterceptor接口,然后去里面拿到Request去按需求做你的操作,大部分都是套路代码,写一个遍会了就会了,最后别忘记了把这个拦截器设置给OkHttpClient完整的代码贴下:public void append_url_by_interceptor(View view){
OkHttpClient httpClient = new OkHttpClient();
if (BuildConfig.DEBUG) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
//统一在url后面添加参数
AppendUrlParamInterceptor appendUrlParamInterceptor = new AppendUrlParamInterceptor();
httpClient = new OkHttpClient.Builder().addInterceptor(logging).addInterceptor(appendUrlParamInterceptor).build();
}
Retrofit retrofit = new Retrofit.Builder() .client(httpClient) .baseUrl("http://gank.io") .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build();
KSService service = retrofit.create(KSService.class);
Observable<ResponseBody> observable = service.getInfoDataByRxjava("Android","10",1);
observable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<ResponseBody>(){
@Override
public void accept(ResponseBody responseBody) throws Exception {
String s = responseBody.string();
Log.e(TAG,"请求的结果:"+s);
}
});
}现在查看log,到底有没有成功
刚才是用get请求方式,那么post请求方式可以么,试试就知道了,
现在定义一个方法去请求我们服务器@FormUrlEncoded
@POST("aa/HelloServlet")
Observable<ResponseBody> appendUrlParamByInterceptor(@FieldMap Map<String,String> map);实现
public void append_url_by_post(View view){ OkHttpClient httpClient = new OkHttpClient(); if (BuildConfig.DEBUG) { HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BODY); //统一在url后面添加参数 AppendUrlParamInterceptor appendUrlParamInterceptor = new AppendUrlParamInterceptor(); httpClient = new OkHttpClient.Builder().addInterceptor(logging).addInterceptor(appendUrlParamInterceptor).build(); } Retrofit retrofit = new Retrofit.Builder() .client(httpClient) .baseUrl("http://192.168.1.107:8080/") .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build(); KSService service = retrofit.create(KSService.class); Map<String,String> map = new HashMap<>(); map.put("username","zhouguizhi"); map.put("pwd","123456"); Observable<ResponseBody> observable = service.appendUrlParamByInterceptor(map); observable .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Consumer<ResponseBody>(){ @Override public void accept(ResponseBody responseBody) throws Exception { String json = responseBody.string(); Log.e(TAG,"请求的结果:"+json); } }); }看下我服务端端额控制台信息
然后看下客户端的log:
可以看到是post请求,然后是第一次url请求地址也出来了,拦截了后,我们追加的请求参数也打印出来了,
Header统一添加参数
还是用刚才的那个请求,统一添加请求头自定义拦截器如下:public class AppendHeaderParamInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();//拿到发送的请求
Headers headers = request.headers().newBuilder().add("token","mytoken").build();
Request newRequest = request.newBuilder().headers(headers).build();
return chain.proceed(newRequest);
}
}实现:
public void append_header(View view){ OkHttpClient httpClient = new OkHttpClient(); if (BuildConfig.DEBUG) { HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); AppendHeaderParamInterceptor header = new AppendHeaderParamInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BODY); //统一添加请求header httpClient = new OkHttpClient.Builder().addInterceptor(logging) .addInterceptor(header) .build(); } Retrofit retrofit = new Retrofit.Builder() .client(httpClient) .baseUrl("http://192.168.1.107:8080/") .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build(); KSService service = retrofit.create(KSService.class); Map<String,String> map = new HashMap<>(); map.put("username","zhouguizhi"); map.put("pwd","123456"); Observable<ResponseBody> observable = service.appendUrlParamByInterceptor(map); observable .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Consumer<ResponseBody>(){ @Override public void accept(ResponseBody responseBody) throws Exception { String json = responseBody.string(); Log.e(TAG,"请求的结果:"+json); } }); }观察我服务端打印的请求header头数据
现在看客户端的log:
无网络统一处理
之前项目无网络都是每个接口手动去判断,如果你做了封装的话还好点,至少只要判断一次就行了,而不是等请求发送出去了以后,回来发现给你一个提示无网络或者什么的,我们在请求之前就统一检查网络并处理
还是和以前一样,自定义拦截器,public class HandleNoNetInterceptor implements Interceptor {
private Context content;
public HandleNoNetInterceptor(Context content) {
this.content = content;
}
@Override
public Response intercept(Chain chain) throws IOException {
if(NetUtils.isNetworkAvalible(content)){
return chain.proceed(chain.request());
}else{
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(content,"网络异常,请检查你的网络",Toast.LENGTH_LONG).show();
}
});
throw new RuntimeException("网络异常了~~~~~");
}
}
}因为我们在发送网络请求的时候,是放在子线程中,所以你不使用handler post这个toast是无法出来的,如果是使用rxjava,他有个onError在这里可以做针对你的需求做处理,
OkHttpClient httpClient = new OkHttpClient(); if (BuildConfig.DEBUG) { HandleNoNetInterceptor handleNoNetInterceptor =new HandleNoNetInterceptor(this); HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BODY); //统一添加请求header httpClient = new OkHttpClient.Builder() .addInterceptor(logging) .addInterceptor(handleNoNetInterceptor) .build(); } Retrofit retrofit = new Retrofit.Builder() .client(httpClient) .baseUrl("http://192.168.1.107:8080/") .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build(); KSService service = retrofit.create(KSService.class); Map<String,String> map = new HashMap<>(); map.put("username","zhouguizhi"); map.put("pwd","123456"); Observable<ResponseBody> observable = service.appendUrlParamByInterceptor(map); observable .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<ResponseBody>() { @Override public void onSubscribe(@NonNull Disposable d) { } @Override public void onNext(@NonNull ResponseBody responseBody) { } @Override public void onError(@NonNull Throwable e) { Log.e(TAG,"异常消息:"+e.getLocalizedMessage()); } @Override public void onComplete() { } });千万别忘记了添加权限:
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
缓存设置
也是要自定义拦截器的,public class CacheInterceptor implements Interceptor {
private Context context;
public CacheInterceptor(Context context) {
this.context = context;
}
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
//这是在没网的时候设置缓存
if(!NetUtils.isNetworkAvalible(context.getApplicationContext())){
Request cacheRequest = request.newBuilder()
.cacheControl(CacheControl.FORCE_CACHE)
.build();
return chain.proceed(cacheRequest);
}
Response cacheResponse = chain.proceed(request);
long cacheMaxTime = 1*60*60;//缓存1小时
return cacheResponse.newBuilder()
.header("Cache-Control", "public, only-if-cached, max-stale="+cacheMaxTime)
.removeHeader("Pragma")
.build();
}
}
我这是请求http://www.qq.com,这个缓存必须要服务器支持,如果服务器不支持的话,是会报错的,
HTTP 504 Unsatisfiable Request (only-if-cached)我刚开始就遇到了这个问题,网上查了下.
请求的方法@GET
Observable<ResponseBody> request_qq(@Url String url);最终的实现
public void cache_set(View view){ OkHttpClient httpClient = new OkHttpClient(); if (BuildConfig.DEBUG) { HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BODY); long cacheSize = 1024*1024*6; Cache cache = new Cache(new File(FileUtil.getPath(),"cache_content"),cacheSize); httpClient = new OkHttpClient.Builder().cache(cache) .addInterceptor(logging) .addInterceptor(new CacheInterceptor(this)) .build(); } Retrofit retrofit = new Retrofit.Builder() .client(httpClient) .baseUrl("http://192.168.1.107:8080/") .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build(); KSService service = retrofit.create(KSService.class); Observable<ResponseBody> observable = service.request_qq("http://www.qq.com"); observable .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<ResponseBody>() { @Override public void onSubscribe(@NonNull Disposable d) { } @Override public void onNext(@NonNull ResponseBody responseBody) { } @Override public void onError(@NonNull Throwable e) { } @Override public void onComplete() { } }); }查看我缓存的文件,在sd下的cache_content文件:
打开缓存文件;
这是需要服务器支持缓存的,如果你服务器不支持缓存,但是你在没网的时候又想缓存的数据,就要自己搞了,
超时,错误重试机制
这个和okHttp一样的,废话本来底层网络就是用okhttp..connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(15,TimeUnit.SECONDS)
.writeTimeout(15,TimeUnit.SECONDS)
.retryOnConnectionFailure(true)还有个多文件上传,还没弄,这个后期补上,就写到这里吧.
相关文章推荐
- Android 网络框架 Retrofit2.0介绍、使用和封装
- Android 网络框架 Retrofit2.0介绍、使用和封装
- Retrofit 2.0使用详解,配合OkHttp、Gson,Android最强网络请求框架
- Retrofit 2.0使用详解,配合OkHttp、Gson,Android最强网络请求框架
- Android开源网络请求框架Retrofit的介绍使用
- Android中网络框架Retrofit2.0简单使用
- Android 网络请求框架Retrofit2.0使用笔记
- Android框架学习之Retrofit(二)RxJava和Retrofit2.0的结合使用
- Android 网络请求框架 Retrofit2.0实践使用总结
- Retrofit 2.0使用详解,配合OkHttp、Gson,Android最强网络请求框架
- KJFrameForAndroid 2.0框架使用
- Android Retrofit 2.0框架上传图片解决方案(一张与多张的处理)
- 6.3 强大的网络请求第三方框架 Retrofit 的介绍和使用<未完待翻译>
- Android开源框架Universal-Image-Loader基本介绍和使用
- Ext2.0框架的Grid使用介绍
- Android Retrofit 2.0框架 GET和POST的实现方式(配合RxJava)
- android开源框架android-async-http详细的使用介绍
- Android 使用Retrofit2.0 + OKHttp 实现 HTTP协议请求
- Android Retrofit框架的使用和个人见解
- android开源框架android-async-http使用案例介绍