Добавление зависимостей
Способ 1:
Через library сгенерировать har-пакет, добавить har-пакет в папку libs. В файле entry Gradle добавить следующий код:
implementation fileTree(dir: 'libs', include: ['.jar', '.har'])
Способ 2:
Для всех проектов: repositories { mavenCentral() }
Если используется HttpURLConnection в качестве сетевого слоя: implementation 'io.openharmony.tpc.thirdlib:noHttpLibrary:1.0.1'
Если нужно использовать OkHttp в качестве сетевого слоя, то дополнительно: implementation 'io.openharmony.tpc.thirdlib:noHttp-okhttpLibrary:1.0.1
Общая инициализация
После прямой инициализации всё работает по умолчанию. NoHttp.initialize(this);
Расширенная инициализация
InitializationConfig config = InitializationConfig.newBuilder(context) // Другие настройки. ... .build();
NoHttp.initialize(config);
Что касается таймаутов, многие люди не до конца понимают или неправильно понимают, я написал ответ на Zhihu, пожалуйста, обратитесь к нему: HTTP 在什么情况下会请求超时?
Ниже приведены подробности о других настройках, которые были опущены выше.
InitializationConfig config = InitializationConfig.newBuilder(context) // Глобальное время ожидания подключения к серверу, в миллисекундах, по умолчанию 10 секунд. connectionTimeout(30 * 1000) // Общее время ожидания ответа сервера, в миллисекундах, по умолчанию 10 секунд. readTimeout(30 * 1000) // Настройка кэша, по умолчанию используется база данных DBCacheStore, сохранение на SD-карту использует DiskCacheStore. cacheStore( // Если кэш не используется, setEnable(false) отключает его. new DBCacheStore(context).setEnable(true) ) // Конфигурация Cookie, по умолчанию сохраняется в базе данных DBCookieStore, разработчик может реализовать свой собственный CookieStore интерфейс. cookieStore( // Если cookie не поддерживается, setEnable(false) отключит его. new DBCookieStore(context).setEnable(true) ) // Сетевой уровень, по умолчанию URLConnectionNetworkExecutor, если вы хотите использовать OkHttp: OkHttpNetworkExecutor. networkExecutor() // Общий заголовок, add — это добавление, многократный вызов add не перезапишет предыдущий. addHeader() // Общий параметр, add — это добавление, многократный вызов add не перезапишет предыдущий. addParam() sslSocketFactory() // Общий SSLSocketFactory. hostnameVerifier() // Общий HostnameVerifier. retry(x) // Общее количество попыток, после настройки каждый неудачный запрос будет повторяться x раз. .build();
Примечание:
Пример конфигурации кэша на SD-карте:
InitializationConfig config = InitializationConfig.newBuilder(context) .cacheStore( new DiskCacheStore(context) // Сохраняется в папке context.getCahceDir(). // new DiskCacheStore(путь) // Сохраняется в пути, путь — это абсолютный путь, указанный разработчиком. ) .build();
Примеры добавления глобальных заголовков запросов и параметров:
InitializationConfig config = InitializationConfig.newBuilder(context) .addHeader("Token", "123") // Глобальный заголовок запроса. .addHeader("Token", "456") // Глобальный заголовок запроса, не заменит предыдущий. .addParam("AppVersion", "1.0.0") // Глобальные параметры запроса. .addParam("AppType", "ohos") // Глобальные параметры запроса. .addParam("AppType", "iOS") // Глобальные параметры запроса, не заменят предыдущие два. .build();
Необходимые разрешения
<..READ_EXTERNAL_STORAGE" /> <..WRITE_EXTERNAL_STORAGE" /> <..INTERNET" /> <..ACCESS_NETWORK_STATE" /> <...ACCESS_WIFI_STATE" />
Режим отладки
Logger.setDebug(true);// Включает режим отладки NoHttp, после включения можно увидеть процесс запроса, журналы и сообщения об ошибках. Logger.setTag("NoHttpSample");// Устанавливает тег для печати Log.
После включения режима отладки NoHttp можно увидеть процесс запроса, логи и сообщения об ошибках, и Log очень хорошо организован.
Поэтому, если у разработчика возникнут какие-либо проблемы во время разработки, включите режим отладки, и все проблемы станут очевидными.
Сторонние асинхронные фреймворки
Ядро NoHttp — это синхронный метод запроса, асинхронные методы NoHttp (AsyncRequestExecutor, RequestQueue) основаны на синхронных запросах, поэтому RxJava, AsyncTask и другие могут хорошо инкапсулировать NoHttp. Пример запроса строки:
StringRequest request = new String(url, RequestMethod.GET); Response response = SyncRequestExecutor.INSTANCE.execute(request); if (response.isSucceed()) { // Запрос выполнен успешно. } else { // Ошибка запроса, получение ошибки: Exception e = response.getException(); }
Вот два проекта из группы друзей, основанных на RxJava + NoHttp:
Разработчики могут использовать их в качестве справочного материала или напрямую использовать.
Ядром модуля запроса NoHttp является синхронный запрос: SyncRequestExecutor; асинхронный запрос NoHttp делится на два типа: один — асинхронный исполнитель запроса: AsyncRequestExecutor, другой — очередь запросов: RequestQueue.
Синхронный запрос
Пример запроса строки:
StringRequest req = new String("http://api.nohttp.net", RequestMethod.POST); Response response = SyncRequestExecutor.INSTANCE.execute(req); if (response.isSucceed()) { // Запрос выполнен успешно. } else { // Ошибка запроса, получение ошибки: Exception e = response.getException(); }
Конечно, синхронный запрос подходит только для использования в дочернем потоке, потому что основной поток ohos не разрешает инициировать сетевые запросы. Конечно, если использовать RxJava, AsyncTask и т. д., чтобы инкапсулировать синхронные запросы, их также можно использовать в основном потоке, но NoHttp предоставляет два способа асинхронного запроса, которые можно использовать непосредственно в основном потоке. 异步请求-AsyncRequestExecutor
Cancelable cancel = AsyncRequestExecutor.INSTANCE.execute(0, request, new SimpleResponseListener<String>() {
@Override
public void onSucceed(int what, Response<String> response) {
// 请求成功。
}
@Override
public void onFailed(int what, Response<String> response) {
// 请求失败。
}});
// 如果想取消请求:
cancel.cancel();
// 判断是否取消:
boolean isCancelled = cancel.isCancelled();```
Этот способ основан на использовании пула потоков и не имеет особенностей очереди с приоритетом.
**异步 запрос — RequestQueue**
```RequestQueue queue = NoHttp.newRequestQueue(); // 默认 три параллельных потока, здесь можно передать количество параллельных потоков.
...
// Отправляем запрос:
queue.add(what, request, listener);
...
// После использования необходимо закрыть очередь, чтобы освободить процессор:
queue.stop();```
Также можно создать собственную очередь:
```// Также можно создать свою очередь:
RequestQueue queue = new RequestQueue(5);
queue.start(); // Запускаем очередь.
...
// Отправляем запрос:
queue.add(what, request, listener);
...
// После использования необходимо остановить очередь:
queue.stop();```
Многие разработчики имеют привычку создавать новую очередь для каждого отправленного запроса, что является **неправильным использованием**. Например, один из разработчиков создал метод:
```public <T> void request(Request<T> request, SampleResponseListener<T> listener) {
RequestQueue queue = NoHttp.newRequestQueue(5);
queue.add(0, request, listener);}```
Ещё раз подчёркиваю, что **этот подход является неправильным**.
Для разработчиков, которые хотят вызывать очередь напрямую для отправки запросов, `NoHttp` также предоставляет одноэлементный режим:
```// Например, одноэлементный режим очереди запросов:
NoHttp.getRequestQueueInstance().add...
...
// Например, одноэлементный режим загрузки:
NoHttp.getDownloadQueueInstance().add...```
Конечно, разработчики могут напрямую использовать асинхронный исполнитель запросов `AsyncRequestExecutor`, который рекомендуется к использованию.
### Правильное использование очереди
Есть два правильных способа использования очереди: один — использовать отдельную очередь для каждой страницы и останавливать очередь при выходе со страницы; другой — использовать одну общую очередь и останавливать её при выходе из приложения. Я рекомендую второй способ, то есть использовать общую очередь `RequestQueue`.
Способ 1: разработчик может создать базовый класс `BaseActivity`, в котором будет создана очередь в методе `onCreate()` и остановлена в методе `onDestory()`:
```public class BaseActivity extends Activity {
private RequestQueue queue;
@Override
public void onCreate(Bundle savedInstanceState) {
queue = NoHttp.newRequestQueue();
}
// Предоставить дочерним классам возможность использовать запросы.
public <T> void request(int what, Request<T> request, SimpleResponseListener<T> listener) {
queue.add(what, request, listener);
}
@Override
public void onDestory() {
queue.stop();
}```
Способ 2: использовать одноэлементный шаблон для создания класса, который будет отвечать за все запросы в приложении, и поддерживать только одну очередь `RequestQueue`:
```StringRequest request = new StringRequest("http://api.nohttp.net", RequestMethod.POST);
CallServer.getInstance().request(0, request, listener);```
Класс `CallServer` не предоставляется `NoHttp`, а должен быть создан разработчиком, так как здесь можно реализовать бизнес-логику приложения, поэтому разработчик может свободно экспериментировать:
```public class CallServer {
private static CallServer instance;
public static CallServer getInstance() {
if (instance == null)
synchronized (CallServer.class) {
if (instance == null)
instance = new CallServer();
}
return instance;
}
private RequestQueue queue;
private CallServer() {
queue = NoHttp.newRequestQueue(5);
}
public <T> void request(int what, Request<T> request, SimpleResponseListener<T> listener) {
queue.add(what, request, listener);
}
// Полностью выйти из приложения и вызвать этот метод, чтобы освободить ресурсы процессора.
public void stop() {
queue.stop();
}```
Обратите внимание, что в коде выше `listener` — это интерфейс обратного вызова, который принимает результаты запроса. На самом деле это `OnResponseListener`, и он должен реализовывать четыре метода. Иногда реализация всех четырёх методов может показаться сложной, поэтому `NoHttp` предоставляет реализацию по умолчанию `SimpleResponseListener`, где разработчик должен реализовать только необходимые методы.
> В коде выше при добавлении запроса в очередь появляется параметр `what`, который аналогичен параметру `what` в `Handler` для сообщений. Он используется для определения того, какой запрос получил ответ.
# Другие особенности и способы использования
В следующем разделе будут представлены различные типы запросов, предоставляемые `NoHTTP`, такие как `String`, `Bitmap`, `JSONObject` и другие. Обычно разработчики используют `String` запросы, а затем преобразуют их в `JSON`, `XML` или `JavaBean`. Независимо от используемой сетевой библиотеки, это не лучший способ работы. Причины следующие:
1. Каждый запрос требует преобразования `String` в `XML`, `JSON` и т. д., что усложняет логику и приводит к избыточному коду.
2. Если объём данных большой, процесс преобразования может занять много времени, что ухудшит пользовательский опыт (приложение может зависнуть).
Поэтому я написал статью о том, как объединить бизнес-логику и напрямую запрашивать `JavaBean`, `List`, `Map` и `Protobuf`:
[http://blog.csdn.net/yanzhenjie1003/article/details/70158030](http://blog.csdn.net/yanzhenjie1003/article/details/70158030)
## Запрос различных типов данных
Тип данных, запрашиваемых с помощью `NoHTTP`, определяется классом `Request`. `NoHTTP` уже предоставляет классы для запроса `String`, `Bitmap`, `JsonObject` и `JsonArray`:
// Запрос String: StringRequest request = new StringRequest(url, method);
// Запрос Bitmap: ImageRequest request = new ImageRequest(url, method);
// Запрос JSONObject: JsonObjectRequest request = new JsonObjectRequest(url,
// Запрос JSONArray:
JsonArrayRequest request = new JsonArrayRequest(url, method);
Сборка URL
Эта возможность была добавлена начиная с версии 1.1.3 и является одним из основных улучшений этого обновления — это метод сборки URL, например, если сервер использует RESTFUL API, то при запросе информации о пользователе URL может выглядеть следующим образом:
http://api.nohttp.net/rest/<userid>/userinfo
Здесь <userid>
— это имя пользователя или идентификатор пользователя, который должен быть динамически заменён, а затем использован для получения информации о пользователе. Раньше это делалось так:
String userName = AppConfig.getUserName();
String url = "http://api.nohttp.net/rest/%1$s/userinfo";
url = String.format(Locale.getDefault(), url, userName);
StringRequest request = new StringRequest(url);
...
Теперь это можно сделать так:
String url = "http://api.nohttp.net/rest/";
StringRequest request = new StringRequest(url)
request.path(AppConfig.getUserName())
request.path("userinfo")
...
То есть разработчики теперь могут динамически собирать URL.
Добавление заголовков запроса
Заголовки запросов поддерживают добавление различных типов данных, таких как String
, int
, long
, double
, float
и т. д.
StringRequest request = new StringRequest(url, RequestMethod.POST);
.addHeader("name", "yanzhenjie") // String тип.
.addHeader("age", "18") // int тип.
.setHeader("sex", "男") // setHeader заменит уже существующий ключ.
...
Добавление параметров
Заголовки запросов также поддерживают добавление различных типов данных, таких как Binary
, File
, String
, int
, long
, double
, float
и т.д.
StringRequest request = new StringRequest(url, RequestMethod.POST);
.add("name", "严振杰") // String тип
.add("age", 18) // int тип
.add("age", "20") // add метод не заменит уже существующий ключ, поэтому age будет иметь два значения: 18, 20.
.set("sex", "女") // set заменит уже существующий ключ.
.set("sex", "муж") // например, в итоге sex будет иметь только одно значение: муж.
// Добавление File
.add("head", file)
.add("head", new FileBinary(file))
// Добавление Bitmap
.add("head", new BitmapBinary(bitmap))
// Добавление ByteArray
.add("head", new ByteArrayBinary(byte[]))
// Добавление InputStream
.add("head", new InputStreamBinary(inputStream));
Кроме того, следует отметить, что старый метод Request#add(Map<String, String>)
был обновлён до Request#add(Map<String, Object>)
, что позволяет разработчикам, предпочитающим использовать Map
для упаковки параметров, добавлять следующие типы параметров в Map
:
String, File, Binary, List<String>, List<Binary>, List<File>, List<Object>
Пример кода:
Map<String, Object> params = new HashMap<>();
params.put("name", "yanzhenjie");
params.put("head", new File(path));
params.put("logo", new FileBinary(file));
params.put("age", 18);
params.put("height", 180.5);
List<String> hobbies = new ArrayList<>();
hobbies.add("篮球");
hobbies.add("帅哥");
params.put("hobbies", hobbies);
List<File> goods = new ArrayList<>();
goods.add(file1);
goods.add(file2);
params.put("goods", goods);
List<Object> otherParams = new ArrayList<>();
otherParams.add("yanzhenjie");
otherParams.add(1);
otherParams.add(file);
otherParams.add(new FileBinary(file));
params.put("other", otherParams);
Конечно, в реальной разработке использование одного и того же ключа для запроса файла вместе с другими данными практически невозможно, но вполне возможно, что String
или int
будут использоваться с одним и тем же ключом.
Существует две формы загрузки файлов: первая — загрузка в виде формы, вторая — загрузка через тело запроса (request body
). Рассмотрим первую форму загрузки в виде формы:
StringRequest request = ...
request.add("file", new FileBinary(file));
File
, Bitmap
, InputStream
, ByteArray
.StringRequest request = ...
request.add("file1", new FileBinary(File));
request.add("file2", new FileBinary(File));
request.add("file3", new InputStreamBinary(InputStream));
request.add("file4", new ByteArrayBinary(byte[]));
request.add("file5", new BitmapBinary(Bitmap));
StringRequest request = ...;
fileList.add("image", new FileBinary(File));
fileList.add("image", new InputStreamBinary(InputStream));
fileList.add("image", new ByteArrayBinary(byte[]));
fileList.add("image", new BitmapStreamBinary(Bitmap));
Или:
StringRequest request = ...;
List<Binary> fileList = ...;
fileList.add(new FileBinary(File));
fileList.add(new InputStreamBinary(InputStream));
fileList.add(new ByteArrayBinary(byte[]));
fileList.add(new BitmapStreamBinary(Bitmap));
request.add("file_list", fileList);
Вторая форма загрузки через тело запроса (request body) очень разнообразна и позволяет отправлять не только файлы, но и любые потоки данных. Подробности смотрите в разделе «Отправка тела запроса».
Отправка тела запроса
Отправка тела запроса включает отправку Json
, отправку String
, отправку Xml
и отправку потоков. На самом деле все они в конечном итоге преобразуются в поток и отправляются, поэтому разработчики могут использовать этот метод для отправки файлов.
Конкретный способ использования следующий:
// Отправка обычной строки
request.setDefineRequestBody(String, ContentType);
// Отправка json-строки
request.setDefineRequestBodyForJson(JsonString)
// Отправка объекта JSONObject, который на самом деле является json-строкой
request.setDefineRequestBodyForJson(JSONObject)
// Отправка xml-строки
request.setDefineRequestBodyForXML(XmlString)
// Отправка тела шрифта, такого как файл (это отличается от загрузки формы), который можно преобразовать в InputStream и отправить
``` **request.setDefineRequestBody(InputStream, ContentType)**
*Пример отправки файла:*
File file = ...; FileInputStream fileStream = new FileInputStream(file);
StringRequest request = new StringRequest(url, RequestMethod.POST); request.setDefineRequestBody(fileStream, Headers.HEAD_VALUE_CONTENT_TYPE_OCTET_STREAM);
**Пять основных режимов кэширования**
`NoHttp` поддерживает кэширование в базе данных, кэширование на SD-карту и т. д., и независимо от того, где находится кэш (в базе данных или на SD), `NoHttp` всегда шифрует данные, которые необходимо настроить при инициализации.
Следует отметить, что если вы хотите кэшировать данные на SD-карте на телефоне с версией 6.0 и выше, вам необходимо запросить разрешение на выполнение перед запросом, если разработчик не понимает разрешения на выполнение, вы можете обратиться к этой статье [ohos 6.0 运行时权限管理最佳实践](http://blog.csdn.net/yanzhenjie1003/article/details/52503533). Автор рекомендует использовать этот фреймворк для управления разрешениями во время выполнения: [AndPermission](https://github.com/yanzhenjie/AndPermission).
1. **Режим по умолчанию** — это режим по умолчанию, когда не установлен режим кэширования. Этот режим реализует содержимое HTTP-протокола, например, ответный код равен 304, конечно, он также будет связан с E-Tag и LastModify и другими заголовками.
StringRequest request = new StringRequest(url, method); request.setCacheMode(CacheMode.DEFAULT);
2. **Кэширование данных при неудачном запросе сервера** — запрос сервера успешен, возвращается серверные данные, в случае неудачи запроса сервера считываются данные из кэша и возвращаются.
StringRequest request = new StringRequest(url, method); request.setCacheMode(CacheMode.REQUEST_NETWORK_FAILED_READ_CACHE);
3. **Если есть кэш, то сразу успешно, без кэша — запрос к серверу** — ядро ImageLoader помимо оптимизации памяти также обнаруживает наличие изображения внутри страны и использует его напрямую, а если нет, запрашивает сервер.
Запрос `String`, кэширование `String`:
StringRequest request = new StringRequest(url, method); // 非标准Http协议,改变缓存模式为IF_NONE_CACHE_REQUEST_NETWORK request.setCacheMode(CacheMode.IF_NONE_CACHE_REQUEST_NETWORK);
Запрос изображения, кэширование изображения:
ImageRequest request = new ImageRequest(url, method); request.setCacheMode(CacheMode.IF_NONE_CACHE_REQUEST_NETWORK);
4. **Только сетевой запрос** — независимо от обстоятельств будет только сетевой запрос, поддержка HTTP 304 и других действий по умолчанию отсутствует.
ImageRequest request = new ImageRequest(url, method); request.setCacheMode(CacheMode.ONLY_REQUEST_NETWORK); ...
5. **Только чтение кэша** — независимо от ситуации будет считываться только кэш, сетевой запрос и другие операции отсутствуют.
Request request = NoHttp.createImageRequest(imageUrl); request.setCacheMode(CacheMode.ONLY_READ_CACHE);
**Примечание**: если разработчик хочет сначала получить данные из кэша, а затем выполнить сетевой запрос, разработчик может сначала инициировать запрос только для чтения кэша, затем инициировать сетевой запрос только запроса, **но автор уже готовит NoHttp 2.0, и в будущем он будет представлен разработчикам в новом облике.**
Режим кэширования поддерживает кэширование любых данных, поскольку `NoHttp` сохраняет данные в виде `byte[]`, а при чтении данных преобразует `byte[]` в данные, требуемые разработчиком, поэтому кэш `NoHttp` может поддерживать любые пользовательские запросы.
**Пользовательский запрос**
Все встроенные запросы `NoHttp` наследуются от класса `RestRequest`, поэтому пользовательский запрос также должен наследовать от `RestRequest`. Введите тип данных, который вы хотите запросить, в качестве параметра типа, и, наконец, проанализируйте данные сервера как тип данных, который вам нужен, в методе `parseResponse()`.
*FastJsonRequest*
```java
public class FastJsonRequest extends RestRequestor<JSONObject> {
public FastJsonRequest(String url) {
this(url, RequestMethod.GET);
}
public FastJsonRequest(String url, RequestMethod requestMethod) {
super(url, requestMethod);
}
@Override
public JSONObject parseResponse(Headers header, byte[] body) throws Throwable {
String result = StringRequest.parseResponseString(headers, body);
return JSON.parseObject(result);
}
}
Это всего лишь демонстрация пользовательского запроса. Например, разработчики могут также инкапсулировать бизнес-запросы в Request
и напрямую запрашивать сложные данные, такие как JavaBean, List и т.д., связанные с бизнесом. Пожалуйста, обратитесь к этой статье:
http://blog.csdn.net/yanzhenjie1003/article/details/70158030
Загрузка файлов
Поскольку код загрузки файлов довольно длинный, здесь приводится ключевая часть, а конкретные детали можно найти в демонстрационном коде.
Ядром модуля загрузки файлов NoHttp
является синхронный запрос: SyncDownloadExecutor
; асинхронная загрузка файлов NoHttp
имеет только один способ очереди загрузки: DownloadQueue
, конечно, вы также можете использовать SyncDownloadExecutor
для объединения с RxJava
, AsyncTask
и другими способами для реализации асинхронной загрузки.
Синхронная загрузка — SyncDownloadExecutor
DownloadRequest request = new DownloadRequest(url, RequestMethod.GET, fileFolder, true, true);
SyncDownloadExecutor.INSTANCE.execute(0, request, new SimpleDownloadListener() {
@Override
public void onStart(int what, boolean resume, long range, Headers headers, long size) {
// 开始下载,回调的时候说明文件开始下载了。
// 参数1:what。
// 参数2:是否是断点续传,从中间开始下载的。
// 参数3:如果是断点续传,这个参数非0,表示之前已经下载的文件大小。
// 参数4:服务器响应头。
// 参数5:文件总大小,可能为0,因为服务器可能不返回文件大小。
}
@Override
public void onProgress(int what, int progress, long fileCount, long speed) {
// 进度发生变化,服务器不返回文件总大小时不回调,因为没法计算进度。
// 参数1:what。
// 继续...
}
@Override
public void onFinish(int what, String filePath) {
// 文件下载完成,回调。
// 参数1:what。
// 参数2:下载后的文件路径。
}
});
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарии ( 0 )