在上一篇Xamarin开发环境及开发框架初探中,曾简单提到MvvmCross这个Xamarin下的开发框架。最近又评估了一些别的,发现老牌Mvvm框架Prism现在也支持Xamarin Forms了,可喜可贺!今天我们就来近距离尝试、比较一下,分别基于这两个框架写一个简单Android/iOS跨平台应用的感受。

Prism

Prism框架应该来源于微软的Microsoft patterns & practices的Prism Guidance。最初,应该被更多用于WPF开发。我对WPF了解太少,就不评论它的过去了,我们这里重点关注他目前Preview状态的Prism Xamarin Forms支持。它提供了一个VS2015的模版插件,可以从这里直接下载安装,也可以从VS2015的Extensions and Updates菜单里面下载安装。安装这个插件,并重启VS2015后,我们可以看到,项目模版里多了一个Prism分组,包含三个项目类型,其中一个是Prism Unity App (Forms),就是for Xamarin Forms的。名字里包含Unity,那是指这个模版使用Unity DI框架。

事实上Prism支持Autofac,NInject等各种DI框架,不过模版只有Unity的。我们来尝试新建一个项目。相比于Xamarin官方及其他开发框架,它的项目模版,提供了非常友好的UI,可以选择新建的项目需要支持哪些平台。如下图:

我们就选Android和iOS。新建的项目很自然的包含一个PCL格式的共享项目,和一个Droid项目,一个iOS项目:

从目录结构来看,和Xamarin Forms官方的模版生成的项目非常类似,表面看来,好像就是在共享项目里多了ViewModels和Views目录。我们来看看Droid的MainActivity类,我们知道这个类是每一个Xamarin Droid项目的入口类,基本相当于Console程序的Main方法的地位。可以看到,LoadApplication()方法还是接受一个App类的instance,(这个类还是定义在共享项目里的,我们下面再细说),不过,这个App类的构造函数接受一个实现了Prism的IPlatformInitializer接口的AndroidInitializer。它只包含一个RegisterTypes(UUnityContainer container)方法,非常容易理解,就是用来配置Unity容器的,我们可以在这里注册各种业务层、数据层等等的接口和类到Unity。

好像Droid项目相比官方Xamarin Forms模版也就这点区别了,可见Prism对官方模版架构的改动非常小。我们再来看看iOS项目的AppDelegate类。这个类和MainActivity的地位相当,也是iOS项目的入口类。看上去也是和MainActivity做的事情非常类似,这里也包含一个iOSInitializer类实现了IPlatformInitializer接口。太容易理解了,自描述,都不需要文档,我喜欢。

下面就剩下在共享项目了。我们先来看看App类,它还是包含一个App.xaml和一个code behind文件。App.xaml看上去和Xamarin Forms官方模版的不一样了,它继承于PrismApplication类。

App.xaml.cs的内容就有比较大的不同了。首先,他当然需要接受一个IPlatformInitializer,并传递给基类,这个容易理解。此外,两个override方法,一个RegisterTypes()用于注册功能页面。另一个OnInitialized()用于指定默认打开那个页面,并且还可以给页面传参数。还是足够简单的。

很明显,项目运行时,会先显示MainPage,而MainPage的xaml和对应的ViewModel分别定义在Views目录和ViewModels目录中。MainPage是一个标准的xaml page,唯一的和Xamarin Forms版本不同的是,它包含了一个prism:ViewModelLocator.AutowireViewModel="True"的属性,从字面意思,我们就能知道,它表示,他会自动resolve并绑定到一个同名ViewModel,也就是ViewModels目录中的MainPageViewModel类。而这个类的实现也是不能再简单了。主要就一个构造函数加两个方法,一个OnNavigatedFrom(),一个OnNavigatedTo(),真的不需要文档,就能理解,一个是页面load时执行的逻辑,一个是页面离开时执行的逻辑。

