В прошлой статье я рассказывал о том, как скачивать файлы из сети и сохранять их на диск, а так же использовать MediaScanner для того, чтобы сделать файлы доступными другим приложениям (например галерее, если это фотография или видео).
Сегодня же хотелось бы рассказать о более интересном и предпочтительном способе скачивания файлов — использовании системного сервиса DownloadManager.
DownloadManager это такой сервис платформы Android, который предоставляет весь спектр функциональности, связанной с загрузкой файлов. Он сам начнет скачивание, корректно отреагирует на обрыв связи или смену типа сети, запустит скачивание по новой (если был обрыв связи), создаст и отобразит уведомление о загрузке файла, уведомит MediaScanner о том, что файл загружен и многое другое.
Для корректного использования DownloadManager нам понадобится:
Создать свой класс — наследник BroadcastReceiver, в котором мы будем реагировать на интенты, отсылаемые DownloadManager’ом;
Подготовить URI с ресурсом, который нужно загрузить;
Создать реквест с нашим URI и другими настройками DownloadManager;
Запустить скачивание.
Итак перво наперво — напишем класс-наследник BroadcastReceiver и реализуем логику, которая покажет Toast, когда наш файл скачается:
/// <summary>
/// Ресивер, который принимает информацию о состоянии загружаемых файлов приложением (задача только одна - отобразить сообщение об успешной загрузке)
/// </summary>
public class DownloadManagerReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
if (intent.Action == DownloadManager.ActionDownloadComplete)
{
//это не совсем правильно с точки зрения производительности, лучше передать ссылку на менеджер извне
DownloadManager downloadManager = (DownloadManager)context.GetSystemService(Activity.DownloadService);
long downloadId = intent.GetLongExtra(DownloadManager.ExtraDownloadId, -1);
if (downloadId == -1)
{
return;
}
DownloadManager.Query query = new DownloadManager.Query();
query.SetFilterById(new long[]{ downloadId });
ICursor cursor = downloadManager.InvokeQuery(query);
if (cursor != null && cursor.MoveToFirst())
{
if (DownloadStatus.Successful == (DownloadStatus)cursor.GetInt(cursor.GetColumnIndex(DownloadManager.ColumnStatus)))
{
string filename = cursor.GetString(cursor.GetColumnIndex(DownloadManager.ColumnTitle));
Toast.makeText(context, string.Format(context.GetString(Resource.String.chat_attachement_succesfully_downloaded), filename), Toast.LENGTH_SHORT);
}
}
}
}
}
Далее необходимо создать и зарегистрировать наш ресивер:
protected override async void OnCreate(Bundle bundle)
{
//код инициализации активности
//...
/////////
//создаем ресивер для корректной обработки успешного скачивания файла
_downloadManagerReceiver = new DownloadManagerReceiver();
RegisterReceiver(_downloadManagerReceiver, new IntentFilter(DownloadManager.ActionDownloadComplete));
}
Последним этапом — необходимо получить ссылку на экземпляр сервиса DownloadService, настроить менеджер и запустить скачивание:
private void DownloadFile(string filename){
DownloadManager downloadManager = (DownloadManager)GetSystemService(Activity.DownloadService);
//реквест содержит URI файла и дополнительные настройки DownloadManager'а
DownloadManager.Request request = new DownloadManager.Request(Android.Net.Uri.Parse("http://наш_url_для_скачивания"));
//нотификейшн будет виден всегда, пока юзер не отменит его вручную или не кликнет
request.SetNotificationVisibility(DownloadVisibility.VisibleNotifyCompleted);
//задаем публичную директорию с загрузками и вторым параметром имя файла + расширение
request.SetDestinationInExternalPublicDir(Android.OS.Environment.DirectoryDownloads, filename);
//разрешаем сканить новый файл МедиаСканнеру
request.AllowScanningByMediaScanner();
//запускаем скачивание
downloadManager.Enqueue(request);
}
Вот и все, после старта скачивания менеджер создать нотификейшн со статусом скачивания, а после позволит открыть файл в приложении по-умолчанию.
Практически любому приложению, которое позволяет обмениваться контентом необходимо скачивать данные из сети и сохранять их на диск устройства. Существует два распространенных способа это сделать:
Скачать и сохранить файл вручную;
Использовать класс DownloadManager и доверить работу ему.
При этом скачанные файлы не будут видны приложениям на устройстве до тех пор, пока разработчик самостоятельно не сделает их доступными (либо до следующей перезагрузке устройства).
В этой статье мы рассмотрим ручной способ сохранения на диск применительно к Xamarin.Android. Более интересный вариант с использованием DownloadManager будет рассмотрен в другой статье.
Для начала необходимо получить массив байт файла из сети (например по его URL). Для этого можно использовать например простейший WebClient, написать примерно такой код:
string url = “url_к_нашему_файлу”;
byte[] dataBytes = new System.Net.WebClient().DownloadData(url);
Далее необходимо сохранить полученный массив байт как файл на диске. Для этого сначала необходимо получить путь до одной из публичных директорий, они могут быть следующими:
Environment.DirectoryMusic;
Environment.DirectoryPodcasts;
Environment.DirectoryRingtones;
Environment.DirectoryAlarms;
Environment.DirectoryNotifications;
Environment.DirectoryPictures;
Environment.DirectoryMovies;
Environment.DirectoryDownloads;
Environment.DirectoryDcim.
Назначение директорий вполне ясно из их названия, будем использовать Environment.DirectoryDownloads. Напишем следующий код (пояснение в комментариях):
//получаем путь к директории с загрузками
string dirPath = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads).AbsolutePath;
//если директории нет (такое может быть, да О_о, создадим ее)
if (!Directory.Exists(dirPath))
{
Directory.CreateDirectory(dirPath);
}
//получаем путь до конечного файла в нашей директории, если он уже есть - удалим его
string filePath = Path.Combine(dirPath, file.Name);
if (File.Exists(filePath))
{
File.Delete(filePath);
}
//записываем массив байт на диск
File.WriteAllBytes(filePath, fileBytes);
Почти готово, осталось только объявить системе о том, что на диске теперь есть новый файл, доступный всем вокруг. Для этого используется подсистема Android, которая называется MediaScanner. Существует несколько способов работы с МедиаСканнером, все они обладают разным уровнем гибкости и сложности. Мы воспользуемся самым простым — отправим широковещательный интент системе, МедиаСканнер его поймает и сделает добавит файл в свою базу.
//объявим МедиаСканеру о том, что появился новый файл
Intent mediaScannerIntent = new Intent(Intent.ActionMediaScannerScanFile);
mediaScannerIntent.SetData(Android.Net.Uri.FromFile(new Java.IO.File(filePath)));
SendBroadcast(mediaScannerIntent);