简体   繁体   English

更新从PCL到Xamarin.Android的文件下载进度

[英]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. 在我们的xamarin.android应用程序中,我们从远程服务器下载文件,并使用进度状态更新客户端列表行项目。

For this we have created a pcl to do the download which is common for android and ios. 为此,我们创建了一个pcl来进行下载,这对于android和ios来说很常见。

From xamarin.android app from new worker thread we have called the httpwebrequest to download the file from remote server as below, 从新工作者线程的xamarin.android应用程序我们调用httpwebrequest从远程服务器下载文件,如下所示,

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. 从PCL下载后,我们使用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. 下面的代码片段用于从httpwebresponse读取输入流,并将数据写入内部存储中存在的输出流文件。

    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, 一旦在xamarin.android中收到回调,我们将在UI线程上更新ui内部运行,如下所示,

   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. 我们从线程调用下载API。 While reading the Xamarin "Report download progress" reference link says that await/async internally calls use the threadpool to do the background task. 在阅读Xamarin“报告下载进度”参考链接时,await / async内部调用使用线程池来执行后台任务。

If I try without thread the UI hangs once I pressed the download icon. 如果我尝试没有线程,一旦按下下载图标,UI就会挂起。 If i call inside thread the UI is responsive and it crashed immediately. 如果我调用内部线程,UI会响应并立即崩溃。

The PCL async/await to Xamarin android ui communication is not proper. PCL异步/等待Xamarin android ui通信是不正确的。

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 . 我在UI线程上通知下载侦听器的方法是将( RetainInstance图)DownloadManager- 片段RetainInstance设置为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). 因此,从您的活动中,使用其Tag获取(或创建)此片段,然后使用您的下载侦听器开始下载。 Then, from your activity, you can call NotifyDataSetChanged and similar methods. 然后,从您的活动中,您可以调用NotifyDataSetChanged和类似的方法。

I hope this is useful in the process of solving your problem. 我希望这在解决问题的过程中很有用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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