Retrofit與okhttp都是出於Square公司,Retrofit對okhttp做了一層包裝,把網絡請求都交給了okhttp(可以參考okhttp3使用),因此只需要簡單的配置,就能用Retrofit進行網絡請求。
一、引入retrofit 2.0
需要在項目下的build.gradle添加retrofit 2.0.2
的引用
// retrofit 庫
compile 'com.squareup.retrofit2:retrofit:2.0.2'
// 使用gson解析數據內容
compile 'com.squareup.retrofit2:converter-gson:2.0.2'
如上圖所示,引入retrofit 2.0.2
後,會在External Libraries
中看到retrofit-2.0.2
,okhttp-3.2.0
,okio-1.6.0
,這是因爲retrofit 2.0.2
封裝了okhttp-3.2.0
,而okhttp-3.2.0
又依賴於okio-1.6.0
,因此引入一個retrofit
庫,會自動添加其依賴的其他庫。
二、retrofit 2.0 基本使用
1、retrofit對象創建
Retrofit retrofit = new Retrofit.Builder()
// 設置基本url,通常是協議+IP地址+端口
.baseUrl(API_URL)
// 設置數據解析的格式,需要引入gson庫
.addConverterFactory(GsonConverterFactory.create())
.build();
2、retrofit網絡請求接口
retrofit將Http Api使用Java接口表示;
public interface GitHub {
@GET("/repos/{owner}/{repo}/contributors")
Call<List<Contributor>> contributors(
@Path("owner") String owner,
@Path("repo") String repo);
}
每一個Call
對象從GitHub接口裏的方法獲取,Call
基於Http的同步/異步方法想服務端獲取請求結果。
Call<List<Contributor>> call = github.contributors("square", "retrofit");
3、動態設置url的Get方法
retrofit 2.0允許動態設置 URL,需要使用一對大括號{}將需要動態替換的URL內容括起來,對應的參數使用相同的字符串,同時使用@Path
註解表示。
如,需要請求這個地址(https://api.github.com/repos/square/retrofit/contributors)的數據,可以添加請求接口:
public interface GitHub {
@GET("/repos/{owner}/{repo}/contributors")
Call<List<Contributor>> contributors(
@Path("owner") String owner,
@Path("repo") String repo);
}
在實際調用的時候,傳入正確的參數便可
Call<List<Contributor>> call = github.contributors("square", "retrofit");
這裏給出完整的動態設置Url的同步調用Get方法源碼;
/**
* 同步方法
*/
private void btn_synchronous_dynamically_getMethod() {
try {
new Thread(new Runnable() {
@Override
public void run() {
try {
// Create a very simple REST adapter which points the GitHub API.
Retrofit retrofit = new Retrofit.Builder()
// 設置基本url,通常是協議+IP地址+端口
.baseUrl(API_URL)
// 設置數據解析的格式,需要引入gson庫
.addConverterFactory(GsonConverterFactory.create())
.build();
// Create an instance of our GitHub API interface.
GitHub github = retrofit.create(GitHub.class);
// Create a call instance for looking up Retrofit contributors.
Call<List<Contributor>> call = github.contributors("square", "retrofit");
// Fetch and print a list of the contributors to the library.
List<Contributor> contributors = call.execute().body();
final StringBuilder sb = new StringBuilder();
for (Contributor contributor : contributors) {
sb.append(contributor.getLogin() + " (" + contributor
.getContributions() + ")\n");
}
Log.i(TAG, "tht synchronous function run on thread id is " + Thread
.currentThread().getId());
Log.i(TAG, sb.toString());
runOnUiThread(new Runnable() {
@Override
public void run() {
mToast.setText(sb.toString());
mToast.show();
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
} catch (Exception e) {
e.printStackTrace();
}
}
注:Android 4.0之後,不允許網絡請求在主線程中使用。
異步方法通過在Call的enqueue中添加結果回調來完成。如果需要獲取Http原始的Response
對象,使用Response raw = response.raw();
/**
* 異步方法
*/
private void btn_asynchronous_dynamically_getMethod() {
// Create a very simple REST adapter which points the GitHub API.
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
// Create an instance of our GitHub API interface.
GitHub github = retrofit.create(GitHub.class);
// Create a call instance for looking up Retrofit contributors.
Call<List<Contributor>> call = github.contributors("square", "retrofit");
// Fetch and print a list of the contributors to the library.
call.enqueue(new Callback<List<Contributor>>() {
@Override
public void onResponse(Call<List<Contributor>> call, Response<List<Contributor>>
response) {
try {
// get raw response
// Response raw = response.raw();
Log.i(TAG, "tht callback function run on thread id is " + Thread
.currentThread().getId());
StringBuilder sb = new StringBuilder();
List<Contributor> contributors = response.body();
for (Contributor contributor : contributors) {
sb.append(contributor.getLogin() + " (" + contributor
.getContributions() + ")\n");
}
mToast.setText(sb.toString());
mToast.show();
Log.i(TAG, sb.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onFailure(Call<List<Contributor>> call, Throwable t) {
}
});
}
4、retrofit 查詢參數
4.1、一個參數查詢
如果只有一個查詢參數的Get/Post請求,可以使用Query
註解;
比如針對鏈接(http://api.k780.com:88/?app=weather.future)請求,需要設置java請求接口爲:
@POST("/")
Call<WeatherList> getWeatherForAQuery(@Query("app") String queryParams);
注:參數查詢不能使用@POST(“/app={param}”),然後通過@Path註解動態傳遞數值。
4.2、多個參數查詢
如果查詢參數有多個,需要使用QueryMap
註解,參數通過Map<String,String>
對象傳遞。當然,也可以只使用Query
註解,不過需要添加多個Query
註解纔行;
public interface YourInterface {
@GET("your-url")
Call<responsebody> postcode(@Query("op") String op, @Query("postcode") String postcode);
}
下面演示對天氣Url(http://api.k780.com:88/?app=weather.future&weaid=1&&appkey=10003&sign=b59bc3ef6191eb9f747dd4e83c99f2a4&format=json)數據請求;
定義請求天氣的Java接口,如果是用Get請求,需要將@POST
改成@GET
,需要用到的WeatherList對象,可以再文章的最後下載demo;
public interface WeatherApi {
@POST("/")
Call<WeatherList> getWeatherForQueryMap(@QueryMap Map<String,String> options);
}
注:註解括號內容不能爲空,即使baseUrl寫成http://api.k780.com:88/,註解也不能爲 @POST(“”),否則運行出錯。
最終調用方法;
private void btn_asynchronous_queryParamete_getMethod() {
try {
Retrofit retrofit = new Retrofit.Builder().baseUrl("http://api.k780.com:88/")
.addConverterFactory(GsonConverterFactory.create()).build();
Map<String, String> options = new HashMap<String, String>();
options.put("app", "weather.future");
options.put("weaid", "1");
options.put("appkey", "10003");
options.put("sign", "b59bc3ef6191eb9f747dd4e83c99f2a4");
options.put("format", "json");
Call<WeatherList> result = retrofit.create(WeatherApi.class).getWeather2(options);
result.enqueue(new Callback<WeatherList>() {
@Override
public void onResponse(Call<WeatherList> call, Response<WeatherList> response) {
Log.i("MainActivity", new Gson().toJson(response.body()));
}
@Override
public void onFailure(Call<WeatherList> call, Throwable t) {
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
4.3、同名的多個查詢參數
有些情況下通過同名的多個參數請求,比如https://api.example.com/tasks?id=123&id=124&id=125 ,由於參數數據類型是一樣的,可以通過List<>
組合多個參數;
public interface TaskService {
@GET("/tasks")
Call<List<Task>> getTask(@Query("id") List<Long> taskIds);
}
5、發送json參數
當請求參數是個JSON字符串,可以使用@Body註解一個對象,Retrofit可以將一個對象轉化爲JSON字符串;
定義一個對象;
public class Task {
private long id;
private String text;
public Task(long id, String text) {
this.id = id;
this.text = text;
}
}
聲明請求數據接口;
public interface TaskService {
@POST("/tasks")
Call<Task> createTask(@Body Task task);
}
調用方法;
Task task = new Task(1, "my task title");
Call<Task> call = taskService.createTask(task);
call.enqueue(new Callback<Task>() {});
調用createTask
方法,把task
對象的屬性轉化爲JSON形式,對應的task
參數實際爲
{
"id": 1,
"text": "my task title"
}
5、文件上傳
retrofit的文件上傳,需要使用@Multipart
註解;
文件上傳接口定義;
public interface FileUploadService {
public static final String BASE_URL = "http://your.api/endpoint/base-url";
@Multipart
@POST("/upload")
void upload(@Part("myfile") TypedFile file,
@Part("description") String description,
Callback<String> cb);
}
定義call對象模板;
public class ServiceGenerator {
public static final String API_BASE_URL = "http://your.api-base.url";
private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
private static Retrofit.Builder builder =
new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(GsonConverterFactory.create());
public static <S> S createService(Class<S> serviceClass) {
Retrofit retrofit = builder.client(httpClient.build()).build();
return retrofit.create(serviceClass);
}
}
實際調用;
FileUploadService service = ServiceGenerator.createService(FileUploadService.class, FileUploadService.BASE_URL);
TypedFile typedFile = new TypedFile("multipart/form-data", new File("path/to/your/file"));
String description = "hello, this is description speaking";
service.upload(typedFile, description, new Callback<String>() {
@Override
public void success(String s, Response response) {
Log.e("Upload", "success");
}
@Override
public void failure(RetrofitError error) {
Log.e("Upload", "error");
}
});
TypedFile
在Retrofit
中定義,提供File
和mime type
兩個參數,這裏使用的mime type
值爲multipart/form-data
。