How do i know when my Async Task is finished?

SamDuncenSamDuncen ANMember ✭✭
edited July 2015 in Xamarin.Android

Hi,
I have a Async Task function that downloads a image from the web. I would like to know when this task is finished so i can put a loading image temporary and update it when the image is done loading.

My image downloading functions looks like this :
private async Task GetImageBitmapFromUrl(Uri url) // or use a string here
{
var client = new HttpClient(new NativeMessageHandler());

                        using (var data = await client.GetStreamAsync(url))
                        {
                            return await BitmapFactory.DecodeStreamAsync(data);
                        }

                        }

My question is, how can i execute a code ones the function is finished completely ?

Best Answer

Answers

  • stvansolanostvansolano UMInsider, University ✭✭✭

    Hello @SamDuncen

    You can chain async operations and continue with other ones by using the ContinueWith method of the Task class. For example:

        private Task GetImageBitmapFromUrl(Uri url) // or use a string here
        {
            return new HttpClient(new NativeMessageHandler()).GetStreamAsync(url)
                .ContinueWith(task => {
                    return BitmapFactory.DecodeStreamAsync(data);
                    })
                .ContinueWith(task => {
                    var result = task.Result;
                    // handle the previous task result here or return a new one
                    return result;
                });
        }
    

    Let me know if helps :)

  • CheesebaronCheesebaron DKInsider, University mod

    Please don't do as @stvansolano suggests. There is no reason to not use the await keyword.

    The async Task is completed as soon as you reach the next thing to be executed. So right after you return it is done.

    One thing I do when using Tasks is when I await them, and the code to run right after is not needed to be switched back to the originating context, I call the method with ConfigureAwait(false) chained. This ensures that you are not bogging down performance by switching context all the time. However, also if you for some mad reason call .Result on your task or run in synchronously, you won't end up in a deadlock.

    Using ContinueWith is really unnecessary and just stupidly nesting and complicating your code.

  • SamDuncenSamDuncen ANMember ✭✭

    @Cheesebaron
    Can you make a quick small example of the method "ConfigureAwait(false)" using my code as this is the first time i have heard of it

  • LoriLalondeLoriLalonde CAInsider, University, Developer Group Leader ✭✭✭

    @SamDuncen, I don't think you need to use ConfigureAwait in this instance. If using async/await, you can achieve what you're hoping for quite simply, if all you're looking to do is replace a placeholder image with a loaded image once it's downloaded. I've put together a quick and dirty example with comments inline so you can see the order of execution and what happens when using async / await.

            protected override void OnStart()
            {
                base.OnStart();
    
                //do not await this call so that the view loads normally and is not held up by loading external images
                //the ImageView in the layout should define a default image placeholder
                GetImageBitmapFromUrl(imageUrl);
    
                //code execution will return here once the GetStreamAsync method is called within GetImageBitmapFromUrl
                AnotherMethodCall();
            }
    
            private async Task GetImageBitmapFromUrl(Uri url)
            {
                var client = new HttpClient(new NativeMessageHandler());
                Bitmap bmp = null;
    
                using (var data = await client.GetStreamAsync(url)) //once GetStreamAsync is called, code execution will return back to the line following the call to GetImageBitmapFromUrl in the OnStart method
                {
                    //Code listed after the await in this method will be suspended until the call completes.
                    //Once GetStreamAsync completes, this next line will be executed.
                    bmp = await BitmapFactory.DecodeStreamAsync(data);
                }
    
                //Code execution will resume here when DecodeStreamAsync completes.
                if (bmp != null)
                {
                    //if a bitmap was returned successfully, replace the placeholder image with the bitmap
                    myImageView.SetImageBitmap(bmp);
                }
            }
    
  • CheesebaronCheesebaron DKInsider, University mod

    @LoriLalonde the reason to use ConfigureAwait(false) is to not switch back to the context you came from.

    You example does that for both the async calls. Now this does not matter much if you execute this task rarely, although it can become apparent in application performance if there is more pressure on the system and several Tasks switch context a lot.

    Also please be careful with calling client.GetStreamAsync directly. If you for instance run that piece of code on Windows Phone it will throw a different exception than when doing it with the NativeHandler from ModernHttpClient on the Xamarin platforms. Best practice with HttpClient in general is to throw handle errors yourself when the Status Code is wrong, rather than wait for an exception thrown from HttpClient.

  • SaschaPiaszekSaschaPiaszek USMember
    edited January 2016

    @Cheesebaron said:
    private async Task GetImageBitmapFromUrl(Uri url) // or use a string here
    {
    var client = new HttpClient(new NativeMessageHandler());
    var res = await client.GetAsync(url).ConfigureAwait(false);
    if (res.IsSuccessStatusCode)
    {
    using(var data = await res.Content.ReadAsStreamAsync().ConfigureAwait(false))
    return await BitmapFactory.DecodeStreamAsync(data).ConfigureAwait(false);
    }
    return null;
    }

    Something like this. Would probably do.

    Hi can this save Images Local an return for my ListView?

        public override View GetView(int position, View convertView, ViewGroup parent)
        {
            var item = items[position];
            var view = (convertView ??
              context.LayoutInflater.Inflate(
                Resource.Layout.CustomListe,
                parent,
                false)) as LinearLayout;
            var imageItem = view.FindViewById(Resource.Id.imageItem) as ImageView;
            var textTop = view.FindViewById(Resource.Id.textTop) as TextView;
            var textBottom = view.FindViewById(Resource.Id.textBottom) as TextView;
            try
            {
                var thump = localImg("http://image.png").Result;
                imageItem.SetImageBitmap(thump);
                //imageItem.SetImageResource(Resource.Drawable.pc_dev);
            }
            catch
            {
                imageItem.SetImageResource(Resource.Drawable.pc_dev);
            }
            textTop.SetText("News ID: "+item.Item1, TextView.BufferType.Normal);
            textBottom.SetText("Von: "+item.Item3, TextView.BufferType.Normal);
            return view;
        }
    

    or can cache the images?

  • Hmm
    the LogCat say:
    Thread started: #27
    Thread started: #28
    Thread started: #29
    Thread started: #30
    01-14 12:18:03.441 I/Choreographer(10921): Skipped 66 frames! The application may be doing too much work on its main thread.

    on debunging my app on a Galaxy s6

Sign In or Register to comment.