Retrofit簡介:
Retrofit是Square公司開發的一款針對Android網絡請求的框架,Retrofit2底層基于OkHttp實現的,Okhttp現在已經得到Google官方認可,大量的app都采用OkHttp做網絡請求
1.要使用Retrofit首先得添加gradle依賴
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
2.新建一個Retrofit對象
mRetrofit = new Retrofit.Builder()
.baseUrl("http://192.168.0.102/")
.client(configClient())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
需要注意的是baseUrl必須以”/”結尾
3.新建一個Interface,需要請求的接口服務類
public interface Api {
@GET("index.php?c=User&m=getUser")
Call<List<User>> getUser();
}
在我們定義請求的接口時,會傳入具體的接口的地址(Endpoint):
@GET("index.php?c=User&m=getUser")
Call<List<User>> getUser();
Retrofit會幫我們完成拼接,最后的URL是:
http://192.168.0.102/index.php?c=User&m=getUser
Endpoint可以為完整的URL:
Base URL: http://example.com/
Endpoint: https://github.com/square/retrofit/
Result: https://github.com/square/retrofit/
當我們需要請求不同服務器上的API時,這就非常有用了。
@GET
Call<List<User>> getUser2(@Url String url);
Call<List<User>> call=userClient.getUser2("http://www.jiangxu.online/index.php?c=User&m=getUser");
Retrofit中一些參數注解:
@Path:所有在網址中的參數(URL的問號前面),如:
http://102.10.10.132/api/Accounts/{accountId}
@Query:URL問號后面的參數,如:
http://102.10.10.132/api/Comments?access_token={access_token}
@QueryMap:相當于多個@Query
@Field:用于POST請求,提交單個數據
@Body:相當于多個@Field,以對象的形式提交
@Path和@Query的區別;
example:www.app.net/api/searchtypes/862189/filters?Type=6&SearchText=School
@GET("/api/searchtypes/{Id}/filters")
void getFilterList(@Path("Id") long customerId,
@Query("Type") String responseType,
@Query("SearchText") String searchText,
Callback<FilterResponse> filterResponse);
url拼接:www.app.net/api/searchtypes/{Path}/filters?Type={Query}&SearchText={Query}
?
Get查詢參數
查詢參數是一種很常見的客戶端往服務端傳遞數據的方式,比如我們需要傳一個id給服務端,那么URL可能是這樣的:
https://api.example.com/tasks?id=123
Retrofit 定義實現查詢參數:
public interface TaskService {
@GET("/tasks")
Call<Task> getTask(@Query("id") long taskId);
}
方法getTask需要傳入taskId,這個參數會被映射到@Query(“id”)中的id中,最后的URL會變成這樣:
/tasks?id=<taskId>
有時我們需要對于一個id參數傳入多個值,比如這樣:
https://api.example.com/tasks?id=123&id=124&id=125
對于這樣的需求,Retrofit 通過傳入一個List來實現:
public interface TaskService {
@GET("/tasks")
Call<List<Task>> getTask(@Query("id") List<Long> taskIds);
}
這樣,拼接后的URL就是我們需要的那樣。
有時,我們在給服務端傳遞數據時,有些參數是可選的,比如下面的URL:
https://your.api.com/tasks?sort=value-of-order-parameter
sort參數是可選的,有些情況下可能不需要傳入。
接口的定義:
public interface TaskService {
@GET("/tasks")
Call<List<Task>> getTasks(@Query("sort") String order);
}
那么,在我們不想添加排序控制的時候,我們可以傳入null,Retrofit 會忽略值為null的參數。
service.getTasks(null);
需要注意的是,可忽略參數的參數類型不能是int, float, long這些基本類型,應該用Integer, Float, Long來代替。
Post 提交參數
Retrofit Post方式提交表單形式的參數需要添加標記@FormUrlEncoded,通過@Field注釋添加鍵值對。?
接口定義:
public interface UserClient {
@FormUrlEncoded
@POST("/index.php?c=User&m=login")
Call<List<User>> login(@Field("phone") String phone, @Field("password") String password);
}
客戶端調用:
private void login() {
UserClient userClient = ServiceGenerator.createService(UserClient.class);
Call<List<User>> call = userClient.login("13695378745","123456");
call.enqueue(new Callback<List<User>>() {
});
}
Post 提交JSON數據
有時提交的數據量比較大時,用鍵值對的方式提交參數不太方便,Retrofit可以通過@Body注釋,直接傳遞一個對象給請求主體,Retrofit通過JSON轉化器,把對象映射成JSON數據。
接口定義:
public interface TaskService {
@POST("/tasks")
Call<Task> createTask(@Body Task task);
}
傳遞實體需要有Model:
public class Task {
private long id;
private String text;
public Task() {}
public Task(long id, String text) {
this.id = id;
this.text = text;
}
}
客戶端調用:
Task task = new Task(1, "my task title");
Call<Task> call = taskService.createTask(task);
call.enqueue(new Callback<Task>() {});
這樣,服務端得到的是JOSN數據:
{
"id": 1,
"text": "my task title"
}
文件下載
接口定義:
@Streaming
@GET
Call<ResponseBody> downloadFile(@Url String fileUrl);
在下載的時候,是在子線程中工作運行的,有時需要顯示下載的進度,同時支持多個下載并發進行,這樣需要封裝一下,使用起來方便:
public class DownFileManager {
private final static String TAG = "DownFileManager";
private static DownFileManager sDownFileManager;
private CallBack mCallBack;
private final Handler mHandler = new Handler(Looper.getMainLooper());
private ExecutorService executorService;
public interface CallBack {
void onSuccess(String path);
void inProgress(int progress);
void onFail();
}
public static DownFileManager getInstance() {
if (sDownFileManager == null) {
sDownFileManager = new DownFileManager();
}
return sDownFileManager;
}
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), Util.threadFactory("DownFileManager", false));
}
return executorService;
}
public void download(final String path, final String filePath, CallBack callBack) {
mCallBack = callBack;
executorService();
executorService.submit(new Runnable() {
boolean isSuccess;
@Override
public void run() {
DownloadService service = ServiceGenerator.createService(DownloadService.class);
Call<ResponseBody> call = service.downloadFile(path);
try {
isSuccess = writeResponseBodyToDisk(filePath, call.execute().body());
} catch (Exception e) {
e.printStackTrace();
}
mHandler.post(new Runnable() {
@Override
public void run() {
if (isSuccess) {
mCallBack.onSuccess(filePath);
} else {
mCallBack.onFail();
}
}
});
}
});
}
private boolean writeResponseBodyToDisk(String path, ResponseBody body) {
try {
File futureStudioIconFile = new File(path);
InputStream inputStream = null;
OutputStream outputStream = null;
try {
byte[] fileReader = new byte[4096];
long fileSize = body.contentLength();
long fileSizeDownloaded = 0;
inputStream = body.byteStream();
outputStream = new FileOutputStream(futureStudioIconFile);
int oldProgress = 0;
while (true) {
int read = inputStream.read(fileReader);
if (read == -1) {
break;
}
outputStream.write(fileReader, 0, read);
fileSizeDownloaded += read;
final int progress = (int) ((fileSizeDownloaded * 1.0 / fileSize) * 100);
if (oldProgress != progress) {
mHandler.post(new Runnable() {
@Override
public void run() {
mCallBack.inProgress(progress);
}
});
}
oldProgress = progress;
}
outputStream.flush();
return true;
} catch (IOException e) {
return false;
} finally {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
}
} catch (IOException e) {
return false;
}
}
}
使用:
DownFileManager.getInstance().download(downloadUrl, filePath, new DownFileManager.CallBack() {
@Override
public void onSuccess(String path) {
//下載文件的路徑
}
@Override
public void inProgress(int progress) {
//下載的進度
Trace.i("DownFileManager","progress ="+progress);
}
@Override
public void onFail() {
}
});
文件上傳
Retrofit 支持 Multipart 請求,所以我們可以用它來實現文件上傳,對于要實現文件上傳的接口,需要添加@Multipart注釋。?
接口定義:
public interface FileUploadService {
@Multipart
@POST("index.php?c=Upload&m=doUpload")
Call<Msg> upload(
@Part("file\"; filename=\"image.png\" ") RequestBody file,
@Part("description") RequestBody description);
}
對于要上傳的文件,我們需要把它包裝成RequestBody 對象:
private void uploadFile( File file) {
FileUploadService service =
ServiceGenerator.createService(FileUploadService.class);
String descriptionString = "hello, this is description speaking";
RequestBody description =
RequestBody.create(MediaType.parse("multipart/form-data"), descriptionString);
RequestBody requestBody =
RequestBody.create(MediaType.parse("multipart/form-data"), file);
Call<Msg> call = service.upload(requestBody, description);
call.enqueue(new Callback<Msg>() {
@Override
public void onResponse(Call<Msg> call, Response<Msg> response) {
Log.v("Upload", "success");
}
@Override
public void onFailure(Call<Msg> call, Throwable t) {
Log.e("Upload", t.getMessage());
}
});
}
同步請求和異步請求
同步的請求會在同一個線程中執行,在Android中必須另開線程,否則在主線程中執行會拋出異常。?
同步和異步請求的接口都是一樣的,只是調用的時候會不一樣:
public interface TaskService {
@GET("/tasks")
Call<List<Task>> getTasks();
}
同步請求調用:
TaskService taskService = ServiceGenerator.createService(TaskService.class);
Call<List<Task>> call = taskService.getTasks();
List<Task>> tasks = call.execute().body();
異步請求調用:
TaskService taskService = ServiceGenerator.createService(TaskService.class);
Call<List<Task>> call = taskService.getTasks();
call.enqueue(new Callback<List<Task>>() {
@Override
public void onResponse(Call<List<Task>> call, Response<List<Task>> response) {
if (response.isSuccess()) {
// tasks available
} else {
// error response, no access to resource?
}
}
@Override
public void onFailure(Call<List<Task>> call, Throwable t) {
// something went completely south (like no internet connection)
Log.d("Error", t.getMessage());
}
}
異步請求實現了一個CallBack,包含了兩個回調方法:onResponse和 onFailure,在onResponse中我們可以拿到我們需要的實體數據,在onFailure中,可以拿到錯誤的Log。
原文地址:http://blog.csdn.net/jiangxuqaz/article/details/50759239
部分內容,也根據自己的理解添加或者修改
Demo地址: