Невозможно отследить ход загрузки с помощью HttpURLConnection и OkHttp. - PullRequest
0 голосов
/ 06 мая 2018

По какой-то причине HttpURLConnection, похоже, буферизует загружаемые данные независимо от того, что я пытаюсь. Я могу показать процент выполнения данных, но ясно, что прогресс продвигается слишком быстро, пока данные не передаются с такой высокой скоростью.

Принимающий сервер не находится во внутренней сети, но где-то размещен. Пограничный маршрутизатор сокращает полосу загрузки до 2 Мбит, чтобы имитировать медленную сеть, и на графике пропускной способности маршрутизатора я вижу график скорости передачи данных для устройства разработки. Точка доступа WiFi также позволяет мне видеть график скорости передачи данных, и он выглядит так же, как и граничный маршрутизатор, поэтому ни одно устройство в интрасети не буферизует данные. Это определенно девелоперское устройство (Nexus 5X)

Ниже приводится код, который используется:

HttpURLConnection hucConnection = (HttpURLConnection) url.openConnection();
//hucConnection.setUseCaches(false); // does not solve the issue
//hucConnection.setDefaultUseCaches(false); // does not solve the issue
//hucConnection.setAllowUserInteraction(true); // does not solve the issue
hucConnection.setConnectTimeout(6 * 1000);
hucConnection.setReadTimeout(30 * 1000);
hucConnection.setRequestProperty("content-type", "application/json; charset=UTF-8");
hucConnection.setRequestMethod("POST");
hucConnection.setDoInput(true);
hucConnection.setDoOutput(true);

// Data to transfer
byte[] bData = joTransfer.toString().getBytes("UTF-8");
int iDataLength = bData.length;

//hucConnection.setRequestProperty("content-transfer-encoding", "binary"); // does not solve the issue

// use compression
hucConnection.setRequestProperty("content-encoding", "deflate");
ByteArrayOutputStream stream = new ByteArrayOutputStream();
Deflater deflater = new Deflater(Deflater.DEFAULT_COMPRESSION);
DeflaterOutputStream zip = new DeflaterOutputStream(stream, deflater);
zip.write(bData);
zip.close();
deflater.end();
byte[] bZippedData = stream.toByteArray();
Integer iZippedDataLength = bZippedData.length;


int iChunk = 1000;
hucConnection.setChunkedStreamingMode(iChunk);
//hucConnection.setFixedLengthStreamingMode(iZippedDataLength); // does not solve the issue
hucConnection.connect();

OutputStream osOutputStream = hucConnection.getOutputStream();

// FROM HERE ---->>>
int iUploadedLength;
for (iUploadedLength = 0; iUploadedLength < iZippedDataLength - iChunk; iUploadedLength += iChunk) {
    LogWrapper.e(TAG, "l -> f:" + iUploadedLength + " t:" + (iUploadedLength+iChunk));
    osOutputStream.write(Arrays.copyOfRange(bZippedData, iUploadedLength , iUploadedLength+iChunk));
    osOutputStream.flush();
}
LogWrapper.e(TAG, "r -> f:" + iUploadedLength + " t:" + iZippedDataLength);
osOutputStream.write(Arrays.copyOfRange(bZippedData, iUploadedLength, iZippedDataLength));
osOutputStream.flush();
osOutputStream.close();
// <<<---- TO HERE ---- XXXXXXXXX max 1 second XXXXXXXXX


// FROM HERE ---->>>
int iResponseCode = hucConnection.getResponseCode();
// <<<---- TO HERE ---- XXXXXXXXX about 10 seconds XXXXXXXXX

if (iResponseCode != HttpURLConnection.HTTP_OK) {
  ...

Я ожидал, что вызовы osOutputStream.flush(); заставят HttpURLConnection отправить данные на сервер, но по какой-то причине этого не происходит.

Кажется, что где-то буферизуется, потому что после osOutputStream.close(); и до hucConnection.getResponseCode(); данные загружаются на сервер.

Все переводы (загрузка и выгрузка) работают правильно, данные не повреждены.

Есть ли способ исправить это или альтернатива использованию HttpURLConnection? Я читал, что класс Socket не имеет этой проблемы, но я не уверен, правильно ли он обрабатывает перенаправления и тому подобное. Мне не нужно использовать куки или другие вещи.

Апрокс. 10 секунд, необходимых для завершения hucConnection.getResponseCode();, - это когда загружено около 3 МБ (3 МБ * 8b / B = 24 МБ, 24 МБ / 2 МБ / с = 12 с), загруженные данные отправляются после этого вызова. Ход загруженных данных является точным.

Возможно ли, что сторонняя библиотека изменяет поведение HttpURLConnection и выполняет прокси? Нравится Firebase или что-то? Я уже отключил Crashlytics, но я думаю, что Firebase также выполняет сбор статистики (время отклика). Я думаю, что у меня были некоторые странные проблемы около 1-2 месяцев назад в другом приложении, где я получал проблему с ошибкой прокси в разрешении доменного имени, как будто что-то внутри Android пропускало сетевой трафик.

Я собираюсь попробовать OkHttp, у одного из получателей есть пример пост-трансляции (https://github.com/square/okhttp/wiki/Recipes)

Обновление: я реализовал это, используя okhttp3, следуя вышеупомянутому получателю. У меня там точно такая же проблема.

Это на Android 8.1

Сервер является экземпляром nginx.

Я также запустил приложение на экземпляре эмулятора Genymotion, той же ОС, и похоже, что там лучше, но проблема все еще, кажется, присутствует. Хотя радикальное ограничение на граничном маршрутизаторе не влияет на Nexus 5X, оно влияет на эмулятор. Но, тем не менее, даже точность отслеживания загрузки эмулятора оставляет желать лучшего.

Имеет ли смысл использовать для этого соединение WebSocket? Это было бы моим последним средством.

1 Ответ

0 голосов
/ 06 мая 2018

Логика для загрузки используется в AsyncTask, но я думаю, что она должна быть такой же (только переключающий вход> выход и т. Д.)

InputStream inputStream = null;

try {
    try {
    OutputStream outputStream = new FileOutputStream(documentFile, false);

    try {
        inputStream = httpConn.getInputStream();
        byte[] buffer = new byte[4 * 1024]; // or other buffer size
        long downloaded = 0;
        long target = dataLength;
        int readed;

        long updateSize = target / 10;
        long updateHelp = 0;

        while ((readed = inputStream.read(buffer)) != -1) {
            downloaded += readed;
            updateHelp += readed;
            if (updateHelp >= updateSize) {
                updateHelp = 0;
                publishProgress(downloaded, target);
            }
            outputStream.write(buffer, 0, readed);
            if (isCancelled()) {
                return false;
            }
        }
        outputStream.flush();
        outputStream.close();
        return true;
    } catch (Exception e) {
        return false;
    }
} catch (Exception e) {
    e.printStackTrace();
} finally {
    if (inputStream != null) {
        inputStream.close();
    }
}
} catch (Exception e) {
    e.printStackTrace();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...