Не так давно столкнулся с задачей по отображению прогресс бара при отправке файла. Начал искать информацию по данной теме и понял, что ничего толкового на русском языке нет. Подумал-подумал и решил написать свою статью о способах отслеживания прогресса при загрузке и отправке файлов. Для отправки есть один точный вариант, который работает как часы. Основная задача — переопределить класс okhttp3.RequestBody. Для получения прогресса во время загрузки есть такой же простой вариант, аннотация @Streaming. Eсть еще один вариант, но он более продвинутый и построен на встроенном DownloadManager. Как говорилось ранее, основная задача — это сделать собственный класс RequestBody, назовем его ProgressRequestBody и унаследуем от RequestBody. В contentType мы просто должны вернуть MediaType, который можно передать в конструктор нашего класса, а перед этим достать его из URI с помощь ContentResolver.getType() В функцииwriteTo нам необходимо записать в sink наш файл частями, размер которых вы определяете сами. Я возьму DEFAULT_BUFFER_SIZE, который равен  бит. В это же время нужно добавить коллбек, который будет возвращать уже записанное число бит. Итоговый код  ProgressRequestBody получился таким: Функция inputStream.read(buffer) возвращает количество байт записанных в buffer, если данных для чтение не осталось, то функция возвращает -1 В функцию интерфейса сервиса необходимо добавить аннотацию @Multipart, а параметром передать файл с аннотацией @Part и типом MultipartBody.Part Функция для отправки файла примет следующий вид: В итоге один простой класс позволяет реализовать отслеживание статуса отправки, который можно показать пользователю. Самый подходящий вариант для загрузки файлов - это использование DownloadManager. DownloadManager — системный сервис, который обрабатывает длительные загрузки по протоколу HTTP. Этот сервис заслуживает отдельной статьи, так как имеет множество настроек и методов для работы с загрузкой. Ниже приведен пример, на сколько просто можно загрузить файл с его помощью: Для базовой загрузки этого достаточно. Удобно, не правда ли? Последней строкой запрос добавляется в очередь системного загрузчика. После этого должно появиться уведомление о начале загрузке, в котором будет отображаться прогресс загрузки + кнопка отмены. Для нашего случая этого не достаточно, прогресс, конечно, отображается, но нам необходимо показывать его в нашем приложении. Для этого сделаем собственный ContentObserver и зарегистрируем его. Метод onChange() срабатывает, когда содержимое по данному uri изменяется. В этот момент необходимо получить информацию о загрузке из DownloadManager. Для таких случаев у менеджера есть метод DownloadManager.query(Query query), инстанс которого можно получить с помощью метода setFilterById(): В то же время downloadId можно достать из уже знакомого метода enqueue(): Метод DownloadManager.query(Query query) возвращает cursor, из которого мы и будем доставать необходимую информацию. Далее получаем значения количества загруженных байтов и размер целого файла: И остается только отобразить пользователю информацию, но сделать это нужно только после того, как totalбудет неравен -1 (когда данные действительно появятся): В итоге получаем ContentObserver следующего вида: Для регистрации ContentObserver нам нужен URI места, куда загружается файл. Его можно получить все из того же DownloadManager.query(Query query) следующим образом: Будьте внимательны, при установке setDestinationInExternalPublicDir или setDestinationInExternalFilesDir localUri будет возвращать null. Потому при установке собсвенного пути не забывайте запоминать URI. И последний штрих — это регистрация ContentObserver: Все заворачиваем в функцию и получаем: P.S. Если вы хотите скрыть уведомление от системного загрузчика, то можете установить флаг VISIBILITY_HIDDEN в .setNotificationVisibility, но для этого необходимо добавить пермишен в манифест: Переходим ко второму варианту, который выглядит просто только на первый взгляд. Сразу после того, как вы решите сделать правильную и безопасную реализацию, вы столкнетесь с большим количеством вопросов: “А что будет после закрытия приложения? Что будет, если перейти на другой фрагмент?” Для правильного функционирования нужно будет написать кучу кода и воспользоваться WorkManager’ом для работы в фоне (но и тут есть нюансы, т.к WorkManager не дает гарантии того, что ваша работа будет запущена в тот же момент, когда вы нажали на кнопку “Загрузить”). Для реализации этого варианта потребуется аннотация @Streaming. Она позволяет обрабатывать ответ без преобразования тела в byte[], поэтому у нас есть возможность оперировать скачиваемым потоком данных так, как нам это необходимо. Остется добавить ее к фунции сервиса: И добавить функцию, которая во время считывания данных будет рассчитывать прогресс загрузки и отправлять его UI: И все готово. Мы получили проценты, а затем отобразили их пользователю. P.S. Не забудьте сохранить скачанный файл P.P.S Надеюсь, статья была полезной. Буду благодарен за обратную связь!  Middle Android Developer