Retrofit的使用

来源:互联网 发布:网络电视破解版apk 编辑:程序博客网 时间:2024/06/05 03:58

本文参考网络其他文章,总结出来。

Retrofit

Github:https://github.com/square/retrofit
官网教程:http://square.github.io/retrofit/

Retrofit和Java领域的ORM概念类似, ORM把结构化数据转换为Java对象,而Retrofit 把REST API返回的数据转化为Java对象方便操作。同时还封装了网络代码的调用。

ORM
对象关系映射(英语:Object Relational Mapping,简称ORM,或O/RM,或O/R mapping),是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。从效果上说,它其实是创建了一个可在编程语言里使用的“虚拟对象数据库”。
对象关系映射(Object-Relational Mapping)提供了概念性的、易于理解的模型化数据的方法。ORM方法论基于三个核心原则: 简单:以最基本的形式建模数据。
 传达性:数据库结构被任何人都能理解的语言文档化。
 精确性:基于数据模型创建正确标准化的结构。
 典型地,建模者通过收集来自那些熟悉应用程序但不熟练的数据建模者的人的信息开发信息模型。建模者必须能够用非技术企业专家可以理解的术语在概念层次上与数据结构进行通讯。建模者也必须能以简单的单元分析信息,对样本数据进行处理。ORM专门被设计为改进这种联系。
简单的说:ORM相当于中继数据。具体到产品上,例如ADO.NET Entity Framework。DLINQ中实体类的属性[Table]就算是一种中继数据。
ORM具体详解:http://blog.csdn.net/sgear/article/details/7408251

简介

Retrofit 是一个Square开发的类型安全的REST安卓客户端请求库。这个库为网络认证、API请求以及用OkHttp发送网络请求提供了强大的框架 。Retrofit 可以利用接口,方法和注解参数来声明式定义一个请求应该如何被创建。并且可更换或自定义HTTP client,以及可更换或自定义Converter,返回数据解析方式。Retrofit可用于Android和Java的一个类型安全(type-safe)的REST客户端,如果你的服务器使用的使RESTAPI,那么你将非常适合使用它。

使用

 <!--权限 网络-->
    <uses-permission android:name="android.permission.INTERNET"/>
