1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/HarmonyOS-tpc-NoHttp

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Это зеркальный репозиторий, синхронизируется ежедневно с исходного репозитория.
Клонировать/Скачать
Внести вклад в разработку кода
Синхронизировать код
Отмена
Подсказка: Поскольку Git не поддерживает пустые директории, создание директории приведёт к созданию пустого файла .keep.
Loading...
README.md

Добавление зависимостей

Способ 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();

Примечание:

  1. Все эти настройки можно настроить одновременно, а также можно настроить только некоторые из них.
  2. addHeader() и addParam() можно вызывать несколько раз, и значения не будут перезаписаны.
  3. При использовании DiskCacheStore() по умолчанию кэш сохраняется в context.getCacheDir(), при использовании DiskCacheStore(path) кэш сохраняется в каталоге path, но обратите внимание на разрешения на чтение и запись SD-карты и разрешения во время выполнения: AndPermission.

Пример конфигурации кэша на 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:

  1. IRequest (автор: Юань Шэньбинь).
  2. NohttpRxUtils (автор: Ли Ци).

Разработчики могут использовать их в качестве справочного материала или напрямую использовать.

Синхронный запрос и асинхронный запрос

Ядром модуля запроса 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 )

Вы можете оставить комментарий после Вход в систему

Введение

Описание недоступно Развернуть Свернуть
Apache-2.0
Отмена

Обновления

Пока нет обновлений

Участники

все

Недавние действия

Больше нет результатов для загрузки
1
https://gitlife.ru/oschina-mirror/HarmonyOS-tpc-NoHttp.git
git@gitlife.ru:oschina-mirror/HarmonyOS-tpc-NoHttp.git
oschina-mirror
HarmonyOS-tpc-NoHttp
HarmonyOS-tpc-NoHttp
master