在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(...);
相关文章推荐
- apache2.4出现(OS 64)指定的网络名不再可用。 : AH00341: winnt_accept: Asynchronous AcceptEx failed.问题
- Swift使用AFNetwroking访问网络数据
- 测试python HTTPServer功能
- https建立通讯过程及运行机制
- Win10 + VMware-CentOS7文件共享、网络连接
- libvirt网络过滤规则简单总结
- libvirt网络过滤规则:禁止客户机(bridge方式)连接外网
- java网络编程
- 配置KVM虚拟机的网络,Bridge和Nat方式
- httpoxy漏洞的一些整理
- 网络协程编程
- 深度学习实战——caffe windows 下训练自己的网络模型
- 网络CCNA基础了解
- LAMP 搭建和压力测试
- 关于ELM
- workerman新增tcp端口支持app socket通信
- 网络编程+多线程实现简单的聊天室功能
- luogu2038[NOIP2014 T4]无线网络发射器选址
- luogu2038[NOIP2014 T4]无线网络发射器选址
- gethostbyname()函数详解