HttpUtils
HttpUtils 是近期开源的对 OkHttp 轻量封装的框架,它独创的异步预处理器,特色的标签,灵活的上传下载进度监听与过程控制功能,在轻松解决很多原本另人头疼问题的同时,设计上也力求纯粹与优雅。
<dependency>
<groupId>com.ejlchina</groupId>
<artifactId>httputils</artifactId>
<version>2.3.0</version>
</dependency>
compile 'com.ejlchina:httputils:2.3.0'
HTTP http = HTTP.builder().build();
以上代码构建了一个最简单的HTTP
实例,它拥有以下三个方法:
async(String url)
开始一个异步HTTP任务sync(String url)
开始一个同步HTTP任务cancel(String tag)
根据标签批量取消HTTP任务 为了使用方便,在构建的时候,我们更愿意指定一个BaseUrl
(请参见5.1 设置 BaseUrl):
HTTP http = HTTP.builder()
.baseUrl("http://api.demo.com")
.build();
为了简化文档,下文中出现的http
均是在构建时设置了BaseUrl
的HTTP
实例。
使用方法sync(String url)
开始一个同步请求:
List<User> users = http.sync("/users") // http://api.demo.com/users
.get() // GET请求
.getBody() // 获取响应报文体
.toList(User.class); // 得到目标数据
方法sync
返回一个同步HttpTask
,可链式使用。
使用方法async(String url)
开始一个异步请求:
http.async("/users/1") // http://api.demo.com/users/1
.setOnResponse((HttpResult result) -> {
// 得到目标数据
User user = result.getBody().toBean(User.class);
})
.get(); // GET请求
方法async
返回一个异步HttpTask
,可链式使用。 Запрос тела в формате JSON или преобразование в JSON JavaBean:
Загрузка файлов:
Добавление тегов:
Установка диапазона:
Привязка объекта:
Иногда требуется классифицировать HTTP-задачи. Для этого можно использовать теги:
http.async("/users") // (1)
.setTag("A").get();
http.async("/users") // (2)
.setTag("A.B").get();
http.async("/users") // (3)
.setTag("B").get();
http.async("/users") // (4)
.setTag("B.C").get();
http.async("/users") // (5)
.setTag("C").get();
После добавления тегов можно отменять задачи по тегам:
int count = http.cancel("B"); // (2), (3), (4) будут отменены (отменяются задачи с тегом «B»)
System.out.println(count); // вывод 3
Следует отметить, что только асинхронные HTTP-задачи могут быть отменены. Теги также могут использоваться в препроцессорах, как описано в разделах параллельный препроцессор и последовательный препроцессор.
HTTP http = HTTP.builder()
.baseUrl("http://api.demo.com") // установка BaseUrl
.build();
Эта конфигурация действует глобально. После настройки BaseUrl конкретные запросы могут быть опущены, что делает код более лаконичным:
http.sync("/users").get() // http://api.demo.com/users
http.sync("/auth/signin") // http://api.demo.com/auth/signin
.addBodyParam("username", "Jackson")
.addBodyParam("password", "xxxxxx")
.post() // POST-запрос
Если есть специальные задачи, всё ещё можно использовать полный путь, это не помешает:
http.sync("https://www.baidu.com").get()
Чтобы изменить поток выполнения функции обратного вызова, можно настроить обработчик обратного вызова. Например, в Android можно выполнить все обратные вызовы в потоке пользовательского интерфейса, настроив следующим образом:
HTTP http = HTTP.builder()
.callbackExecutor((Runnable run) -> {
runOnUiThread(run); // выполнение в потоке UI
})
.build();
Этот параметр влияет на все обратные вызовы.
В отличие от других фреймворков, которые скрывают возможности OkHttp, HttpUtils не скрывает полезные функции OkHttp:
HTTP http = HTTP.builder()
.config((Builder builder) -> {
// настройка пула соединений (минимум 10 соединений (по умолчанию 5), время ожидания 5 минут)
builder.connectionPool(new ConnectionPool(10, 5, TimeUnit.MINUTES));
// настройка времени ожидания соединения (по умолчанию 10 секунд)
builder.connectTimeout(20, TimeUnit.SECONDS);
// настройка перехватчика
builder.addInterceptor((Chain chain) -> {
Request request = chain.request();
// обязательно вернуть результат синхронно, перехватчики не могут выполнять асинхронные операции
return chain.proceed(request);
});
// другие настройки: SSL, кэш, прокси, события и т. д.
})
.build();
Препроцессор (Preprocessor) позволяет нам изменять запрос перед его отправкой, но отличается от перехватчиков OkHttp (Interceptor): препроцессор позволяет обрабатывать эти проблемы асинхронно.
Например, если мы хотим автоматически добавить заголовок Token к запросу, а токен можно получить только асинхронным методом requestToken, то использование перехватчика будет затруднительным. Однако препроцессор легко решает эту проблему:
HTTP http = HTTP.builder()
.addPreprocessor((PreChain chain) -> {
HttpTask<?> task = chain.getTask();// получение текущей HTTP-задачи
if (!task.isTagged("Auth")) { // проверка необходимости Token для этой задачи по метке
return;
}
requestToken((String token) -> { // асинхронный запрос Token
task.addHeader("Token", token);// добавление заголовка к задаче
chain.proceed(); // продолжение текущей задачи
});
})
.build();
Как и перехватчики, препроцессоры могут добавляться в нескольких экземплярах.
Обычные препроцессоры можно обрабатывать параллельно, однако иногда мы хотим, чтобы один препроцессор обрабатывал только одну задачу за раз. Например, когда срок действия токена истёк, нам нужно обновить его, и эта операция должна выполняться только одной задачей за раз, поскольку если несколько задач попытаются обновить токен одновременно, новый токен может стать недействительным до того, как другие задачи получат его. Это нежелательно.
Для решения этой проблемы HttpUtils предоставляет последовательный препроцессор, который позволяет задачам выстраиваться в очередь и последовательно проходить через препроцессор:
HTTP http = HTTP.builder()
.addSerialPreprocessor((PreChain chain) -> {
HttpTask<?> task = chain.getTask();
if
Обратите внимание, что данный текст представляет собой машинный перевод и может содержать неточности. (!task.isTagged("Auth")) {
return;
}
// 检查过期,若需要则刷新Token
requestTokenAndRefreshIfExpired((String token) -> {
task.addHeader("Token", token);
chain.proceed(); // 调用此方法前,不会有其它任务进入该处理器
});
})
.build();
#### 6.6 全局回调处理
```java
HTTP http = HTTP.builder()
.responseListener((HttpTask<?> task, HttpResult result) -> {
// 所有请求响应后都会走这里
return true; // 返回 true 表示继续执行 task 的 OnResponse 回调,false 表示不再执行
})
.completeListener((HttpTask<?> task, State state) -> {
// 所有请求执行完都会走这里
return true; // 返回 true 表示继续执行 task 的 OnComplete 回调,false 表示不再执行
})
.exceptionListener((HttpTask<?> task, IOException error) -> {
// 所有请求发生异常都会走这里
return true; // 返回 true 表示继续执行 task 的 OnException 回调,false 表示不再执行
})
.build();
HTTP http = HTTP.builder()
.downloadListener((HttpTask<?> task, Download download) -> {
// 所有下载在开始之前都会先走这里
Ctrl ctrl = download.getCtrl(); // 下载控制器
})
.build();
### 7 使用 HttpUtils 类
类`HttpUtils`本是 1.x 版本里的最重要的核心类,由于在 2.x 版本里抽象出了`HTTP`接口,使得它的重要性已不如往昔。但合理的使用它,仍然可以带来不少便利,特别是在没有IOC容器的环境里,比如在Android开发和一些工具项目的开发中。
类`HttpUtils`共定义了四个静态方法:
* `async(String url)` 开始一个异步请求 (内容通过一个`HTTP`单例实现)
* `sync(String url)` 开始一个同步请求 (内容通过一个`HTTP`单例实现)
* `cancel(String tag)` 按标签取消请求(内容通过一个`HTTP`单例实现)
* `of(HTTP http)` 配置`HttpUtils`持有的`HTTP`实例(不调用此方法前默认使用一个没有没有经过任何配置的`HTTP`懒实例)
也就是说,能使用`http`实例的地方,都可以使用`HttpUtils`类,例如:
```java
// 在配置HTTP实例之前,只能使用全路径方式
List<Role> roles = HttpUtils.sync("http://api.demo.com/roles")
.get().getBody().toList(Role.class);
// 配置HTTP实例,全局生效
HttpUtils.of(HTTP.builder()
.baseUrl("http://api.demo.com")
.build());
// 内部使用新的HTTP实例
List<User> users = HttpUtils.sync("/users")
.get().getBody().toList(User.class);
### 8 文件下载
HttpUtils 并没有把文件的下载排除在常规的请求之外,同一套API,它优雅的设计使得下载与常规请求融合的毫无违和感,一个最简单的示例:
```java
http.sync("/download/test.zip")
.get() // 使用 GET 方法(其它方法也可以,看服务器支持)
.getBody() // 得到报文体
.toFile("D:/download/test.zip") // 指定下载的目录,文件名将根据下载信息自动生成
.start(); // 启动下载
或使用异步连接方式:
http.async("/download/test.zip")
.setOnResponse((HttpResult result) -> {
result.getBody().toFile("D:/download/test.zip").start();
})
.get();
这里要说明一下:sync
与async
的区别在于连接服务器并得到响应这个过程的同步与异步(这个过程的耗时在大文件下载中占比极小),而start
方法启动的下载过程则都是异步的。
就直接上代码啦,诸君一看便懂:
http.sync("/download/test.zip")
.get()
.getBody()
.setStepBytes(1024) // 设置每接收 1024 个字节执行一次进度回调(不设置默认为 8192)
// .setStepRate(0.01) // 设置每接收 1% 执行一次进度回调(不设置以 StepBytes 为准)
.setOnProcess((Process process) -> { // 下载进度回调
long doneBytes = process.getDoneBytes(); // 已下载字节数
long totalBytes = process.getTotalBytes(); // 总共的字节数
double rate = process.getRate(); // 已下载的比例
boolean isDone = process.isDone(); // 是否下载完成
})
.toFolder("D:/download/") // 指定下载的目录,文件名将根据下载信息自动生成
// .toFile("D:/download/test.zip") // 指定下载的路径,若文件已存在则覆盖
.setOnSuccess((File file) -> { // 下载成功回调
})
.start();
值得一提的是:由于 HttpUtils 并没有把下载做的很特别,这里设置的进度回调不只对下载文件起用作,即使对响应JSON的常规请求,只要设置了进度回调,它也会告�ть你报文接收的进度(提前是服务器响应的报文有Content-Length
头),例如:
List<User> users = http.sync("/users")
.get()
.getBody()
.setStepBytes(2)
.setOnProcess((Process process) -> {
System.out.println(process.getRate());
})
.toList(User.class);
#### 8.2 下载过程控制
过于简单:还是直接上代码:
```java
Ctrl ctrl = http.sync("/download/test.zip")
.get() ### 1. Выполнение асинхронного запроса
```java
HttpCall call = http.async("/upload")
.addFileParam("test", "D:/download/test.zip")
.setOnProcess((Process process) -> {
System.out.println(process.getRate());
})
.post()
call.cancel(); // 取消上传
Вопрос: «У функции отмены загрузки нет таких опций, как приостановка и возобновление, наверное, никому это не нужно?»
В процессе разработки под Android часто требуется выполнять определённый код в отдельном потоке. Например, обновление страницы после сетевого запроса выполняется в основном (UI) потоке, а сохранение файла — в потоке ввода-вывода (IO). HttpUtils предоставляет хорошее решение для подобных задач.
По умолчанию все обратные вызовы выполняются в потоке IO. Почему была выбрана такая реализация? Дело в том, что HttpUtils — это чистый Java-пакет инструментов для работы с HTTP, который не зависит от Android и не знает о специфике его UI-потока. Такой подход расширяет возможности использования пакета за пределами Android.
Однако при использовании HttpUtils в контексте Android возникает вопрос: можно ли элегантно решить проблему с UI-потоком? Конечно, можно! Простой способ — настроить обработчик обратного вызова:
HTTP http = HTTP.builder()
.callbackExecutor((Runnable run) -> {
// В реальном коде Handler можно вынести за пределы каждого обратного вызова, чтобы избежать повторного создания
new Handler(Looper.getMainLooper()).post(run); // Выполнить в главном потоке
})
.build();
Приведённый выше код позволяет выполнить все обратные вызовы в основном потоке, например:
http.async("/users")
.addBodyParam("name", "Jack")
.setOnProcess((Process process) -> {
// Выполняется в основном потоке
})
.setOnResponse((HttpResult result) -> {
// Выполняется в основном потоке
})
.setOnException((Exception e) -> {
// Выполняется в основном потоке
})
.setOnComplete((State state) -> {
// Выполняется в основном потоке
})
.post();
Но если вы хотите, чтобы некоторые обратные вызовы выполнялись в потоке ввода-вывода, обеспечивая свободное переключение, как это сделать? HttpUtils предлагает гибкий метод:
http.async("/users")
.addBodyParam("name", "Jack")
.setOnProcess((Process process) -> {
// Выполняется в основном потоке
})
.nextOnIO() // Следующий обратный вызов выполняется в потоке ввода-вывода
.setOnResponse((HttpResult result) -> {
// Выполняется в потоке ввода-вывода
})
.setOnException((Exception e) -> {
// Если не указано nextOnIO, выполняется в обработчике обратного вызова
// Здесь выполняется в основном потоке
})
.nextOnIO() // Следующий обратный вызов выполняется в потоке ввода-вывода
.setOnComplete((State state) -> {
// Выполняется в потоке ввода-вывода
})
.post();
Любой обратный вызов может быть свободно переключён с помощью метода nextOnIO()
. То же самое относится и к загрузке файлов:
http.sync("/download/test.zip")
.get()
.getBody()
.setOnProcess((Process process) -> {
// Выполняется в основном потоке
})
.toFolder("D:/download/")
.nextOnIO() // Следующий обратный вызов выполняется в потоке ввода-вывода
.setOnSuccess((File file) -> {
// Выполняется в потоке ввода-вывода
})
.setOnFailure((Failure failure) -> {
// Выполняется в основном потоке
})
.start();
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарии ( 0 )