1

I've been getting a bit of strange behavior from the mobile app I'm writing using Xamarin Forms, the premise is simple:

  • Navigate to a new page (this happens asynchronously)
  • Retrieve some data (asynchronously as well)
  • Bind data to view

Because I'm using mvvm I can't hook into any of the page events (appearing/disappearing/etc.) so I can only start retrieving data from the constructor (bad idea, I know).

That's where (I think) the issue lies:

the navigator (running on the UI Thread) asynchronously resolves the view and viewmodel the viewmodel can't load the data asynchronously from the constructor so it blocks the UI Thread untill the loading is done.

And as long as your network connection is fast enough you might not even notice it, but when it does take too long the system appears to think it's in a deadlock and terminates.

So I've tried to solve this by using

Task.Factory.StartNew(() => { GetData(); });

in the constructor and this works about 80% of the time but, as I've discovered, starting a new task does not guarantee a new Thread.

So once in a while the data retrieval task will run on the UI Thread, blocking it and take just slightly too long to load resulting in a crash.

Is there a clean way of async data loading with mvvm?

killernerd
  • 377
  • 2
  • 6
  • 21
  • Generally you want to use `Task.Run()`. You might want to think about using `await` and `async` _all the way_. –  Feb 19 '16 at 14:09
  • You can still use a special data loading method in the VM instead of the constructor! In the code behind of your view, do a `protected async override void OnAppearing()` and call do a async call to the loading method of your vm inside it. – Depechie Feb 19 '16 at 14:13
  • Generally `async void` is a bad idea, unless it's an event handler as uncaught exception won't be caught my try/catch, but on the SynchronizationContext that was active when the async void was started (usually the UI thread). It usually leads to an unhandled exception – Tseng Feb 19 '16 at 14:21
  • @Tseng I realize async void is a bad idea but in this case there really is nothing to return. Get the data, bind it to the view and if an error occurs log it and alert the user. So how should I change it then? – killernerd Feb 19 '16 at 14:24
  • @Depechie I'm trying to keep any and all logic out of the "code behinds" otherwise it'll become spaghetti real quickly. – killernerd Feb 19 '16 at 14:25
  • Not sure why, tell me, where do you assign the View.BindingContext? If it's also in the code behind, why not trigger the data load at the correct time/place? – Depechie Feb 19 '16 at 14:26
  • @killernerd: It's not about returning something. `async Task` as no return value later, but is awaitable and the exception is caught by the task. async void isn't. Check out the example in https://msdn.microsoft.com/en-us/magazine/jj991977.aspx. It specifically mentions that ´async void` was introduced for a very specific case: async event handlers. One shouldn't misuse it for different things – Tseng Feb 19 '16 at 14:28
  • @Depechie All my code-behinds are empty, that's the idea of MVVM. Loose coupling of views and viewmodels. That also means I don't have to assign bindingcontexts myself, it all happens automatically. – killernerd Feb 19 '16 at 14:30
  • Nope... means you are using a framwork :) And that will do it for you. Each MVVM framework has to hook up the view's context at a certain time. But that could also mean that the framework you are using maybe has another hookup that you could use. – Depechie Feb 19 '16 at 14:33
  • @Depechie same thing, no? I don't have to do it because the framework handles it for me. All I'm doing is registering the views/vms and linking them together, the framework then handles the bindingcontext at runtime (if i really wanted to i could even change views and viewmodels at runtime as per design). In any case, adding an explicit call from the code behind to the viewmodel beats the purpose of the loose coupling you try to achieve with mvvm in the first place, no? – killernerd Feb 19 '16 at 14:42
  • @Depechie: "code behind" is the partial *.cs file attached to the XAML file and not "any code that isn't part of a ViewModel but interacts with views". Having a navigation service is no code behind, but it's part of the "view layer". Prism uses a ViewModel locator that you access via XAML like `prism:ViewModelLocator.AutoWireViewModel="True"`. But its not code behind. Also that's only true for views, user controls still have and should have code behind – Tseng Feb 19 '16 at 14:45
  • _"All my code-behinds are empty, that's the idea of MVVM"_ - that's a fallacy. There are times when code behind _is_ required such as defining a _dependency property_; having the view instigate an `await` (which is where async tasks generally should begin anyway); or simply displaying a parented dialog –  Feb 19 '16 at 14:45
  • Ha so now we know it's PRISM, that normally has an extra overridable method in the viewmodel to LoadData – Depechie Feb 19 '16 at 14:46
  • @MickyD: Views rarely if ever have DP's. You're talking about reusable UserControls, code behind in UserControls is fine :) – Tseng Feb 19 '16 at 14:47
  • And I know what code behind is :) only depending on the mvvm framework you need to put the context yourself OR it happens through reflection by the framework – Depechie Feb 19 '16 at 14:47
  • @Tseng I had skimmed over that article before but didn't really go into it because in my mind any and all exceptions were being handled inside the async part of the code. Guess I was wrong. I must say that the app does not throw an exception, when it happens it only prints a "threadid=3: reacting to signal 3" followed by "writing stacktrace to traces.txt". Opening that file does not make things any clearer as it simply prints all the threads that were running at the time of the crash, scrolling through it there's not an exception to be found – killernerd Feb 19 '16 at 14:47
  • @Tseng Who says `UserControl`s can't participate in MVVM? –  Feb 19 '16 at 14:48
  • @Depechie Autofac actually. – killernerd Feb 19 '16 at 14:50
  • @MickyD: I didn't said they can't participate, but a user control never has a same named viewmodel. You'll just run into trouble if you try to split a user control into `MyUserControl` and `MyUserControlViewModel` and try to bind your `CustomerViewModel` to `MyUserControl`. `UserControls` are self suficient, they can and **must** have code behind.`UserControl` = app agnostic and can be reused in many apps, `View` = app specific and cant be reused – Tseng Feb 19 '16 at 14:51
  • @MickyD in the case of a dependency property? Why? (or rather: how?) aly my dp's are defined in the viewmodel and bound in xaml. – killernerd Feb 19 '16 at 14:52
  • Nope, DPs can only work with types that derive from `DependecyObject` (all user controls and ui elements) and part of WPF: https://msdn.microsoft.com/library/en-us/ms752914(v=vs.100).aspx – Tseng Feb 19 '16 at 14:54
  • @Tseng What nonsense. My Smart Application consisting of 30+ `UserControl`s and a handful of `Window`s has absolutely no problem with MVVM. MVVM does not care if your **V** is a `Window` or a `UserControl`. I think you missed the basics –  Feb 19 '16 at 14:56
  • @MickyD: You/we got a terminology problem here. It's not about deriving from `Window` or `UserControl` . A view derives from `UserControl` too, but has no own logic, just a layout. A `UserControl` (i.e. `DataGrid`, `DatePicker` etc.) is reusable with its own logic. There is no `DatePickerViewModel` there you have to use everytime when you want to use `DatePicker`. If you are developing your own `DatePicker` you create a usercontrol. If you make a `UserControl` to display your `Customer` details, it's a view. Subtle but important difference. usercontrol codebehind = okay, in view **not so** – Tseng Feb 19 '16 at 15:01
  • [You shouldn't make your VM expose DependencyProperty for some very important reasons explained here](http://stackoverflow.com/a/783154/585968). Plus, I doubt if Blend will see them easily. –  Feb 19 '16 at 15:02
  • just a sidenote here but you guys do realize i'm working in xamarin forms here, right? On second thought having it in the tags might not clarify that enough, updating OP – killernerd Feb 19 '16 at 15:06
  • @Tseng Views don't discriminate. Other than that you appear to be contradicting yourself. I shall leave you there –  Feb 19 '16 at 15:08
  • What you maybe could do ( not tried it myself yet ), is add an EventToCommand behavior. – Depechie Feb 19 '16 at 15:08
  • Disccused here too... https://forums.xamarin.com/discussion/28213/mvvm-and-eventhandlers – Depechie Feb 19 '16 at 15:09
  • @Depechie Interesting, I hadn't considered using EventToCommand Behavior for page events for some reason (I have been using them for listview events for instance). At least that way the navigator should have finished doing its thing. – killernerd Feb 19 '16 at 15:21
  • @Tseng Perhaps, perhaps not. What I find bewildering is that you are declaring something impossible that PRISM has in fact been doing for years. Before appointing yourself an expert perhaps you should educate yourself on what is possible. Make sure you index that blog properly in order to gain _a reader_. Looking forward to your next piece of perplexing misinformation. –  Feb 19 '16 at 15:23
  • Well at least everything is in the xaml then :) – Depechie Feb 19 '16 at 15:27
  • @MickyD: I've done a couple of WPF/WinPhone applications with MVVM and Prism, and never put a single line in code behind, unless it was a general and reusable user control such as `DatePicker`. All you need can be done with xaml, interactivity behaviors and/or attached properties or as application service (i.e. navigation service). Believe me, it's doable if you know how to use the tools that are offered to you – Tseng Feb 19 '16 at 15:34
  • @Depechie Yes just don't call it a _"view"_ or a _"window"_ or someone will get _their knickers in a knot_ ;) –  Feb 19 '16 at 15:34
  • @Tseng Golly! I look forward in breathless anticipation to being able to initiate an `await SomethingAsync()` purely from XAML. –  Feb 19 '16 at 15:39
  • @Depechie Unfortunately that didn't work. Loading the data from the "pageloaded" event still has a chance of blocking the app and crashing the thread. – killernerd Feb 22 '16 at 09:20
  • That is unfortunate :( – Depechie Feb 22 '16 at 09:21

1 Answers1

0

ok, so I found the problem. It was indeed a problem with network code blocking the application but since I wrote 99% of this project myself I assumed that all of it used my central network manager.

I was wrong. Somewhere deep in the bowels of the code, in one of the base classes, there is a bit of logic that tracks and loads localizations. It works a bit like this:

  • resolve page
  • resolve viewmodel
  • check viewmodel translations
  • do i have the necessary translations in memory?
  • no? Do i have them in my local db?
  • again no? Attempt to load them from a localization server
  • save translations in db and memory and add them to the viewmodel
  • bind viewmodel to view.

Since we're using MVVM it was a challenge to get the translations to load in time and show them before the rest was done loading (we couldn't find a way to alert the UI that there had been a change in the translations db).

So the original author had chosen to load the translations synchronously which, as you might've guessed at this point, blocked the UI thread untill it either finished or took too long and crashed.

This basically means that I have spent the better part of two days rewriting the entire view-resolving logic to work asynchronously and do a bit of pre-fetching of the translations (where necessary).

killernerd
  • 377
  • 2
  • 6
  • 21