让我们来尝试增加一个第二个页面和对应的ViewModel,看看页面跳转怎么玩。我们来新建一个SecondPage.xaml和对应的SecondPageViewModel类。页面功能就照抄MainPage了,只是显示的text不同。然后,我们需要在App类的RegisterTypes()里注册SecondPage:

接下来,我们来给MainPage.xaml增加一个按钮,点击切换到SecondPage:

当然,MainPageViewModel里我们也要添加相应的代码,主要是,要依赖注入一个INavigationService,用来执行跳转,当然还要一个LoadSecondCommand绑定到前面那个按钮。

Ok, 运行程序,点击按钮,就能从MainPage跳转到SecondPage了。

Prism的简单示例就到这儿。总体的感觉就是,Prism框架和Xamarin Forms本身的架构结合地的非常自然,接口定义,和编码方式也非常符合一般人的思路,几乎不需文档就能理解。

MvvmCross

下面我们再来近距离看看MvvmCross的情况。MvvmCross也有一个VS2015的插件,提供了支持Forms的项目模版。我们来练一把。下载安装这个插件后。我们来新建一个项目。新建的项目是下面这个样子:

两个Test项目我们先不管。我们可以看到,它相比Prism多了不少东西,我们一个一个来看。这次,我们先从共享项目开始。Model和Repository目录可以先忽略,他们只是生成了数据访问的repository接口和Model,其实和MvvmCross本身没什么关系。Resources和Services目录也可以先放一边,因为,它们实现了一个简易的多语言支持方案,接口其实非常简单明了。ViewModels和Views目录,其实和Prism的是一个概念,不过略有差别。

第一个差别是,MainPage.xaml中定义了一个xmlns:res="clr-namespace:MvvmCrossForms1.Core.Resources;assembly=MvvmCrossForms1.Core"属性,做什么用的呢?通过它可以访问Resources目录中定义的多语言翻译的文本资源。有了它,我们可以很方便的绑定多语言文本资源到xaml上的属性。

第二个差别是,所有的ViewModel类需要继承自MvvmCross提供的MvxViewModel类。并且,需要为每个ViewModel属性的set实现,调用基类的RaisePropertyChanged()方法。这样,理论上,它就能动态获知View上的属性变更,为实现动态数据绑定,提供了可能。

接下来,是App类。虽然名字还是叫App,实际上,它这个App类并不是最终继承自Xamarin Forms的Application类的,它不是一个View,也没有xaml,而是继承于MvvmCross自己的Application类。在具体的Driod和iOS项目到时可以看到,它的框架内部实际上封装了真正的Xamarin Application类。这个App类只包含一个Initialize()方法实现,前半部分调用它的内部IoC容器的配置方法,将所有的Services和Repositories接口和类,注册到了IoC容器。中间设置了App的当前语言。最后一行,调用RegisterAppStart<ViewModels.MainViewModel>()显示默认的MainPage页面。但是注意,比较特别的是,它不是通过页面的名称,而是通过ViewModel类,强类型方式制定要显示哪个页面的。

好了,共享项目就这些特别了。我们来演示如何进行页面跳转。新建名叫SecondPage的xaml和一个名叫SecondViewModel的ViewModel类。然后,给MainPage.xaml添加一个Button如下:

然后,在MainViewModel类中,添加一个ShowSecondCommand就行了,这个比Prism的版本简单多了。注意,它这里也是通过强类型的ViewModel进行页面跳转的。强类型的好处是,如果有任何重构,所有的引用会自动更新,不像Prism的字符串,还要自己手动改。

再来看Droid项目。Droid项目的Bootstrap目录一般用于MvvmCross的各种插件的初始化,这里不详述。Services目录呢,一般用于放置共享项目中定义的某些功能接口的Droid特定实现,这里也可以先忽略。Linker类可以忽略,是它生成了给MvvmCross框架自己用的。

这个Droid项目真正的入口是SplashScreen,它基于标准的Xamarin Activity提供了一个App开机页面的默认实现。它的isInitializationComplete()方法,指定了,App完成后,显示MvxFormsApplicationActivity这个Activity。

