我有一个异步下载程序,该下载程序从网络上将大量资源下载到Xamarin Android表单应用程序。我要下载的代码如下:
public interface IAnimationDownloadCallbacks
{
void DidFinishDownload(StoreItemAnimation itemAnimation, string downloadedUrl, bool downloadResult);
}
public async Task<bool> DownloadAnimationFromUrl(string url, StoreItemAnimation storeItem = null, IAnimationDownloadCallbacks callbacks = null)
{
App.Log("Downloading : " + url);
if (String.IsNullOrEmpty(url) || url.Contains("/") == false)
{
if (callbacks != null)
callbacks.DidFinishDownload(storeItem, url, false);
return false;
}
IFolder folder = FileSystem.Current.LocalStorage;
var filename = GetFileName(url);
folder = await folder.CreateFolderAsync("Cache", CreationCollisionOption.OpenIfExists);
ExistenceCheckResult fileExists = await folder.CheckExistsAsync(filename);
if (fileExists == ExistenceCheckResult.FileExists)
{
App.Log("Skipping, already downloaded.");
if (callbacks != null)
callbacks.DidFinishDownload(storeItem, url, true);
return true;
}
else
{
try
{
App.Log("waiting to download... " + url);
App.Log("started... " + url);
bool didCopy = false;
if (Device.RuntimePlatform == Device.iOS)
{
using (var wc = new HttpClient(new HttpClientHandler()))
{
var imageData = await wc.GetStreamAsync(url);
App.Log("Length: " + imageData.Length.ToString());
IFile file = await folder.CreateFileAsync(filename, CreationCollisionOption.ReplaceExisting);
using (Stream stream = await file.OpenAsync(FileAccess.ReadAndWrite))
{
await imageData.CopyToAsync(stream);
//stream.TryDispose();
App.Log("Done writing : " + filename + " url: " + url);
if(callbacks!=null)
callbacks.DidFinishDownload(storeItem, url, true);
return true;
}
}
}
else
{
using (var stream = await ImageService.Instance.LoadUrl(url).AsPNGStreamAsync())
{
App.Log("Length: " + stream.Length.ToString());
IFile file = await folder.CreateFileAsync(filename, CreationCollisionOption.ReplaceExisting);
using (Stream streams = await file.OpenAsync(FileAccess.ReadAndWrite))
{
await stream.CopyToAsync(streams);
didCopy = true;
//streams.TryDispose();
}
//stream.TryDispose();
}
}
App.Log("(DidCopy: "+didCopy+")Done writing : " + filename + " url: " + url);
if (callbacks != null)
callbacks.DidFinishDownload(storeItem, url, true);
return true;
}
catch (Exception error)
{
App.Log(error.Message);
App.Log("Delete file");
ExistenceCheckResult isFileExists = await folder.CheckExistsAsync(filename);
if (isFileExists == ExistenceCheckResult.FileExists)
{
App.Log("File found");
IFile file = await folder.GetFileAsync(filename);
await file.DeleteAsync();
}
if (callbacks != null)
callbacks.DidFinishDownload(storeItem, url, false);
return false;
}
}
}
现在,我想一次下载多个资源(我尝试同时进行所有下载),所以我一次创建了八个资源的批量下载。现在,我不知道为什么我的应用程序崩溃而没有日志,异常或任何其他原因。我研究发现,这可能是由于ANR引起的,但我似乎找不到它在我的应用程序中造成瓶颈。这是批量下载的代码:
public class BatchQueueAnimationDownload: ImageCache.IAnimationDownloadCallbacks
{
/*
*
* Declare Batch download callbacks
* that will be used to reference the calling class
* of this instance
*/
public interface BatchQueueDownloadCallbacks
{
void OnNewItemDownloaded(StoreItemAnimation itemAnimation, string downloadedUrl, bool downloadResult);
void OnAllQueueCompleted(int queueSize);
}
/*
* Size of maximum simultaneous downloads
*/
private readonly int BATCH_SIZE = 8;
/*
* Counter for current simultaneous downloads
*/
private int currentDownloadsCount = 0;
/*
* Counter for currently completed downloads
*/
private int completedDownloadsCount = 0;
/*
* Indexer to keep track of last queued download
*/
private int lastQueuedIndex = 0;
/*
* Batch download callback declaration, this will be assigned
* when an instance of this class is invoked or if a queue
* of downloads are added see #{AddDownload(...) method}
*/
BatchQueueDownloadCallbacks callbacks;
/*
* Flag to switch when all queued downloads are completed.
* true = all downloads are completed
* false = when a new queue is initiated {see #Reset() method}
*/
bool isCompleted = false;
/*
* List collection of all downloads in queue. All downloads/downloadables
* are in this list.
*/
ObservableCollection<DownloadQueue> downloadQueue = new ObservableCollection<DownloadQueue>();
/*
* static reference of this class. Only one instance of this class
* would be running.
*/
static BatchQueueAnimationDownload instance;
/*
* Instance property, initialize an instance if not yet
* otherwise, return the current instance to invoking class
*
*/
public static BatchQueueAnimationDownload GetInstance {
get {
if (instance == null)
instance = new BatchQueueAnimationDownload();
return instance;
}
}
/*
* Method that will accept addition to the
* download queue. Once added, that queue becomes available for
* download.
*
* item = The reference to the StoreItemAnimation object so
* that we'll be able to determine which StoreAnimationItem
* is being downloaded.
* downloadUrl = the url that will be called for download. It can be
* either url_android or url_android2 of the StoreItemAnimation instance.
* callbacks = Implementing class which invokes this instance, by default
* it is this class itself.
*/
public void AddDownload(StoreItemAnimation item, string downloadUrl, BatchQueueDownloadCallbacks callbacks)
{
DownloadQueue q = new DownloadQueue(item, downloadUrl, this);
downloadQueue.Add(q);
this.callbacks = callbacks;
//StartDownload(q);
}
/*
* Invoke this class once all queues have been added.
* This starts the actual batch downloads itself.
*/
public void Start()
{
Reset();
if(downloadQueue.Count>0){
/*
* Download 1st batch
*/
while(currentDownloadsCount < BATCH_SIZE && downloadQueue.Count > lastQueuedIndex){
StartDownload(downloadQueue[lastQueuedIndex]);
}
}
}
/*
* Individual downloads for each
* queued download items.
*
* queue = the queue item that will be downloaded
*/
void StartDownload(DownloadQueue queue)
{
App.Log("Current Simultaneous Download: " + currentDownloadsCount + " of " + BATCH_SIZE + " Complete:" + completedDownloadsCount);
if ((callbacks != null) && (queue == null))
{
callbacks.OnAllQueueCompleted(downloadQueue.Count);
isCompleted = true;
return;
}
/*
* Check if current simultaneous download is already maxed out (BATCH_SIZE)
* and if the download queue is already started downloading
*/
if (currentDownloadsCount < BATCH_SIZE )
{
/*
* Check if downloads already completed entire queue
*/
if (completedDownloadsCount >= downloadQueue.Count )
{
/*
* If so, invoke the invoking class' <i>completed<i> callback
*/
if(callbacks != null)
callbacks.OnAllQueueCompleted(downloadQueue.Count);
return;
}
/*
* Check if the queue counter is greater than the
* actual size of the queue. Not doing so will fire
* IndexOutOfBoundsException or Exception in general.
*/
if (lastQueuedIndex < downloadQueue.Count)
{
ImageCache.GetInstance.DownloadAnimationFromUrl(queue.url, queue.item, queue.callbacks);
/*
* Increment the current simultanous downloads.
* This is decremented on completion of each download.
*/
currentDownloadsCount++;
/*
* Increment the counter for the queued downloads.
*/
lastQueuedIndex++;
}
/*
* When the queue counter is equal to the
* queue size, it means that all queues have
* been downloaded.
*/
else
{
if (callbacks != null)
callbacks.OnAllQueueCompleted(downloadQueue.Count);
}
}
}
public void DidFinishDownload(StoreItemAnimation itemAnimation, string downloadedUrl, bool downloadResult)
{
if (this.callbacks != null)
this.callbacks.OnNewItemDownloaded(itemAnimation, downloadedUrl, downloadResult);
//GC.Collect(0, GCCollectionMode.Optimized, false);
/*
* Empty Batch slot, available for download use
*/
currentDownloadsCount--;
/*
* Increment completed download
*/
completedDownloadsCount++;
/*
* Start download for next in queue if any
*/
if (lastQueuedIndex < downloadQueue.Count)
{
StartDownload(downloadQueue[lastQueuedIndex]);
}
else
{
if (!isCompleted)
{
isCompleted = true;
callbacks.OnAllQueueCompleted(downloadQueue.Count);
}
}
}
public void Reset()
{
App.Log("RESET! "+currentDownloadsCount+"/"+completedDownloadsCount+"/"+lastQueuedIndex+"/"+instance);
currentDownloadsCount = 0;
completedDownloadsCount = 0;
lastQueuedIndex = 0;
isCompleted = false;
}
}
class DownloadQueue
{
public StoreItemAnimation item;
public string url;
public ImageCache.IAnimationDownloadCallbacks callbacks;
public DownloadQueue(StoreItemAnimation item, string url, ImageCache.IAnimationDownloadCallbacks callbacks)
{
this.item = item;
this.url = url;
this.callbacks = callbacks;
}
}
任何帮助将不胜感激。预先谢谢你。
更新 这仅在Android API 23上发生(据我测试)。