0

If I'm fetching an object from cache before showing my first view, then the app will just show a blank white page instead (on Android, didn't test other platforms). If I lock the screen and unlock it, then my view shows. It's quite simple to reproduce:

  • Create new Xamarin.Forms app (I chose tabbed layout for this sample).
  • Add akavache nuget package to all projects.
  • In App.xaml.cs, initialize akavache and get an object before setting the MainPage property.
  • Start app - will be completely white screen.
  • Lock screen, and unlock - interface will show. This part only seems to work on my physical phone, not the emulator.

Here is a sample App.xaml.cs which will reproduce the problem.

using System.Reactive.Linq;
using Akavache;
using Xamarin.Forms;
using AkavacheTest.Services;
using AkavacheTest.Views;

namespace AkavacheTest
{
    public partial class App : Application
    {

        public App()
        {
            InitializeComponent();

            DependencyService.Register<MockDataStore>();
        }

        protected override async void OnStart()
        {
            Akavache.Registrations.Start("Test");
            var test = await BlobCache.LocalMachine.GetOrFetchObject("Settings", async () => "New");
            MainPage = new MainPage();
        }
    }
}

So what's going on here, and more importantly - is there a fix or workaround for this?

Inrego
  • 1,524
  • 1
  • 15
  • 25
  • have you done anything to debug this? Have you stepped through the code to see if your MainPage is getting called? Are there any errors, exceptions, or log messages? – Jason Apr 12 '20 at 22:21
  • I have spent some time debugging. I can't see any errors. MainPage is being constructed, and `OnAppearing` is also being called on MainPage. – Inrego Apr 12 '20 at 22:25

1 Answers1

1

As far as workarounds go, I managed to find one. If I set the MainPage before getting from cache, then I can set it again after grabbing cache without problem.

These both work:

    protected override async void OnStart()
    {
        MainPage = new Page();
        Akavache.Registrations.Start("Test");
        var test = await BlobCache.LocalMachine.GetOrFetchObject("Settings", async () => "New");
        MainPage = new MainPage();
    }

    protected override async void OnStart()
    {
        Akavache.Registrations.Start("Test");
        MainPage = new Page();
        var test = await BlobCache.LocalMachine.GetOrFetchObject("Settings", async () => "New");
        MainPage = new MainPage();
    }

I'm still curious why this is happening, though. See comment by @BenReierson.

With this information in mind, a better workaround would be this:

protected override void OnStart()
{
    Akavache.Registrations.Start("Test");
    var test = BlobCache.LocalMachine.GetOrFetchObject("Settings", async () => "New").GetAwaiter().GetResult();
    MainPage = new MainPage();
}

So we actually wait for the result from Akavache before setting MainPage. While the other workaround worked, it's a bit wasteful to set a page (which causes it to be rendered), just to set another page almost instantly after.

Inrego
  • 1,524
  • 1
  • 15
  • 25
  • 1
    Because OnStart is not being awaited, as it's not normally an async method and doesn't return a task. The caller is seeing it return before MainPage is set, which is likely causing the issue. – Ben Reierson Apr 13 '20 at 11:35
  • @BenReierson that makes sense. To test your theory, I tested your theory and it seems to be right. I'll update this answer with a better workaround. – Inrego Apr 13 '20 at 11:54
  • You just saved my life... spent way too much time on this one without luck but this worked for me, at last. – Fred Jun 30 '20 at 15:22
  • Just wanted to add, that I ended up going with another (similar) workaround. In my App class, I navigate to a "loading" page. On this page, I write something like "Loading", and do all my initialization here. Once done initializing, I load cache and use that to decide which page to navigate to. – Inrego Jul 01 '20 at 10:45