SplashScreen的基类内部,会通过反射,去寻找项目中一个名叫Setup的类,这个Setup类里,就是放所有App初始化代码的地方。所有的初始化执行完之后,SplashScreen类的isInitializationComplete()方法才会被调用。

一旦isInitializationComplete()方法被调用,MvxFormsApplicationActivity类被执行,在它的OnCreate()方法会最终启动,定义在共享项目里的App类,然后MainPage才会被真正显示。

iOS项目的情况,略有不同,没看到类似Droid项目的SplashPage功能(可能是不同的team负责iOS和Droid部分开发的?貌似Droid版本功能更完善一些。这个还要在摸索下)。iOS项目的启动类还是AppDelegate,它的FinishedLaunching()方法简单的调用了Setup类,并且直接启动了共享代码里的App类,这样MainPage就能被显示了。

老实说,相比Prism的执行过程,MvvmCross的确实有点绕。不过,它帮你做了好几件任何App开发可能都需要处理的事,包括开机画面、初始化管理、简化的依赖注入、多语言支持,更简单的页面跳转,更简单的数据绑定,另外它还提供了很多其他的插件模块。最关键的是,在MvvmCross的架构下,那些真正复杂难懂的部分,其实都是框架级别的代码,而增加View,ViewModel,Service等这些业务相关的模块的管理,反而比Prism下更简单。所以,如果要我选的话,如果要开发的是一个企业App,功能繁多,需要支持多语言的App,我可能还是会倾向于MvvmCross。我也喜欢Prism的优雅,简洁。真心希望Prism未来能提供更多方便使用的插件,当然,也许只是我孤陋寡闻,果真如此的话就求之不得了!

BTW,不知道为什么,在我本机的iOS模拟器中,升级到iOS 10以后,MvvmCross的iOSApp运行都会闪退,感觉是MvvmCross的bug,希望能尽快有人fix。Prism的iOS项目运行没有任何问题。如果谁知道原因,也请不吝赐教,谢谢!

Update 1:

在MvvmCross的issue tracker新建了一个issue,描述了这个crash的问题:https://github.com/MvvmCross/MvvmCross/issues/1452

Update 2:

上面提到的iOS debugging MvvmCross app crash的问题已经解决了,我删了模拟器里所有以前部署的app,再次debug就可以运行了,很神奇。怀疑是不是,以前的某个app部署过程出错了,然后,后续部署的app和以前有问题的这个app冲突了。不过,至少咱有活路了不是?

Update 3:

免费附赠两行脚本,当Xamarin iOS app调试出错时,如何查看iOS模拟器的device log呢?只需要在MacOS里起一个terminal,执行下面的两条命令:

# list all device's device codes
instruments -s devices # view latest log of a specific device
tail -f ~/Library/Logs/CoreSimulator/<DEVICE_CODE>/system.log

