Thursday, September 22, 2011

Mobile Cross Platform MV Solution

After the initial hours lost on installing everything, I have been playing some more with MonoDroid lately, trying to port a basic Windows Phone 7 application to an Android device. Part of this was quite easy (thanks to MonoDroid that is) and part of it took some refactoring of the existing code. All in all, the experience was very insightful.

The existing WP7 application uses the MVVM pattern, which makes it very easy to get a separation of concerns between your model, view and viewmodel (I sometimes like to rant about this separation of concerns not being as clear as it should be in some applications). It also works very well in combination with the databinding that is provided in Silverlight. If you want to know more about the MVVM pattern, check out the article by Josh Smith in MSDN Magazine.

While MVVM works very well with Silverlight, WPF and Windows Phone apps, I did have some issues using this pattern in combination with MonoDroid. Let me walk you though this.

I started off thinking which parts of the existing code I would be able to reuse for the Android application. Reusing the views was out of the question, since Android views are written differently than Windows Phone views. But my models should be easily reusable, as should be my viewmodels.

The existing application already had the model and viewmodel classes in a separate Windows Phone class library. The views were contained in a Windows Phone 7 application (MemoChallenge.Wp7). So, reusing the models and viewmodels, would just be a matter of using this same class library for my Android application. Now, you can't just reference a Windows Phone 7 class library in an Android project. For this to work you need a second class library, specific for the Android/Monodroid project and some clever project linking.

For the Android application I actually created two more projects, one for the Android  views (MemoChallenge.droid), and one class library (MemoChallenge.droidlogic), which would be a linked project to the Windows Phone 7 class library (MemoChallenge.logic). You can use the Project Linker tool for this. This will link the two class library projects and will make sure that any change you do in one project, gets reflected in the other project.


After doing this, I immediately got my first compile errors, and they were abundant. Why did I get all these errors?

The thing is, viewmodel classes use commands all over the place. Or actually, they use the ICommand interface. This interface turned out to be quite a big problem. It is contained in a Windows specific dll, so the Android SDK does not know what to do with it.

To solve this problem, I could have rewritten the ICommand interface for Android. This would also mean I would have to add conditional compilation attributes all over the viewmodel code. I was not willing to do this, since it would be a lot of work. And I am not that fond of conditional compilation attributes.

Instead I used a combination of the MVVM and MVP pattern. I did not want to throw away the existing viewmodels, since they were working just fine. I did move them though, to the views project (MemoChallenge.Wp7), so they were no longer present in the shared class library. As a replacement for the Android application (and the WP7 application, remember these projects are linked), I added presenter classes. My viewmodels became very slim, I practically removed all of their logic and moved it all to the corresponding presenters. Now only thing my viewmodels had to do is forward the call to a presenter and we're done.

   public class GameViewModel : ViewModelBase, IGameViewModel 
   { 
     private ICommand _startCommand; 
     private ICommand _sameCommand; 
     private ICommand _notSameCommand; 
     private GamePresenter _presenter; 
     private IGameData _gameData; 

     public GameViewModel() 
     { 
       _presenter = new GamePresenter(this); 
     } 

     public ICommand StartCommand 
     { 
       get 
       { 
         if (_startCommand == null) 
           _startCommand = new RelayCommand(() => _presenter.Start(), 
             () => _presenter.CanStart()); 
         return _startCommand; 
       } 
     } 

     //rest of the code omitted 
 }  

I hope this gives you an easy way to get MVVM to work on Android/MonoDroid.

No comments:

Post a Comment