c# - Reactive-ui binding with async viewmodelupdate crashes -


i have question reactive ui, bindings, , how handles ui updates. assumed using reactiveui take care of ui updates on ui thread. found out isn't case.

in short question is: how can use reactiveui two-way-model-bind viewmodel , view, , assure updating viewmodel doesn't crash when run on different thread ui-thread? without having manually subscribe changes , update explicitely on uithread, defeats purpose of reactiveui, making harder encapsulate logic in pcl.

below i've provided simple (android) project using xamaring , reactiveui, following:

  • button text 'hello world'
  • clicking on appends 'a' button's text.
  • i let activity implement iviewfor, , use viewmodel deriving reactiveobject, containing text want change.
  • i bind activity's button.text viewmodel.text, let reactiveui deal changes , ui updates.
  • finally, add function button's onclick append 'a' viewmodel.

the issue have following:

button.click += delegate     {         this.viewmodel.text += "a";                         // not crash         task.run(() => { this.viewmodel.text += "a"; });    // crash     }; 

directly appending 'a' not issue. however, adding 'a' on different thread results in well-known java exception: exception: original thread created view hierarchy can touch views.

i understand exception , it's coming from. in fact, if append 'a' on different thread, had working not binding text. rather subscribing changes, , using runonuithread-method make changes ui. scenario kind of defeats purpose of using reactiveui. clean coding way of simple statement 'this.bind(viewmodel, x => x.text, x => x.button.text);', if has run on uithread, can't see how make work.

and naturally bare mininum show problem. actual problem why bring because want use 'getandfetchlatest'-method akavache. gets data asynchroniously , caches it, , executes function (being updating viewmodel). if data in cache, execute viewmodel-update cached result , computationlogic in different thread, , call same function again once it's done (resulting in crash, because that's on different thread, updates viewmodel, results in crash).

note though explicitely using runonuithread works, don't want (can't even) call within viewmodel. because have more complex piece of code in button tells viewmodel go fetch data , update itself. if required on uithread (i.e. after got data back, update viewmodel), can't bind ios same viewmodel anymore.

and lastly, here's entire code make crash. i've seen task.run-part work, if add more tasks , keep updating viewmodel in them, it's bound crash on ui-thread.

public class mainactivity : activity, iviewfor<mainactivity.randomviewmodel> {     public randomviewmodel viewmodel { get; set; }     private button button;      protected override void oncreate(bundle bundle)     {         base.oncreate(bundle);          setcontentview(resource.layout.main);          this.button = findviewbyid<button>(resource.id.mybutton);          this.viewmodel = new randomviewmodel { text = "hello world" };         this.bind(viewmodel, x => x.text, x => x.button.text);          button.click += delegate             {                 this.viewmodel.text += "a";                         // not crash                 task.run(() => { this.viewmodel.text += "a"; });    // crash             };     }      public class randomviewmodel : reactiveobject     {         private string text;          public string text         {                         {                 return text;             }             set             {                 this.raiseandsetifchanged(ref text, value);             }         }     }      object iviewfor.viewmodel     {                 {             return viewmodel;         }         set         {             viewmodel = value randomviewmodel;         }     } } 

this has been discussed here , there, , short answer "as designed, performance reasons".

i'm not convinced later (performance bad driver when designing api), i'll try explain why think design correct anyway:

when binding object view, expect view come , peak (read) @ object properties, , it's doing ui thread.

once acknowledge that, sane (as in thread-safe , guaranteed work) way modify object (which being peaked ui thread) ui thread.

modifications other threads may work, within specific conditions, devs don't care (up until ui artifacts, in case ... perform refresh...).

for instance if you're using inpc, and property values immutable (e.g. string), and view won't feel bad observing value change before receives notification of (simple controls ok it, grids filtering/sorting capabilities not ok, unless deep-copy source).

you should design viewmodel fact lives in ui context in mind.

with rx, means having .onserveron(/* ui sheduler */) right before viewmodel modification code.


Comments

Popular posts from this blog

resizing Telegram inline keyboard -

command line - How can a Python program background itself? -

php - "cURL error 28: Resolving timed out" on Wordpress on Azure App Service on Linux -