老司机学新平台 - Xamarin Forms开发框架二探 (Prism vs MvvmCross)的更多相关文章

  1. 老司机学新平台 - Xamarin Forms开发框架之MvvmCross插件精选

    在前两篇老司机学Xamarin系列中,简单介绍了Xamarin开发环境的搭建以及Prism和MvvmCross这两个开发框架.不同的框架,往往不仅仅使用不同的架构风格,同时社区活跃度不同,各种功能模块 ...

  2. 老司机学新平台 - Xamarin开发之我的第一个MvvmCross跨平台插件:SimpleAudioPlayer

    大家好,老司机学Xamarin系列又来啦!上一篇MvvmCross插件精选文末提到,Xamarin平台下,一直没找到一个可用的跨平台AudioPlayer插件.那就自力更生,让我们就自己来写一个吧! ...

  3. 老司机学新平台 - Xamarin开发环境及开发框架初探

    随着被微软收购,最近一年间,Xamarin的火爆程度与日俱增.免费.更好的VS2015集成.更好的模拟器,甚至,在windows上运行和调试iOS平台程序,让我这样接触了十几年.NET平台的老司机,即 ...

  4. 老司机学Xamarin系列总目录

    Xamarin开发环境及开发框架初探 Xamarin Forms开发框架二探 (Prism vs MvvmCross) Xamarin Forms开发框架之MvvmCross插件精选 Xamarin开 ...

  5. C语言老司机学Python (五)

    今天看的是标准库概览. 操作系统接口: 用os模块实现. 针对文件和目录管理,还有个shutil模块可以用. 例句: import os os.getcwd() # 返回当前的工作目录 os.chdi ...

  6. C语言老司机学Python (一)

    Python 版本:3.6.4 参考网上教程:http://www.runoob.com/python3/python3-basic-syntax.html 开始了啊. 干咱们这行的老规矩,学新语言的 ...

  7. C语言老司机学Python (六)- 多线程

    前面的1-5都是比较基础的东西,能做的事情也有限. 从本节起,随着更多进阶技术的掌握,渐渐就可以用Python开始浪了. Python3使用threading模块来实现线程操作. 根据在其他语言处学来 ...

  8. C语言老司机学Python (三)

    条件语句: 注意1) condition后面的冒号 2) elif if condition_1: statement_block_1elif condition_2: statement_block ...

  9. C语言老司机学Python (二)

    标准数据类型: 共6种:Number(数字),String(字符串),List(列表),Tuple(元组),Sets(集合),Dictionary(字典) 本次学习主要是和数据类型混个脸熟,知道每样东 ...

随机推荐

  1. git 的基本使用

    git 的使用步骤: 1. 新建一个文件夹,然后进入终端, 2. cd <文件夹路径>  ——->进入当前目录: 2.psw    ————>查看当前路径 3.git init ...

  2. 各大IT技术博客排行榜

    cnblogs 积分排名前3000名 http://www.cnblogs.com/ 左侧有推荐博客排行 cppblog http://www.cppblog.com/AllBloggers.aspx ...

  3. Servlet规范简介——web框架是如何注入到Servlet中的

    Servlet规范简介--web框架是如何注入到Servlet中的 引言 Web框架一般是通过一个Servlet提供统一的请求入口,将指定的资源映射到这个servlet,在这个servlet中进行框架 ...

  4. BZOJ1565 [NOI2009]植物大战僵尸(拓扑排序 + 最大权闭合子图)

    题目 Source http://www.lydsy.com/JudgeOnline/problem.php?id=1565 Description Input Output 仅包含一个整数,表示可以 ...

  5. Win10系统怎样让打开图片方式为照片查看器

    转载自:百度经验 http://jingyan.baidu.com/article/5d368d1ef0cad13f60c057e3.html 1.首先,我们需要使用注册表编辑器来开启Win10系统照 ...

  6. 洛谷1640 bzoj1854游戏 匈牙利就是又短又快

    bzoj炸了,靠离线版题目做了两道(过过样例什么的还是轻松的)但是交不了,正巧洛谷有个"大牛分站",就转回洛谷做题了 水题先行,一道傻逼匈牙利 其实本来的思路是搜索然后发现写出来类 ...

  7. HDU 5008 Boring String Problem(后缀数组+二分)

    题目链接 思路 想到了,但是木写对啊....代码 各种bug,写的乱死了.... 输出最靠前的,比较折腾... #include <cstdio> #include <cstring ...

  8. Android 学习笔记之一 “Unable to establish loopback connection”

    今天碰到一个错误:Unable to establish loopback connection,在网上找各种方法都解决不了,后来看一个帖子说是要关闭系统防火墙,尝试了下还是不行.最后是进任务管理器杀 ...

  9. penpyxl basic function demo code

    Openpyxl basic function demo code demo code: #!/usr/bin/env python # -*- coding: utf-8 -*- "&qu ...

  10. fatal error

    1.   fatal error C1083: 无法打开源文件 编译报此错误:  1>c1xx : fatal error C1083: 无法打开源文件:“Projects\XXXCCCC\VB ...