您的位置:首页 > 理论基础 > 计算机网络

在android中使用Retrofit网络框架

2016-07-19 01:23 741 查看

1.Retrofitk可以让你使用注解的方式描述Http请求

基本使用:

<span style="font-size:14px;">public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
GitHubService service = retrofit.create(GitHubService.class);
Call<List<Repo>> repos = service.listRepos("octocat");</span>

2.GET多个参数查询,键相同。

<span style="font-size:14px;">https://api.example.com/tasks?id=123&id=124&id=125
public interface TaskService {
@GET("/tasks")
Call<List<Task>> getTask(@Query("id") List<Long> taskIds);
}</span>

3.同步和异步请求。

同步:

<span style="font-size:14px;">TaskService taskService = ServiceGenerator.createService(TaskService.class);
Call<List<Task>> call = taskService.getTasks();
List<Task>> tasks = call.execute().body();  </span>

异步:

<span style="font-size:14px;">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.isSuccessful()) {
// 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());
}
}</span>

4.Post a Body

<span style="font-size:14px;">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>() {}); </span>

5.自定义的数据的转换器。

Retrofit已经帮我们实现了几个常用的转换器如json,xml.

但有时候我们也需要自己来完成一个转换器,例如:

<span style="font-size:14px;">public class JacksonConverter implements Converter {
private ObjectMapper mapper = new ObjectMapper();

@Override
public Object fromBody(TypedInput body, Type type) throws ConversionException {
JavaType javaType = mapper.getTypeFactory().constructType(type);

try {
return mapper.readValue(body.in(), javaType);
} catch (IOException e) {
throw new ConversionException(e);
}
}

@Override
public TypedOutput toBody(Object object) {
try {
String charset = "UTF-8";
String json = mapper.writeValueAsString(object);
return new JsonTypedOutput(json.getBytes(charset));
} catch (IOException e) {
throw new AssertionError(e);
}
}

private static class JsonTypedOutput implements TypedOutput {
private final byte[] jsonBytes;

JsonTypedOutput(byte[] jsonBytes) { this.jsonBytes = jsonBytes; }

@Override public String fileName() { return null; }
@Override public String mimeType() { return "application/json; charset=UTF-8"; }
@Override public long length() { return jsonBytes.length; }
@Override public void writeTo(OutputStream out) throws IOException { out.write(jsonBytes); }
}
}
// Create our Converter
JacksonConverter jacksonConverter = new JacksonConverter();
// Build the Retrofit REST adaptor pointing to the URL specified, with the Converter.
// Note: The Converter must be set before the .build() command
RestAdapter restAdapter = new RestAdapter.Builder()
.setConverter(jacksonConverter)
.setEndpoint("https://api.example.com/")
.build();</span>

6.添加请求头:

静态方式:

<span style="font-size:14px;">public interface UserService {
@Headers("Cache-Control: max-age=640000")
@GET("/tasks")
Call<List<Task>> getTasks();
}
public interface UserService {
@Headers({
"Accept: application/vnd.yourapi.v1.full+json",
"User-Agent: Your-App-Name"
})
@GET("/tasks/{task_id}")
Call<Task> getTask(@Path("task_id") long taskId);
}
或者
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(new Interceptor() {
@Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request original = chain.request();

Request request = original.newBuilder()
.header("User-Agent", "Your-App-Name")
.header("Accept", "application/vnd.yourapi.v1.full+json")
.method(original.method(), original.body())
.build();

return chain.proceed(request);
}
}

OkHttpClient client = httpClient.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();</span>

动态方式:

<span style="font-size:14px;">public interface UserService {
@GET("/tasks")
Call<List<Task>> getTasks(@Header("Content-Range") String contentRange);
}</span>

7.添加查询的参数:

<span style="font-size:14px;">public interface TaskService {
@GET("/tasks")
Call<List<Task>> getTasks(@Query("sort") String order);
}
//可选参数,可以置空
public interface TaskService {
@GET("/tasks")
Call<List<Task>> getTasks(
@Query("sort") String order,
@Query("page") Integer page);
}
service.getTasks(null, null);  </span>

8.定义xml转换器