依赖gson与retrofit
dependencies {
    compile 'com.squareup.retrofit2:converter-gson:2.0.0'
    compile 'com.squareup.retrofit2:retrofit:2.1.0'

在使用Retrofit前,我们需要先创建Retrofit实例,并且做一系列配置,然而Retrofit设计的也是非常好,这些配置都是可插拔的:
Retrofit retrofit =new Retrofit.Builder()
//设置baseUrl,注意baseUrl 应该以/ 结尾。
.baseUrl("http://news-at.zhihu.com/api/4/")
//使用Gson解析器,可以替换其他的解析器
.addConverterFactory(GsonConverterFactory.create())
//设置OKHttpClient,如果不设置会提供一个默认的
.client(new OkHttpClient())
// .client(new UrlConnectionClient())
// .client(new ApacheClient())
// .client(new CustomClient())
.build();

更换HTTP client与Converter

Retrofit 背后的 HTTP client,以及序列化机制(JSON/XML 协议)等都是可以替换,因此你可以选择自己合适的方案。
Retrofit 最早出来的时候,只支持 Apache 的 HTTP client。后来增加了 URL connection,以及 OkHttp 的支持。如果你想使用其他的 HTTP client,可以通过以下方式了替换,或者更改为自定义的HTTP client:
//设置OKHttpClient,如果不设置会提供一个默认的OkHttpClient
.client(new OkHttpClient())
// .client(new UrlConnectionClient())
// .client(new ApacheClient())
// .client(new CustomClient())

序列化功能也是可替换的。默认是用的 GSON,你当然也可以用 Jackson 来替换掉。

//使用Gson解析器,可以替换其他的解析器 .addConverterFactory(GsonConverterFactory.create())//当需要返回原始String数据时 .addConverterFactory(ScalarsConverterFactory.create())
除此之外Retrofit还提供以下几种Converter:
  • Gson: com.squareup.retrofit2:converter-gson
  • Jackson: com.squareup.retrofit2:converter-jackson
  • Moshi: com.squareup.retrofit2:converter-moshi
  • Protobuf: com.squareup.retrofit2:converter-protobuf
  • Wire: com.squareup.retrofit2:converter-wire
  • Simple XML: com.squareup.retrofit2:converter-simplexml
  • Scalars (primitives,boxed,andString): com.squareup.retrofit2:converter-scalars

//当需要返回原始String数据时,当你返回的类型不是json是记得更换Converter,
所以当你返回类型有变化是就要更换Converter,这个Retrofit有点坑,应该有个默认String的返回。 .addConverterFactory(ScalarsConverterFactory.create())
简单使用
publicinterface IUserBiz{ @GET("users") Call<List<User>> getUsers();}

Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://192.168.31.242:8080/springmvc_users/user/") .addConverterFactory(GsonConverterFactory.create()) .build();IUserBiz userBiz = retrofit.create(IUserBiz.class);Call<List<User>>call = userBiz.getUsers();call.enqueue(new Callback<List<User>>() { @Overridepublic void onResponse(Call<List<User>>call, Response<List<User>> response) { Log.e(TAG,"normalGet:" + response.body() +""); } @Override public void onFailure(Call<List<User>>call, Throwable t) { } });

URL拼写注意



综上我们总结 对于 Retrofit 2.0中新的URL定义方式,这里是我的建议:
  • Base URL: 总是以 /结尾

  • @Url: 不要以 / 开头

而且在Retrofit 2.0中我们还可以在@Url里面定义完整的URL:
publicinterface IUserBiz{ @POST("http://192.168.31.242:8080/springmvc_users/user/users") Call<List<User>> getUsers();}
这种情况下Base URL会被忽略,适合一个项目会出现多个BaseUrl的情况。

Call是Retrofit中重要的一个概念,代表被封装成单个请求/响应的交互行为

通过调用Retrofit2的execute(同步)或者enqueue(异步)方法,发送请求到网络服务器,并返回一个响应(Response)。

  1. 独立的请求和响应模块
  2. 从响应处理分离出请求创建
  3. 每个实例只能使用一次。
  4. Call可以被克隆。
  5. 支持同步和异步方法。
  6. 能够被取消。

由于call只能被执行一次,所以按照上面的顺序执行会得到如下错误。

java.lang.IllegalStateException: Already executed

我们可以通过clone,来克隆一份call,从新调用。

// cloneCall<List<Contributor>> call1 = call.clone();// 5. 请求网络,异步call1.enqueue(new Callback<List<Contributor>>() { @OverridepublicvoidonResponse(Response<List<Contributor>> response, Retrofit retrofit) { Log.d(TAG, "response:" + response.body().toString()); } @OverridepublicvoidonFailure(Throwable t) { }});

固定查询参数

interface SomeService {
@GET("/some/endpoint?fixed=query") Call<SomeResponse> someEndpoint();}
Call<SomeResponse> someEndpoint();
}
// 方法调用
someService.someEndpoint();
动态参数
interface SomeService {
@GET("/some/endpoint") Call<SomeResponse> someEndpoint(
Call<SomeResponse> someEndpoint(
@Query("dynamic") String dynamic);
}
// 方法调用
someService.someEndpoint("query");

动态参数(Map)

interface SomeService {
@GET("/some/endpoint") Call<SomeResponse> someEndpoint(
Call<SomeResponse> someEndpoint(
@QueryMap Map<String, String> dynamic);}
}// 方法调用
someService.someEndpoint( Collections.singletonMap("dynamic","query"));

省略动态参数

interface SomeService {
@GET("/some/endpoint") Call<SomeResponse> someEndpoint(
Call<SomeResponse> someEndpoint(
@Query("dynamic") String dynamic);}
}
// 方法调用
someService.someEndpoint(null);

固定+动态参数

interface SomeService {
@GET("/some/endpoint?fixed=query") Call<SomeResponse> someEndpoint(
Call<SomeResponse> someEndpoint(
@Query("dynamic") String dynamic);}
}
// 方法调用
someService.someEndpoint("query");

路径替换

interface SomeService {
@GET("/some/endpoint/{thing}") Call<SomeResponse> someEndpoint(
Call<SomeResponse> someEndpoint(
@Path("thing") String thing);}someService.someEndpoint(
}
someService.someEndpoint("bar");

固定头

interface SomeService {
@GET("/some/endpoint")
@Headers("Accept-Encoding: application/json") Call<SomeResponse> someEndpoint();}someService.someEndpoint();
Call<SomeResponse> someEndpoint();
}
    someService.someEndpoint();

动态头

interface SomeService {
@GET("/some/endpoint") Call<SomeResponse> someEndpoint(
Call<SomeResponse> someEndpoint(
@Header("Location") String location);}someService.someEndpoint(
}
someService.someEndpoint("Droidcon NYC 2015");

固定+动态头

interface SomeService {
@GET("/some/endpoint")
@Headers("Accept-Encoding: application/json") Call<SomeResponse> someEndpoint(
Call<SomeResponse> someEndpoint(
@Header("Location") String location);}someService.someEndpoint(
}
someService.someEndpoint("Droidcon NYC 2015");

Post请求,无Body

interface SomeService {

@POST("/some/endpoint") Call<SomeResponse> someEndpoint();}someService.someEndpoint();
Call<SomeResponse> someEndpoint();
}
someService.someEndpoint();

Post请求有Body

interface SomeService {
@POST("/some/endpoint") Call<SomeResponse> someEndpoint(
Call<SomeResponse> someEndpoint(
@Body SomeRequest body);}someService.someEndpoint();
}
someService.someEndpoint();

表单编码字段

interface SomeService {
@FormUrlEncoded@POST("/some/endpoint") Call<SomeResponse> someEndpoint(
Call<SomeResponse> someEndpoint(
@Field("name1") String name1,
@Field("name2") String name2);}someService.someEndpoint(
}
someService.someEndpoint("value1","value2");

表单编码字段(Map)

interface SomeService {
@FormUrlEncoded@POST("/some/endpoint") Call<SomeResponse> someEndpoint(
Call<SomeResponse> someEndpoint(
@FieldMap Map<String, String> names);}someService.someEndpoint(
}
someService.someEndpoint(
// ImmutableMap是OKHttp中的工具类
ImmutableMap.of("name1","value1","name2","value2"));

动态Url(Dynamic URL parameter)

interface GitHubService {
@GET("/repos/{owner}/{repo}/contributors") Call<List<Contributor>> repoContributors(
Call<List<Contributor>> repoContributors(
@Path("owner") String owner,
@Path("repo") String repo);
@GET
Call<List<Contributor>> repoContributorsPaginate(
@Url String url);}
}
// 调用
Call<List<Contributor>> call = gitHubService.repoContributors("square","retrofit");Response<List<Contributor>> response = call.execute();
Response<List<Contributor>> response = call.execute();

文件上传

在我们开发当中肯定必不可少图片上传了,我们使用表单上传文件时,必须让 <form> 表单的 enctyped 等于multipart/form-data。 文件上传我们需要使用@MultiPart@Part ,MultiPart意思就是允许多个@Part多部分上传。

@Multipart@POST("test/upload")Call<ResultBean>upload(@Part("file\"; filename=\"launcher_icon.png") RequestBody file);
值得注意的是我们需要在@Part指定file和filename的值,避免一些不必要的麻烦。

相应的我们在使用retrofit的时候,首先先获取到文件,并且创建RequestBody实例,然后调用接口请求,相应代码块如下:
File file = new File(getExternalFilesDir(null),"launcher_icon.png");
RequestBody fileBody = RequestBody.create(MediaType.parse("image/png"), file);
Call<ResultBean> doubanCall = myTestApiService.upload(fileBody);
doubanCall.enqueue(new Callback<ResultBean>() { @Override public void onResponse(
@Override
public void onResponse(Call<ResultBean>call, Response<ResultBean> response) { if (response
if (response.isSuccessful()) { Log
Log.d(TAG, response.body().toString());
resultTextView.setText("" + response.body().toString());
}
}
@Override
public void onFailure(Call<ResultBean>call, Throwable t) {// Log
// Log.d(TAG, response.body().toString());
resultTextView.setText("" + "error:" + t.getMessage());
}
});


大文件下载

文件下载我们需要使用@Url和 @Streaming ,@Url动态Url正好非常适合我们的场景,而使用@Streaming注解可以让我们下载非常大的文件时,避免Retrofit将整个文件读进内存,否则可能造成OOM现象。 
声明接口如下:
@Streaming
@GETCall<ResponseBody> downloadFileByDynamicUrlAsync(@UrlString downloadUrl);
需要注意的是我们需要使用Retrofitcall.execute同步获取ResponseBody,那么我们就需要放进一个单独的工作线程中:
new AsyncTask<Void, Long, Void>() { @Overrideprotected Void doInBackground(Void... voids) { Call<ResponseBody> call = myTestApiService.downloadFileByDynamicUrlAsync(API_BASE_URL.concat("/res/atom-amd64.deb"));try { Response<ResponseBody> response = call.execute(); boolean writtenToDisk = writeResponseBodyToDisk(response.body()); Log.d(TAG, "下载文件 " + writtenToDisk); } catch (IOException e) { e.printStackTrace(); } returnnull; } @OverrideprotectedvoidonPreExecute() { super.onPreExecute(); } }.execute();
最后我们需要将文件写入磁盘根目录中:
//写入到磁盘根目录privatebooleanwriteResponseBodyToDisk(ResponseBody body) { try { File futureStudioIconFile = new File(Environment.getExternalStorageDirectory() + File.separator + "atom.deb"); InputStream inputStream = null; OutputStream outputStream = null;try { byte[] fileReader = newbyte[4096];finallong fileSize = body.contentLength(); long fileSizeDownloaded = 0; inputStream = body.byteStream(); outputStream = new FileOutputStream(futureStudioIconFile); while (true) { int read = inputStream.read(fileReader); if (read == -1) { break; } outputStream.write(fileReader, 0, read); fileSizeDownloaded += read; Log.d(TAG, "file download: " + fileSizeDownloaded + " of " + fileSize); finallong finalFileSizeDownloaded = fileSizeDownloaded; runOnUiThread(new Runnable() { @Overridepublicvoidrun() { resultTextView.setText("file download: " + finalFileSizeDownloaded + " of " + fileSize); } }); } outputStream.flush(); returntrue; } catch (IOException e) { returnfalse; } finally { if (inputStream != null) { inputStream.close(); } if (outputStream != null) { outputStream.close(); } } } catch (IOException e) { returnfalse; } }












































原创粉丝点击