Share via

LISTENABLE WORKER

Giorgio Sfiligoi 656 Reputation points
2026-04-29T18:36:04.19+00:00

I am using OnSleep to launch an asynchronous background worker that saves certain files as follows:

using Android.Content;
using AndroidX.Work;
using Google.Common.Util.Concurrent;

namespace Draft4.Platforms.Android.Workers;

public class OnSleepAsyncWorker : ListenableWorker
{
    public const string TAG = "OnSleepAsyncWorker";

    public OnSleepAsyncWorker(Context context, WorkerParameters workerParams)
        : base(context, workerParams) { }

    public override IListenableFuture StartWork()
    {
        var taskCompletionSource = new TaskCompletionSource<Result>();
        Task.Run(async () =>
        {
            try
            {
                // save files: upload to cloud storage
                await Draft4.Services.Document.OnSleep();
                Debug.WriteLine("OnSleepAsyncWorker succeeded");
                taskCompletionSource.SetResult(Result.InvokeSuccess()!);
            }
            catch (Exception)
            {
                Debug.WriteLine("OnSleepAsyncWorker failed");
                taskCompletionSource.SetResult(Result.InvokeFailure()!);
            }
        });
        return (IListenableFuture)taskCompletionSource.Task;
    }
}

(adding ... Task.Result; makes no difference)

Running in the Emulator in Debug mode, I get the following message in output:

04-29 20:08:44.119 E/WM-WorkerWrapper(29856): Work [ id=cf526e62-7af7-4840-85e4-5fb9259ae836, tags={ crc64620a83f8248190ee.OnSleepAsyncWorker,OnSleepAsyncWorker } ] failed because it threw an exception/error
04-29 20:08:44.119 E/WM-WorkerWrapper(29856): android.runtime.JavaProxyThrowable: [System.InvalidCastException]: Specified cast is not valid.
...

04-29 20:31:39.104 I/WM-WorkerWrapper(30601): Worker result FAILURE for Work [ id=626111ed-e983-4e42-89bf-76d38c8ed937, tags={ crc64620a83f8248190ee.OnSleepAsyncWorker,OnSleepAsyncWorker } ]

 

On the other hand at the end it reports:

OnSleepAsyncWorker succeeded

(but this is just the output from Debug.WriteLine...)

Also note that the cast (IListenableFuture) is required by the compiler.

I am puzzled: is this really an error? How can I fix it?

Developer technologies | .NET | .NET MAUI

Answer accepted by question author

  1. Nancy Vo (WICLOUD CORPORATION) 3,550 Reputation points Microsoft External Staff Moderator
    2026-04-30T03:58:22.2333333+00:00

    Hello @Giorgio Sfiligoi ,

    Thanks for your question.

    A .NET Task<Result> and an Android IListenableFuture are completely different types. You cannot cast one to the other, even though the compiler allows it, Android throws InvalidCastException at runtime.

    It is highly likely that the error is coming from this line:

    return (IListenableFuture)taskCompletionSource.Task;
    

    I recommend using Simple Worker instead. Just switch from ListenableWorker to the regular Worker class and block the async call:

    using Android.Content;
    using AndroidX.Work;
    using System.Diagnostics;
    
    namespace Draft4.Platforms.Android.Workers
    {
        public class OnSleepWorker : Worker
        {
            public const string TAG = "OnSleepWorker";
    
            public OnSleepWorker(Context context, WorkerParameters workerParams)
                : base(context, workerParams) { }
    
            public override Result DoWork()
            {
                try
                {
                    Draft4.Services.Document.OnSleep()
                        .GetAwaiter()
                        .GetResult();
    
                    Debug.WriteLine("OnSleepWorker succeeded");
                    return Result.InvokeSuccess()!;
                }
                catch (Exception ex)
                {
                    Debug.WriteLine($"OnSleepWorker failed: {ex.Message}");
                    return Result.InvokeFailure()!;
                }
            }
        }
    }
    

    I hope this addresses your question. If this response was helpful, please consider following the guidance to provide feedback.

    1 person found this answer helpful.

1 additional answer

Sort by: Most helpful
  1. Q&A Assist
    2026-04-29T18:36:17.5433333+00:00
    AI generated content. This question contains an answer created with AI from Q&A Assist. Learn more

    ListenableWorker.StartWork must return a real IListenableFuture implementation from the Guava / WorkManager side, not a .NET Task cast to IListenableFuture. The invalid cast is what causes the System.InvalidCastException and the WorkerWrapper error, even though the inner async code runs and logs "OnSleepAsyncWorker succeeded".

    The correct pattern is:

    1. Use the CallbackToFutureAdapter (from AndroidX.Work / Guava) to create an IListenableFuture.
    2. Complete that future from the .NET async code.

    Conceptually, the flow should be:

    public override IListenableFuture StartWork()
    {
        // Create the Java-side future
        var future = CallbackToFutureAdapter.GetFuture(new CallbackToFutureAdapter.IResolver()
        {
            public Java.Lang.Object AttachCompleter(CallbackToFutureAdapter.Completer completer)
            {
                // Run .NET async work
                Task.Run(async () =>
                {
                    try
                    {
                        await Draft4.Services.Document.OnSleep();
                        Debug.WriteLine("OnSleepAsyncWorker succeeded");
                        completer.Set(Result.InvokeSuccess());
                    }
                    catch (Exception)
                    {
                        Debug.WriteLine("OnSleepAsyncWorker failed");
                        completer.Set(Result.InvokeFailure());
                    }
                });
    
                return "OnSleepAsyncWorker"; // debug tag
            }
        });
    
        return future;
    }
    

    Key points:

    • Do not cast Task/TaskCompletionSource.Task to IListenableFuture; they are unrelated types.
    • Let StartWork return the IListenableFuture created by the Java/AndroidX APIs.
    • Complete that future (completer.Set(...)) when the .NET async work finishes.

    Once StartWork returns a valid IListenableFuture, the InvalidCastException and WM-WorkerWrapper error will stop, and WorkManager will correctly see SUCCESS/FAILURE based on the result.


    References:

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.