简体   繁体   中英

Update File download progress from PCL to Xamarin.Android

In our xamarin.android app we are downloading a file from remote server and the client list row item is updated with the progress status.

For this we have created a pcl to do the download which is common for android and ios.

From xamarin.android app from new worker thread we have called the httpwebrequest to download the file from remote server as below,

ThreadStart downloadThreadStart = new ThreadStart(() =>
   {
                Stream destinationFileStream = FileUtils.GetFileStream(fileName, info, context);
                // Call the static method downloadFile present in PCL library class WebRequestHandler
                WebRequestHandler.downloadFile(listener, fileName, key, destinationFileStream);
});
new Thread(downloadThreadStart).Start();

From PCL after download we are calling the write file content method using await/async.

await WriteFileContentAsync(response.GetResponseStream(), destFileStream, 
    Convert.ToInt64(size), listener, geoidFileName);

The below code snippet which is used to read the input stream from the httpwebresponse and write the data to output stream file which is present in internal storage.

    private static async Task WriteFileContentAsync(Stream sourceStream, 
        Stream destFileStream, long size, ResponseListener listener, string filename)
                    {
                        byte[] buffer = new byte[BUFFER_SIZE];
                        //Interface callback sent to the xamarin.android application
                        listener.UpdateDownloadProgress(filename, 0);
                        try
                        {
                            int currentIndex = 0;
                            int bytesRead = await sourceStream.ReadAsync(buffer,
 0, buffer.Length);

                            while (bytesRead > 0)
                            {
                                await destFileStream.WriteAsync(buffer, 0, bytesRead);
                                currentIndex += bytesRead;
                                int percentage = (int)(((double)currentIndex / size) * 100);
                                Debug.WriteLine("Progress value is : " + percentage);
                                listener.UpdateDownloadProgress(filename, percentage);
                                bytesRead = await sourceStream.ReadAsync(buffer, 
0, buffer.Length);
                            }
                            listener.UpdateDownloadProgress(filename, 100);
                            //Send the callback
                            listener.updateDownloadStatus(ResponseStatus.Success, filename);
                        }
                        catch (Exception ex)
                        {
                            Debug.WriteLine("Geoid file write exception : " + ex);
                            listener.updateDownloadStatus(ResponseStatus.Error, filename);
                        }
                        finally
                        {
                            if (sourceStream != null)
                            {
                                sourceStream.Flush();
                                sourceStream.Dispose();
                            }
                            if (destFileStream != null)
                            {
                                destFileStream.Flush();
                                destFileStream.Dispose();
                            }
                        }
                    }

Once the callback is received in xamarin.android we are updating the ui inside run on UI thread as below,

   public void UpdateDownloadProgress(string filename, int progressValue)
   {
            //Skip the progress update if the current and previous progress value is same
            if (previousProgressValue == progressValue)
            {
                return;
            }

            int position = findAndUpdateProgressInList(filename);
            this.itemList[position].ProgressValue = progressValue;        
            RunOnUiThread(() =>
            {
                this.coordinateAdapter.NotifyDataSetChanged();
            });
   }

But the app closes abruptly and crashes. While downloading the status is updated immediately after sometime the app closes abruptly and crashes. The scroll is also not smooth.

We are calling the download APIfrom thread. While reading the Xamarin "Report download progress" reference link says that await/async internally calls use the threadpool to do the background task.

If I try without thread the UI hangs once I pressed the download icon. If i call inside thread the UI is responsive and it crashed immediately.

The PCL async/await to Xamarin android ui communication is not proper.

Help me to get rid out of this issue.

I'm doing something similar in my app (conceptually speaking). The way I notify the download listeners on the UI-thread is by having a (view-less) DownloadManager- Fragment with RetainInstance set to true . This will prevent your fragment from being destroyed on configuration changes (such as orientation changes).

Here's a short excerpt of my code:

        /// <summary> A non-UI, retaining-instance fragment to internally handle downloading files. </summary>
        public class DownloadFragment : Fragment
        {
            /// <summary> The tag to identify this fragment. </summary>
            internal new const string Tag = nameof(DownloadFragment);

            public override void OnCreate(Bundle savedInstanceState)
            {
                base.OnCreate(savedInstanceState);
                RetainInstance = true;
            }

            /// <summary> Starts the given download. </summary>
            public async void RunDownload(Context context, Download download, IDownloadListener listener,
                IDownloadNotificationHandler handler = null)
            {
                // start your async download here, and hook listeners. 
                // Note that listener calls will happen on the UI thread.
            }
        }

So from your activity, get (or create) this fragment using its Tag , and then start your download with your download listener(s). Then, from your activity, you can call NotifyDataSetChanged and similar methods.

I hope this is useful in the process of solving your problem.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM