參考資料:
http://blog.csdn.net/u014165119/article/details/49280779
https://github.com/square/okhttp/tree/master/samples OkHttp的Sample
http://blog.csdn.net/ljd2038/article/details/51189334 文件下載案例
系列文章資料:
這是我對Retorfit2.0源碼的一些解讀,有問題希望大家指正
Retrofit2.0中的Builder模式
Retrofit2.0中的動態代理模式
Retrofit2.0中的適配器模式
Retrofit2.0中的策略模式
一、為什么使用Retrofit2.0
(1).Retrofit2.0是由Square公司開發的一個網絡請求框架,已經成為Android開發中非?;馃岬囊粋€庫。是Square公司對OkHttp進行再次封裝的框架。
Google在大概Android4.3(具體不太清楚了)的時候已經不推薦使用HttpClient ,改為用推薦使用OkHttp,在6.0的時候更是刪除了HttpClient,Retrofit作為Square公司推出的對Okhttp再次封裝得庫,無疑對OkHttp支持的更好。對比現在主流的網絡框架Volly,Volly針對不同的返回類型,需要調用不同的方法,傳入參數也非常麻煩,而Retrofit在調用一個接口時,就像是在調用一個本地方法。同時Retrofit采用鏈式編程的方式,讓代碼的邏輯結構更加清晰。Retrofit非常靈活,可以做到動態的去配置Url。同時它非常適合RESTFUL API的形式。
示例服務端代碼:
@Controller
@EnableWebMvc
@RequestMapping(headers="Accept=*/*",
produces="application/json")
public class LoginController {
@Resource
LoginService service;// 自動依賴注入
@Resource
LoginEntity userEntity;
@Resource
ResultEntity resultEntity;
@RequestMapping(value = "login", method = RequestMethod.GET,produces = "application/json",headers="Accept=*/*")
@ResponseBody
public
ResultEntity login(HttpServletRequest
request,HttpServletResponse response) {
//拿到服務器端的參數
String username = request.getParameter("username");
String password = request.getParameter("password");
if (service.isExitsPeople(username, password)==1) {
userEntity.setUsername(username);
userEntity.setPassword(password);
resultEntity.setIsSuccess(true);
resultEntity.setMessage("登錄成功");
resultEntity.setResult(userEntity);
return resultEntity;
}else{
resultEntity.setIsSuccess(false);
resultEntity.setMessage("登錄失敗");
resultEntity.setResult("");
return resultEntity;
}
//System.out.println("測試login方法是否成功" + service.getUserCount(username, password));
// System.out.println("測試MyBatis使用是否成功" + service.isExitsPeople(username, password));
}
}
示例Retrofit api的代碼
示例客戶端API代碼:
public interface LoginService {
@FormUrlEncoded
@POST("Member/Login")
Call<UserModel>
loginApp(@Field("username") String name, @Field("password")
String pwd
}
(2).它的請求速度非???br>
圖片來自:
http://blog.csdn.net/xiongge358/article/details/50990864
示例代碼:
public
class RetrofitClient {
private Retrofit client;
private String BaseUrl;
/**
* 初始化Retrofit的內容
*/
public RetrofitClient(String BaseUrl) {
this.BaseUrl = BaseUrl;
client = new Retrofit.Builder()
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(
.baseUrl(BaseUrl)
.build();
}
public SmileAPI getSmileAPI() {
return client.create(SmileAPI.class);
}
public WeatherAPI getWeatherAPI() {
return client.create(WeatherAPI.class);
}
}
二、配置Retrofit2.0
在Moudel的gradle中配置
compile'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
同樣Retrofit也可以對OkHttp進行自己的定制,所以如果需要定制OkHttp同樣可以引入Okhttp的庫
compile 'com.squareup.okhttp3:okhttp:3.3.1'
三、使用Retrofit2.0
1.配置Retrofit
***baseUrl:****
- 默認的服務器地址,Retrofit在進行請求一個接口時會根據你填寫的 baseurl+方法名 去請求。
****addConverterFactory:****
添加返回數據的解析方式,Retrofit支持多種格式的解析,xml,json,jackson,Moshi,Protobuf,wire等,添加對這些數據格式的支持同樣需要在Gradle中進行依賴。這些依賴Square公司同樣提供了支持。
這里是Square提供的官方Converter modules列表。選擇一個最滿足你需求的。
Gson: com.squareup.retrofit:converter-gson
Jackson: com.squareup.retrofit:converter-jackson
Moshi: com.squareup.retrofit:converter-moshi
Protobuf: com.squareup.retrofit:converter-protobuf
Wire: com.squareup.retrofit:converter-wire
Simple XML: com.squareup.retrofit:converter-simplexml
****CallAdapter:****
Retrofit會將網絡請求的接口以回調的方式返回,我們通常會得到一個叫做Call<類型>的結果,如果我們需要返回其他的結果,例如RxJava形式的結果,需要對RxJava做支持。如果不需要特殊類型的返回結果,我們是不需要配置的。
****Client:****
通過該方法我們可以添加自己定制的OkHttp。
定制OkHttp
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.retryOnConnectionFailure(true)
.connectTimeout(15, TimeUnit.SECONDS)
.addNetworkInterceptor(mTokenInterceptor)
.build();
//將定制的OkHttp加入到Retrofit中
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(AppConfig.BASE_URL)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.client(client)
.build();
```
***Create:***
如果一切都配置好了,那么通過調用Create,并傳入你的接口方法類型,就可以請求服務端了。
使用Retrofit
####一、 定義請求的接口
Retrofit非常適合Restful API,它采用注解,加動態代理的方式去請求一個服務。
1. **定義你要請求的接口**
因為Retrofit采用動態代理的方式去請求一個接口,所以在定義我們需要請求的接口時,我們需要定義一個接口,并添加相應的注解,告訴Retrofit如何請求我們需要的接口。參數的返回類型默認情況下需要通過Call方法進行包裝。
```
public interface LoginService {
@FormUrlEncoded
@POST("Member/Login")
Call<UserModel>loginApp(@Field("username") String name, @Field("password")String pwd ,@Field("grid") String grid);
}
```
####二、 Retrofit的注解
**方法上的注解**
1.請求的類型
```
@POST
@GET
參數:請求的方法,對應接口文檔的方法名。
```
2.添加靜態的頭部協議
```
@Headers
例如:@Headers("Cache-Control: max-age=640000")
```
3.數據提交的方式
```
@FormUrlEncoded
添加該注解后,將會發送form-encode的數據類型
```
4.數據提交的方式
```
@Multipart
添加該注解后,將會發送Mulitipar類型的數據。
```
**參數注解**
```
1.@Part:如果你在方法注解上加入了@Multipart,那么對應的方法參數必須要用該注解
```
```
2.@Field:如果你在方法注解上加入了@FormUrlEncoded注解,那么對應的方法參數必須用該注解。
```
```
3. @Body:如果傳遞的是一個序列化的參數
例如:@POST("users/new")
Call<User> createUser(@Body User user);
```
**Retrofit請求的Url同樣是可以動態配置的。請求的URL可以在函數中使用替換塊和參數進行動態更新,替換塊是{ and }包圍的字母數字組成的字符串。
**
```
3.@Path :Url中有一段是需要動態配置的時候
例如:
@GET("group/{id}/users")
List<User>
groupList(@Path("id") int groupId);
```
```
4.@Query 查詢參數
例如:
@GET("group/{id}/users")
```
```
List<User> groupList(@Path("id") int groupId,
@Query("sort") String sort);
```
5.復雜的參數同樣可以通過Map方式
```
例如:
@GET("group/{id}/users")
List<User>
groupList(@Path("id") int groupId, @QueryMap Map<String,
String> options);
```
####四、處理Call返回的參數
1. 如果我們定義好了接口。那么這個時候就可以調用create方法,并且傳入我們需要請求的接口類型,拿到返回值了。
使用Call:
例子:
```
MyService service =retrofit.create(MyService.class);
Call<ResultModel<UserInfoModel>>call = service.loginApp("","");
Service.loginApp:就是執行我們定義的方法,并且拿到返回值。
```
2. 接著我們使用Call:
```
call.enqueue(new Callback<Repo>() {
@Override
public void onResponse(Response<UserInfoModel response) {
// 通過reponse拿到結果
UserModelInfo info=response.body();
}
@Override
public void onFailure(Throwable t) {
}
});
```
####五、Retrofit實現文件上傳(方式1)
1.使用@Mulitpart注解標記接口
2.@Part 注解標記文件類型的參數
3.MultipartBody.Part 標記文件的參數
```
public interface FileUploadService {
@Multipart
@POST("upload")
Call<ResponseBody> upload(@Part("description") RequestBody description,
@Part MultipartBody.Part file);
}
```
4.使用RequestBody包裝上傳文件
```
//構建要上傳的文件
File file = new File(filename);
RequestBody requestFile =RequestBody.create(MediaType.parse("multipart/form-data"),file);
```
***注:MediaType.parse 參數代表上傳文件的類型(Content-Type)的類型,例如:如果是上傳的是圖片,可以寫成MediaType.parse("image/jpg")***
5.使用MultipartBody.Part對Request再次包裝
```
//構建要上傳的文件
參數1:對應的接口參數
參數2:上傳的文件名稱
參數3:包裝的RequestBody
MultipartBody.Part body = MultipartBody.Part.createFormData("aFile", file.getName(),
requestFile);
```
6.調用上傳方法:
```
Call<ResponseBody> call = service.upload(description,body);
```
####六、Retrofit實現文件上傳(方式2)
1.通過定義自己的FileConvertFactory,我們可以將File類型,不通過包裝,作為參數傳入。
```
public interface FileUploadService {
@Multipart
@POST("upload") //注意這里的參數 "aFile" 之前是在創建MultipartBody.Part 的時候傳入的
Call<ResponseBody> upload(@Part("description") RequestBody description,
@Part("aFile") File file);
}
```
####7.使用ConvertFactory進行上傳
繼承Converter.Factory :
```
static class FileRequestBodyConverterFactory extends Converter.Factory {
@Override
public Converter<File, RequestBody> requestBodyConverter(Type type, Annotation[]
parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
return new FileRequestBodyConverter();
}
}
static class FileRequestBodyConverter implements Converter<File, RequestBody> {
@Override
public RequestBody convert(File file) throws IOException {
return RequestBody.create(MediaType.parse("application/otcet-stream"),file);
}
}
```
2.在配置Retrofit時加入我們自己的文件類型支持
```
.addConverterFactory(new
FileRequestBodyConverterFactory())
```
####七、文件下載
使用Okhttp的ReponseBody做為返回類型
示例代碼:
```
Call<ResponseBody> call =mService.getFile();
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody>
response) {
try {
writeResponseBodyToDisk(MainActivity.this, response.body());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void
onFailure(Call<ResponseBody> call, Throwable t) {
Log.e("H", "result
: " + t.toString());
}
});
```
ReponseBody會拿到文件流,對拿到的文件流做處理
示例代碼:
```
public boolean writeResponseBodyToDisk(Context context, ResponseBody body) throws IOException {
Log.d("tag","開始下載"+"/"+body.contentType().toString());
boolean flag = true;
String type =
body.contentType().toString();
if (type.equals(".apk")) {
mFileSuffix = ".apk";
} else if (type.equals(".png")) {
mFileSuffix = ".png";
}
else if(type.equals(".jpg")) {
mFileSuffix = ".jpg"; }
if (type.equals(".zip")) {
mFileSuffix = ".zip";
}
// 其他同上 自己判斷加入
final String name ="guo"+System.currentTimeMillis() + mFileSuffix;
final String path =Environment.getExternalStorageDirectory() + File.separator + name;
/**
* 寫入流
*/
InputStream is = body.byteStream();
File file = new File(path);
FileOutputStream fos = new FileOutputStream(file);
BufferedInputStream bis = new BufferedInputStream(is);
byte[] buffer = new byte[1024];
int len;
while ((len = bis.read(buffer)) !=-1) {
fos.write(buffer, 0, len);
fos.flush();
}
fos.close();
bis.close();
is.close();
Log.d("tag","下載完畢");
return flag;
}
```