<span style="font-size:14px;"><configuration id="1234">
<server port="80">
<host>www.domain.com</host>
<security ssl="true">
<keyStore>example keystore</keyStore>
</security>
</server>
</configuration></span>
<span style="font-size:14px;">@Root
public class Configuration {

@Element
private Server server;

@Attribute
private int id;

public int getIdentity() {
return id;
}

public Server getServer() {
return server;
}
}

public class Server {

@Attribute
private int port;

@Element
private String host;

@Element
private Security security;

public int getPort() {
return port;
}

public String getHost() {
return host;
}

public Security getSecurity() {
return security;
}
}

public class Security {

@Attribute
private boolean ssl;

@Element
private String keyStore;

public boolean isSSL() {
return ssl;
}

public String getKeyStore() {
return keyStore;
}
}</span>

9.开启Log

<span style="font-size:14px;">HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
// set your desired log level
logging.setLevel(Level.BODY);

OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
// add your other interceptors …

// add logging as last interceptor
httpClient.addInterceptor(logging);  // <-- this is the important line!

Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(httpClient.build())
.build();</span>

10.上传文件

<span style="font-size:14px;">public interface FileUploadService {
@Multipart
@POST("upload")
Call<ResponseBody> upload(@Part("description") RequestBody description,
@Part MultipartBody.Part file);
}

private void uploadFile(Uri fileUri) {
// create upload service client
FileUploadService service =
ServiceGenerator.createService(FileUploadService.class);

// https://github.com/iPaulPro/aFileChooser/blob/master/aFileChooser/src/com/ipaulpro/afilechooser/utils/FileUtils.java // use the FileUtils to get the actual file by uri
File file = FileUtils.getFile(this, fileUri);

// create RequestBody instance from file
RequestBody requestFile =
RequestBody.create(MediaType.parse("multipart/form-data"), file);

// MultipartBody.Part is used to send also the actual file name
MultipartBody.Part body =
MultipartBody.Part.createFormData("picture", file.getName(), requestFile);

// add another part within the multipart request
String descriptionString = "hello, this is description speaking";
RequestBody description =
RequestBody.create(
MediaType.parse("multipart/form-data"), descriptionString);

// finally, execute the request
Call<ResponseBody> call = service.upload(description, body);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call,
Response<ResponseBody> response) {
Log.v("Upload", "success");
}

@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.e("Upload error:", t.getMessage());
}
});
}</span>

11.错误处理:

<span style="font-size:14px;">{
statusCode: 409,
message: "Email address already registered"
}</span>


public class APIError {

private int statusCode;
private String message;

public APIError() {
}

public int status() {
return statusCode;
}

public String message() {
return message;
}
}


public class ErrorUtils {

public static APIError parseError(Response<?> response) {
Converter<ResponseBody, APIError> converter =
ServiceGenerator.retrofit()
.responseBodyConverter(APIError.class, new Annotation[0]);

APIError error;

try {
error = converter.convert(response.errorBody());
} catch (IOException e) {
return new APIError();
}

return error;
}
}


Call<User> call = service.me();
call.enqueue(new Callback<User>() {
@Override
public void onResponse(Call<User> call, Response<User> response) {
if (response.isSuccessful()) {
// use response data and do some fancy stuff :)
} else {
// parse the response body …
APIError error = ErrorUtils.parseError(response);
// … and use it to show error information

// … or just log the issue like we’re doing :)
Log.d("error message", error.message());
}
}

@Override
public void onFailure(Call<User> call, Throwable t) {
// there is more than just a failing request (like: no internet connection)
}
});

12.Encode Request

public interface TaskService {
@FormUrlEncoded
@POST("tasks")
Call<Task> createTask(@Field(value = "title", encoded = true) String title);
}
public interface TaskService {
@FormUrlEncoded
@POST("tasks")
Call<List<Task>> createTasks(@Field("title") List<String> titles);
}

public interface UserService {
@FormUrlEncoded
@PUT("user")
Call<User> update(@FieldMap Map<String, String> fields);
}

13.简化提交参数的形式:

在参数较多时,或者其中有很多不变的参数可以这样做

例如以前有:

@FormUrlEncoded
@POST("/feedback")
Call<ResponseBody> sendFeedbackSimple(
@Field("osName") String osName,
@Field("osVersion") int osVersion,
@Field("device") String device,
@Field("message") String message,
@Field("userIsATalker") Boolean userIsATalker);
改进如下:

@POST("/feedback")
Call<ResponseBody> sendFeedbackConstant(@Body UserFeedback feedbackObject);

public class UserFeedback {
private String osName = "Android";
private int osVersion = android.os.Build.VERSION.SDK_INT;
private String device = Build.MODEL;
private String message;
private boolean userIsATalker;

public UserFeedback(String message) {
this.message = message;
this.userIsATalker = (message.length() > 200);
}

// getters & setters
// ...
}

private void sendFeedbackFormAdvanced(@NonNull String message) {
FeedbackService taskService = ServiceGenerator.create(FeedbackService.class);

Call<ResponseBody> call = taskService.sendFeedbackConstant(new UserFeedback(message));

call.enqueue(new Callback<ResponseBody>() {
...
});
}

14.下载文件:

注意这里的返回结果是ResponseBody,不能是其他的。

/ option 1: a resource relative to your base URL
@GET("/resource/example.zip")
Call<ResponseBody> downloadFileWithFixedUrl();

// option 2: using a dynamic URL
@GET
Call<ResponseBody> downloadFileWithDynamicUrlSync(@Url String fileUrl);


一般小的文件:

FileDownloadService downloadService = ServiceGenerator.create(FileDownloadService.class);
Call<ResponseBody> call = downloadService.downloadFileWithDynamicUrlSync(fileUrl);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.isSuccess()) {
Log.d(TAG, "server contacted and has file");
boolean writtenToDisk = writeResponseBodyToDisk(response.body());
Log.d(TAG, "file download was a success? " + writtenToDisk);
} else {
Log.d(TAG, "server contact failed");
}
}

@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.e(TAG, "error");
}
});

private boolean writeResponseBodyToDisk(ResponseBody body) {
try {
// todo change the file location/name according to your needs
File futureStudioIconFile = new File(getExternalFilesDir(null) + File.separator + "Future Studio Icon.png");

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);
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);
}
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;
}
}

在下载大文件时需要使用:

@Streaming
@GET
Call<ResponseBody> downloadFileWithDynamicUrlAsync(@Url String fileUrl);
final FileDownloadService downloadService =
ServiceGenerator.create(FileDownloadService.class);

new AsyncTask<Void, Long, Void>() {
@Override
protected Void doInBackground(Void... voids) {
Call<ResponseBody> call = downloadService.downloadFileWithDynamicUrlSync(fileUrl);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.isSuccess()) {
Log.d(TAG, "server contacted and has file");

boolean writtenToDisk = writeResponseBodyToDisk(response.body());

Log.d(TAG, "file download was a success? " + writtenToDisk);
}
else {
Log.d(TAG, "server contact failed");
}
}
return null;
}
}.execute();

15.取消请求:

String fileUrl = "http://futurestud.io/test.mp4";
Call<ResponseBody> call =
downloadService.downloadFileWithDynamicUrlSync(fileUrl);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
Log.d(TAG, "request success");
}

@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
if (call.isCanceled()) {  //这里判别失败原因是无网络还是请求被取消了的情况
Log.e(TAG, "request was cancelled");
}
else {
Log.e(TAG, "other larger issue, i.e. no network connection?");
}
}
});
}

// something happened, for example: user clicked cancel button
call.cancel();
}

16.重复请求和分析请求体:

对于重复请求:应该clone下

FileDownloadService downloadService =
ServiceGenerator.create(FileDownloadService.class);

Call<ResponseBody> originalCall =
downloadService.downloadFileWithDynamicUrlSync(fileUrl);
Callback<ResponseBody> downloadCallback = new Callback<ResponseBody>() {...};

// correct reuse:
Call<ResponseBody> newCall = originalCall.clone();   //克隆一个回调
newCall.enqueue(downloadCallback);


对于分析请求体:调用call.request()方法,这个方法最好在请求结束后调用。

new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
checkRequestContent(call.request());
}

