XHttp2
Один из мощных сетевых запросов, использующий RxJava2 + Retrofit2 + OKHttp.
XHttp2 — это библиотека для сетевых запросов с широкими возможностями:
Посмотреть UML-диаграмму классов проекта можно здесь.
| Публичный аккаунт | juejin | Zhihu | CSDN | Jianshu | SegmentFault | Bilibili | Toutiao | | --- | --- | --- | --- | --- | --- | --- | | | Мой путь в мир Android-разработки | Ссылка | Ссылка | Ссылка | Ссылка | Ссылка | Ссылка | Ссылка
Для удобства интеграции библиотек X-серии разработчик предлагает шаблон проекта: https://github.com/xuexiangjys/TemplateAppProject.
Следует отметить, что все результаты запросов должны соответствовать следующему формату:
{
"Code": 0, // ответный код, 0 означает успех, иначе — сбой
"Msg": "", // причина сбоя запроса
"Data": {} // объект возвращаемых данных
}
Рекомендуется использовать заглавные буквы для полей «Code», «Msg» и «Data». В противном случае разбор может быть неудачным.
Обратите внимание, что запрос считается успешным только в том случае, если Code равен 0. Если ваш код успеха отличается от 0, вы можете использовать XHttpSDK.setSuccessCode для установки значения, означающего успех.
Если вам нужно настроить возвращаемый объект API, обратитесь к разделу #Настройка API-запроса.
1. Выполнение запросов с использованием стандартных API XHttp
Метод | Тип | Значение по умолчанию | Примечание |
---|---|---|---|
baseUrl | String | / | Устанавливает базовый URL для этого запроса |
timeOut | long | 15000 | Устанавливает время ожидания |
accessToken | boolean | false | Определяет необходимость проверки токена |
threadType | String | / | Задаёт тип планирования запроса |
syncRequest | boolean | false | Указывает, является ли запрос синхронным (без создания дочернего потока) |
onMainThread | boolean | true | Определяет, должен ли запрос выполняться в основном потоке после завершения |
upJson | String | "" | Отправляет данные в формате JSON |
keepJson | boolean | false | Сохраняет ответ в формате JSON |
retryCount | int | / | Определяет количество попыток при таймауте |
retryDelay | int | / | Устанавливает задержку повторной попытки при таймауте |
retryIncreaseDelay | int | / | Увеличивает задержку при повторных попытках при таймауте |
headers | HttpHeaders | / | Добавляет информацию в заголовок запроса |
params | HttpParams | / | Устанавливает параметры формы запроса |
cacheMode | CacheMode | CacheMode.NO_CACHE | Определяет режим кэширования |
Пример использования запроса:
XHttp.get("/user/getAllUser")
.syncRequest(false) // асинхронный запрос
.onMainThread(true) // вернуться в основной поток
.execute(new SimpleCallBack<List<User>>() {
@Override
public void onSuccess(List<User> response) {
refreshLayout.finishRefresh(true);
if (response != null && response.size() > 0) {
mUserAdapter.refresh(response);
mLlStateful.showContent();
} else {
mLlStateful.showEmpty();
}
}
@Override
public void onError(ApiException e) {
refreshLayout.finishRefresh(false);
mLlStateful.showError(e.getMessage(), null);
}
});
XHttp.post("/user/deleteUser")
.params("userId", item.getUserId())
.execute(Boolean.class)
.subscribeWith(new TipRequestSubscriber<Boolean>() {
@Override
protected void onSuccess(Boolean aBoolean) {
ToastUtils.toast("Удаление успешно!");
setFragmentResult(RESULT_OK, null);
popToBack();
}
});
---
**2. Использование унифицированных запросов XHttpRequest**
Перед использованием необходимо загрузить или определить соответствующий протокол сущности. Например:
@RequestParams(url = "/user/addUser", accessToken = false) public static class UserService_AddUser extends XHttpRequest {
/**
*
*/
public User request;
@Override
protected Boolean getResponseEntityType() {
return null;
}
}
| Аннотированный параметр | Тип | По умолчанию | Примечание |
| --- | --- | --- | --- |
| @RequestParams | — | — | |
| url | String | — | Адрес сетевого интерфейса запроса |
| timeout | long | 15000 | Время ожидания ответа |
| keepJson | boolean | false | Следует ли сохранять формат JSON |
| accessToken | boolean | true | Нужно ли проверять токен |
| cacheMode | CacheMode | CacheMode.NO_CACHE | Режим кэширования запроса |
| cacheTime | long | -2 (использует глобальные настройки) | Срок действия кэша | **Использование XHttpSDK для выполнения запросов**
1. Использование XHttpSDK для выполнения запроса:
* post(XHttpRequest xHttpRequest, boolean isSyncRequest, boolean toMainThread): получение PostRequest-запроса (используя имя сущности в качестве ключа запроса).
* postToMain(XHttpRequest xHttpRequest): получение PostRequest-запроса (основной поток → основной поток).
* postToIO(XHttpRequest xHttpRequest): получение PostRequest-запроса (основной поток → дочерний поток).
* postInThread(XHttpRequest xHttpRequest): получение PostRequest-запроса (дочерний поток → дочерний поток).
* execute(XHttpRequest xHttpRequest, boolean isSyncRequest, boolean toMainThread): выполнение PostRequest-запроса и возврат observable-объекта (используя имя сущности в качестве ключа запроса).
* `executeToMain(XHttpRequest xHttpRequest)`: выполнение post-запроса и возвращение observable-объекта (основной поток → основной поток)
* `executeToMain(XHttpRequest xHttpRequest, BaseSubscriber<T> subscriber)`: выполнение post-запроса с последующей подпиской и возвращение информации о подписке (основной поток → основной поток)
2. Пример использования:
XHttpRequest req = ApiProvider.getAddUserReq(getRandomUser()); XHttpSDK.executeToMain(req, new ProgressLoadingSubscriber(mIProgressLoader) { @Override public void onSuccess(Boolean aBoolean) { ToastUtils.toast("用户添加成功!"); mRefreshLayout.autoRefresh(); } });
---
**Использование XHttpProxy для выполнения запросов через прокси**
Перед использованием необходимо загрузить/определить соответствующий протокол интерфейса:
/**
图书管理 / public interface IBook { /*
/**
*/ @NetMethod(action = GET, url = "/book/getAllBook", accessToken = false) Observable<List> getAllBooks(); }
1. Описание аннотаций:
| Аннотации | Параметры | Тип | Значение по умолчанию | Описание |
| --- | --- | --- | --- | --- |
| @NetMethod | parameterNames | String[] | {} | Набор имён параметров |
| | paramType | int | JSON=1 | Тип параметра |
| | action | String | POST="post" | Действие запроса |
| | baseUrl | String | "" | Устанавливает базовый URL данного запроса |
| | url | String | "" | Адрес сетевого интерфейса |
| | timeout | long | 15000 | Время ожидания |
| | keepJson | boolean | false | Сохранять ли JSON |
| | accessToken | boolean | true | Требуется ли токен аутентификации |
| | cacheMode | CacheMode | CacheMode.NO_CACHE | Режим кэширования запроса |
| | cacheTime | long | -2 (используется глобальная настройка) | Срок действия кэша |
| | cacheKeyIndex | int | -1 (все параметры) | Индекс параметра, используемого в качестве ключа кэша |
2. Выполнение запроса с помощью XHttpProxy:
Создайте экземпляр XHttpProxy и передайте ему определённый интерфейс API. По умолчанию используется ThreadType.TO_MAIN.
* TO_MAIN: executeToMain (main → io → main)
> Обратите внимание: убедитесь, что сетевой запрос выполняется в основном потоке [фактически это асинхронный запрос (переключение на поток io), а ответ снова переключается на основной поток].
* TO_IO: executeToIO (main → io → io)
> Обратите внимание: убедитесь, что сетевой запрос выполняется в основном потоке [фактически это асинхронный запрос (переключение на поток io), но поток ответа остаётся прежним, то есть поток, который инициировал запрос].
* IN_THREAD: executeInThread (io → io → io)
> Обратите внимание: запрос должен выполняться в дочернем потоке, чтобы использовать этот тип [фактически синхронный запрос без переключения потоков].
3. Пример использования:
// Использование XHttpProxy для прокси-запросов к интерфейсу XHttpProxy.proxy(TestApi.IOrder.class) .buyBook(mBookAdapter.getItem(position).getBookId(), UserManager.getInstance().getUser().getUserId(), 1) .subscribeWith(new TipRequestSubscriber() { @Override public void onSuccess(Boolean aBoolean) { ToastUtils.toast("Покупка книги" + (aBoolean ? "успешна" : "неудачна") + "!"); mRefreshLayout.autoRefresh(); } });
---
**Загрузка и скачивание файлов**
1. Загрузка файлов (multipart/form-data):
Используйте метод post для загрузки файлов с данными формы. Используйте XHttp.post, затем используйте params для передачи связанных параметров, используйте uploadFile для передачи файла, который необходимо загрузить, и используйте следующий пример:
mIProgressLoader.updateMessage("Загружаю..."); XHttp.post("/book/uploadBookPicture") .params("bookId", book.getBookId()) .uploadFile("file", FileUtils.getFileByPath(mPicturePath), new IProgressResponseCallBack() { @Override public void onResponseProgress(long bytesWritten, long contentLength, boolean done) {
}
}).execute(Boolean.class)
.compose(RxLifecycle.with(this).<Boolean>bindToLifecycle())
.subscribeWith(new ProgressLoadingSubscriber<Boolean>(mIProgressLoader) {
@Override
public void onSuccess(Boolean aBoolean) {
mIsEditSuccess = true;
Файл загружен
Используя XHttp.downLoad
, передав адрес url для загрузки, путь сохранения файла и имя файла, можно завершить загрузку файла. Пример использования:
.savePath(PathUtils.getExtPicturesPath())
.execute(new DownloadProgressCallBack<String>() {
@Override
public void onStart() {
HProgressDialogUtils.showHorizontalProgressDialog(getContext(), "Загрузка картинки...", true);
}
@Override
public void onError(ApiException e) {
ToastUtils.toast(e.getMessage());
HProgressDialogUtils.cancel();
}
@Override
public void update(long bytesRead, long contentLength, boolean done) {
HProgressDialogUtils.onLoading(contentLength, bytesRead); //обновляем прогресс-бар
}
@Override
public void onComplete(String path) {
ToastUtils.toast("Загрузка картинки завершена, путь сохранения: " + path);
HProgressDialogUtils.cancel();
}
});
Высокоуровневые операции с сетевыми запросами
При выполнении запроса подпишитесь на ProgressLoadingSubscriber
или ProgressLoadingCallBack
, передавая загрузчик сообщений IProgressLoader
. Пример использования:
XHttpRequest req = ApiProvider.getAddUserReq(getRandomUser());
XHttpSDK.executeToMain(req, new ProgressLoadingSubscriber<Boolean>(mIProgressLoader) {
@Override
public void onSuccess(Boolean aBoolean) {
ToastUtils.toast("Добавление пользователя успешно!");
mRefreshLayout.autoRefresh();
}
});
(1) Здесь необходимо добавить зависимость RxUtil2:
implementation 'com.github.xuexiangjys:rxutil2:1.1.2'
(2) В методе onCreate()
Activity выполните блокировку:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
RxLifecycle.injectRxLifecycle(this);
}
(3) Затем в запросе используйте оператор compose
RxJava для привязки:
.compose(RxLifecycle.with(this).<Boolean>bindToLifecycle())
(1) По умолчанию фреймворк предоставляет готовый перехватчик логирования HttpLoggingInterceptor
, который можно настроить с помощью XHttpSDK.debug("XHttp")
. Он имеет 5 режимов ведения журнала:
NONE: не вести журнал;
BASIC: регистрировать только «заголовок запроса» и «ответный заголовок»;
HEADERS: регистрировать все заголовки запроса и ответа;
PARAM: регистрировать только параметры запроса и ответа;
BODY: регистрировать всю информацию (по умолчанию).
(2) Если требуется выполнить пользовательскую регистрацию параметров сетевых запросов, то можно наследовать HttpLoggingInterceptor
и реализовать собственный перехватчик регистрации сетевых запросов. Для этого нужно переопределить методы logForRequest
и logForResponse
.
(3) Настройка пользовательского перехватчика логирования:
XHttpSDK.debug(new CustomLoggingInterceptor());
Иногда требуется добавить некоторые фиксированные параметры запроса, но значения этих параметров могут меняться. В этом случае необходимо динамически добавлять параметры запроса [например, токен, время и подпись].
(1) Наследуйте BaseDynamicInterceptor
, реализуйте метод updateDynamicParams
:
@Override
protected TreeMap<String, Object> updateDynamicParams(TreeMap<String, Object> dynamicMap) {
if (isAccessToken()) {//добавлять ли токен
dynamicMap.put("token", TokenManager.getInstance().getToken());
}
if (isSign()) {//добавлять ли подпись
dynamicMap.put("sign", TokenManager.getInstance().getSign());
}
if (isTimeStamp()) {//добавлять ли время
dynamicMap.put("timeStamp", DateUtils.getNowMills());
}
return dynamicMap;//dynamicMap: существующие глобальные параметры + локальные параметры + новые динамические параметры
}
(2) Настройка перехватчика динамического добавления параметров:
XHttpSDK.addInterceptor(new CustomDynamicInterceptor()); //настройка перехватчика динамического добавления параметров
Когда сервер возвращает некоторые уникальные коды ошибок (обычно это ошибки проверки токена, устаревания или слишком частых запросов), необходимо выполнить глобальный перехват и обработку этих запросов. В этом случае требуется определить специальный перехватчик для обработки таких запросов.
(1) Наследовать BaseExpiredInterceptor
, реализовать методы isResponseExpired
и responseExpired
:
/**
* Определить, является ли ответ устаревшим
*
* @param oldResponse
* @param bodyString
* @return {@code true} : устаревший <br> {@code false} : актуальный
*/
@Override
protected ExpiredInfo isResponseExpired(Response oldResponse, String bodyString) {
int code = JSONUtils.getInt(bodyString, ApiResult.CODE, 0);
ExpiredInfo expiredInfo = new ExpiredInfo(code);
switch (code) {
case TOKEN_INVALID:
case TOKEN_MISSING:
expiredInfo.setExpiredType(KEY_TOKEN_EXPIRED)
.setBodyString(bodyString);
break;
case AUTH_ERROR:
expiredInfo.setExpiredType(KEY_UNREGISTERED_USER)
.setBodyString(bodyString);
break;
default:
break;
} (2) **Установка перехватчика для проверки недействительных запросов.**
```XHttpSDK.addInterceptor(new CustomExpiredInterceptor()); // Перехватчик для проверки действительности запросов```
### **Пользовательский API-запрос**
#### **Структура пользовательского API-запроса**
Если вы не хотите использовать стандартный объект `ApiResult` в качестве унифицированного объекта ответа сервера, например, вы хотите использовать следующую структуру ответа:
private int errorCode; // Код ошибки запроса private String errorInfo; // Описание причины ошибки запроса private T result; // Результат запроса private long timeStamp; // Время, возвращённое сервером
(1) Сначала наследуйте объект `ApiResult`, переопределите его методы `getCode()`, `getMsg()`, `isSuccess()`, `getData()` и `setData()`.
```public class CustomApiResult<T> extends ApiResult<T>{
private int errorCode;
private String errorInfo;
private T result;
private long timeStamp;
public int getErrorCode() {
return errorCode;
}
// Другие методы
@Override
public int getCode() {
return errorCode;
}
@Override
public String getMsg() {
return errorInfo;
}
@Override
public boolean isSuccess() {
return errorCode == 0;
}
@Override
public void setData(T data) {
result = data;
}
@Override
public T getData() {
return result;
}}```
(2) При выполнении запроса используйте метод `execute(CallBackProxy)` или `execute(CallClazzProxy)` для выполнения запроса. 60)
// Кэш будет хранить данные в течение 300 секунд, по умолчанию используется постоянное хранение.
// Работают как okhttp, так и пользовательский кэш.
.cacheDiskConverter(new GsonDiskConverter()) // По умолчанию используется new SerializableDiskConverter();
.timeStamp(true)
.execute(new ProgressLoadingCallBack<CacheResult<List<Book>>>(mIProgressLoader) {
@Override
public void onSuccess(CacheResult<List<Book>> cacheResult) {
ToastUtils.toast("Запрос успешно выполнен!");
String from;
if (cacheResult.isFromCache) {
from = "Данные взяты из кэша";
} else {
from = "Данные получены из удалённой сети";
}
showResult(from + "\n" + JsonUtil.toJson(cacheResult.data));
}
@Override
public void onError(ApiException e) {
super.onError(e);
ToastUtils.toast(e.getDisplayMessage());
}
});
## Конфигурация запутывания
#XHttp2 -keep class com.xuexiang.xhttp2.model.** { ; } -keep class com.xuexiang.xhttp2.cache.model.* { ; } -keep class com.xuexiang.xhttp2.cache.stategy.**{;} -keep class com.xuexiang.xhttp2.annotation.** { *; }
#okhttp -dontwarn com.squareup.okhttp3.** -keep class com.squareup.okhttp3.** { ;} -dontwarn okio.* -dontwarn javax.annotation.Nullable -dontwarn javax.annotation.ParametersAreNonnullByDefault -dontwarn javax.annotation.**
-dontwarn retrofit2.** -keep class retrofit2.** { *; } -keepattributes Exceptions
-dontwarn sun.misc.** -keepclassmembers class rx.internal.util.unsafe.ArrayQueueField* { long producerIndex; long consumerIndex; } -keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef { rx.internal.util.atomic.LinkedQueueNode producerNode; } -keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef { rx.internal.util.atomic.LinkedQueueNode consumerNode; }
#Если использовать пакет для анализа Gson, просто добавьте следующие несколько строк, и всё будет работать без ошибок -keepattributes Signature -keep class com.google.gson.stream.** { ; } -keepattributes EnclosingMethod -keep class org.xz_sale.entity.**{;} -keep class com.google.gson.** {;} -keep class com.google.**{;} -keep class sun.misc.Unsafe { ; } -keep class com.google.gson.stream.* { ; } -keep class com.google.gson.examples.android.model.* { *; }
---
## Особая благодарность
https://github.com/zhou-you/RxEasyHttp
## Если вам нравится проект, рассмотрите возможность поддержать его
> Ваша поддержка — это моя мотивация, я опубликую список всех спонсоров в качестве доказательства, пожалуйста, оставьте комментарий перед тем, как сделать пожертвование!

Спасибо следующим спонсорам:
Имя | Сумма | Способ
:-|:-|:-
*Голос | 50¥ | WeChat
**Восток | 5¥ | Alipay
## Контакты
[](http://shang.qq.com/wpa/qunwpa?idkey=9922861ef85c19f1575aecea0e8680f60d9386080a97ed310c971ae074998887)
> Для получения дополнительной информации, пожалуйста, отсканируйте QR-код и подпишитесь на мой личный публичный аккаунт WeChat: [Мой Android с открытым исходным кодом]

[demo-gif]: ./img/demo.gif
[download-svg]: https://img.shields.io/badge/downloads-2.61M-blue.svg
[download-url]: https://github.com/xuexiangjys/XHttp2/blob/master/apk/xhttp2_demo_1.0.apk?raw=true
[download-img]: ./img/download.png
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарии ( 0 )