@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
checkRequestContent(call.request());
}
}
//这样我们就可以对我们自己请求的接口参数的正确性有一定的了解了。
private void checkRequestContent(Request request) {
Headers requestHeaders = request.headers();
RequestBody requestBody = request.body();
HttpUrl requestUrl = request.url();

// todo make decision depending on request content
}

17.only send text Body

public interface ScalarService {
@POST("path")
Call<ResponseBody> getStringRequestBody(@Body RequestBody body);
}
String text = "plain text request body";
RequestBody body =
RequestBody.create(MediaType.parse("text/plain"), text);

Call<ResponseBody> call = service.getStringRequestBody(body);
Response<ResponseBody> response = call.execute();
String value = response.body().string();

18.上传多个文件:

public interface FileUploadService {
// previous code for single file uploads
@Multipart
@POST("upload")
Call<ResponseBody> uploadFile(
@Part("description") RequestBody description,
@Part MultipartBody.Part file);

// new code for multiple files
@Multipart
@POST("upload")
Call<ResponseBody> uploadMultipleFiles(
@Part("description") RequestBody description,
@Part MultipartBody.Part file1,
@Part MultipartBody.Part file2);
}


public static final String MULTIPART_FORM_DATA = "multipart/form-data";

@NonNull
private RequestBody createPartFromString(String descriptionString) {
return RequestBody.create(
MediaType.parse(MULTIPART_FORM_DATA), descriptionString);
}

@NonNull
private MultipartBody.Part prepareFilePart(String partName, Uri fileUri) {
// https://github.com/iPaulPro/aFileChooser/blob/master/aFileChooser/src/com/ipaulpro/afilechooser/utils/FileUtils.java // use the FileUtils to get the actual file by uri
File file = FileUtils.getFile(this, fileUri);

// create RequestBody instance from file
RequestBody requestFile =
RequestBody.create(MediaType.parse(MULTIPART_FORM_DATA), file);

// MultipartBody.Part is used to send also the actual file name
return MultipartBody.Part.createFormData(partName, file.getName(), requestFile);
}

Uri file1Uri = ... // get it from a file chooser or a camera intent
Uri file2Uri = ... // get it from a file chooser or a camera intent

// create upload service client
FileUploadService service =
ServiceGenerator.createService(FileUploadService.class);

// create part for file (photo, video, ...)
MultipartBody.Part body1 = prepareFilePart("video", file1Uri);
MultipartBody.Part body2 = prepareFilePart("thumbnail", file2Uri);

// add another part within the multipart request
RequestBody description = createPartFromString("hello, this is description speaking");

// finally, execute the request
Call<ResponseBody> call = service.uploadMultipleFiles(description, body1, body2);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call,
Response<ResponseBody> response) {
Log.v("Upload", "success");
}

@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.e("Upload error:", t.getMessage());
}
});

19.Multiple Parts with @PartMap

public interface FileUploadService {
// declare a description explicitly
// would need to declare
@Multipart
@POST("upload")
Call<ResponseBody> uploadFile(
@Part("description") RequestBody description,
@Part MultipartBody.Part file);

@Multipart
@POST("upload")
Call<ResponseBody> uploadFileWithPartMap(
@PartMap() Map<String, RequestBody> partMap,
@Part MultipartBody.Part file);
}


public static final String MULTIPART_FORM_DATA = "multipart/form-data";

@NonNull
private RequestBody createPartFromString(String descriptionString) {
return RequestBody.create(
MediaType.parse(MULTIPART_FORM_DATA), descriptionString);
}

Uri fileUri = ... // from a file chooser or a camera intent

// create upload service client
FileUploadService service =
ServiceGenerator.createService(FileUploadService.class);

// create part for file (photo, video, ...)
MultipartBody.Part body = prepareFilePart("photo", fileUri);

// create a map of data to pass along
RequestBody description = createPartFromString("hello, this is description speaking");
RequestBody place = createPartFromString("Magdeburg");
RequestBody time = createPartFromString("2016");

HashMap<String, RequestBody> map = new HashMap<>();
map.put("description", description);
map.put("place", place);
map.put("time", time);

// finally, execute the request
Call<ResponseBody> call = service.uploadFileWithPartMap(map, body);
call.enqueue(...);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: