祝各位2017年事业辉煌!开年第一篇博客,继续探索Xamarin.Forms…

为什么我做Xamarin开发的时候中意于Prism.Forms框架?本章为你揭晓。

实例代码地址:https://github.com/NewBLife/XamarinDemo/tree/master/TextToSpeechDemo

DependencyService

1、简介

软件开发有一个原则叫【依赖倒置Dependence Inversion Principle 】

A.高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象。

B.抽象不应该依赖于具体实现,具体实现应该依赖于抽象。

Xamarin.Forms在面对无法实现的平台特有功能时就是使用以上原则设计一个叫【DependencyService】的功能。DependencyService的目的就是让PCL共通代码可以调用与平台相关的功能,它使Xamarin.Forms能像原生应用一样做任何事情!

2、工作原理

  • 接口:定义功能接口在PCL类库或者共享类库
  • 接口实现:各个平台实现接口功能
  • 注册:各个平台实现接口的类库注册DependencyAttribute属性
  • 调用:PCL类库或者共享类库调用DependencyService.Get<接口>()方法获取平台实例对象

稍微看看原代码了解Xamarin.Forms如何实现依赖注入

DependencyAttribute.cs文件,定义了程序集属性标签:

  1. using System;
  2.  
  3. namespace Xamarin.Forms
  4.  
  5. {
  6.  
  7. [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
  8.  
  9. public class DependencyAttribute : Attribute
  10.  
  11. {
  12.  
  13. public DependencyAttribute(Type implementorType)
  14.  
  15. {
  16.  
  17. Implementor = implementorType;
  18.  
  19. }
  20.  
  21. internal Type Implementor { get; private set; }
  22.  
  23. }
  24.  
  25. }

DependencyService.cs文件的Get方法(实体对象默认是单例形式存在)。

  1. static bool s_initialized;
  2.  
  3. static readonly List<Type> DependencyTypes = new List<Type>();
  4.  
  5. static readonly Dictionary<Type, DependencyData> DependencyImplementations = new Dictionary<Type, DependencyData>();
  6.  
  7. public static T Get<T>(DependencyFetchTarget fetchTarget = DependencyFetchTarget.GlobalInstance) where T : class
  8.  
  9. {
  10.  
  11. if (!s_initialized)
  12.  
  13. Initialize();
  14.  
  15. Type targetType = typeof(T);
  16.  
  17. if (!DependencyImplementations.ContainsKey(targetType))
  18.  
  19. {
  20.  
  21. Type implementor = FindImplementor(targetType);
  22.  
  23. DependencyImplementations[targetType] = implementor != null ? new DependencyData { ImplementorType = implementor } : null;
  24.  
  25. }
  26.  
  27. DependencyData dependencyImplementation = DependencyImplementations[targetType];
  28.  
  29. if (dependencyImplementation == null)
  30.  
  31. return null;
  32.  
  33. if (fetchTarget == DependencyFetchTarget.GlobalInstance)
  34.  
  35. {
  36.  
  37. if (dependencyImplementation.GlobalInstance == null)
  38.  
  39. {
  40.  
  41. dependencyImplementation.GlobalInstance = Activator.CreateInstance(dependencyImplementation.ImplementorType);
  42.  
  43. }
  44.  
  45. return (T)dependencyImplementation.GlobalInstance;
  46.  
  47. }
  48.  
  49. return (T)Activator.CreateInstance(dependencyImplementation.ImplementorType);
  50.  
  51. }

DependencyService.cs文件的Initialize方法,遍历所有程序集获取标记了DependencyAttribute属性的类型。这是不太好的地方,这样的做法性能会大打折扣,这也是为什么不推荐使用DependencyService的一个方面。

  1. static void Initialize()
  2.  
  3. {
  4.  
  5. Assembly[] assemblies = Device.GetAssemblies();
  6.  
  7. if (Registrar.ExtraAssemblies != null)
  8.  
  9. {
  10.  
  11. assemblies = assemblies.Union(Registrar.ExtraAssemblies).ToArray();
  12.  
  13. }
  14.  
  15. Type targetAttrType = typeof(DependencyAttribute);
  16.  
  17. // Don't use LINQ for performance reasons
  18.  
  19. // Naive implementation can easily take over a second to run
  20.  
  21. foreach (Assembly assembly in assemblies)
  22.  
  23. {
  24.  
  25. Attribute[] attributes = assembly.GetCustomAttributes(targetAttrType).ToArray();
  26.  
  27. if (attributes.Length == )
  28.  
  29. continue;
  30.  
  31. foreach (DependencyAttribute attribute in attributes)
  32.  
  33. {
  34.  
  35. if (!DependencyTypes.Contains(attribute.Implementor))
  36.  
  37. {
  38.  
  39. DependencyTypes.Add(attribute.Implementor);
  40.  
  41. }
  42.  
  43. }
  44.  
  45. }
  46.  
  47. s_initialized = true;
  48.  
  49. }

3,实例使用

使用TextToSpeechDemo(文本语音)实例讲解如何使用DependencyService。

项目结构:

接口定义:

  1. namespace TextToSpeechDemo
  2. {
  3. public interface ITextToSpeech
  4. {
  5. void Speak(string text);
  6. }
  7. }

Android平台实现ITextToSpeech接口:API定义

最重要的[assembly: Dependency(typeof(TextToSpeech_Android))] 这句注册Dependency属性。

  1. using Android.Runtime;
  2. using Android.Speech.Tts;
  3. using System.Collections.Generic;
  4. using TextToSpeechDemo.Droid;
  5. using Xamarin.Forms;
  6.  
  7. [assembly: Dependency(typeof(TextToSpeech_Android))]
  8. namespace TextToSpeechDemo.Droid
  9. {
  10. public class TextToSpeech_Android : Java.Lang.Object, ITextToSpeech, TextToSpeech.IOnInitListener
  11. {
  12. TextToSpeech speaker;
  13. string toSpeak;
  14. public TextToSpeech_Android() { }
  15.  
  16. public void Speak(string text)
  17. {
  18. var ctx = Forms.Context;
  19. toSpeak = text;
  20. if (speaker == null)
  21. {
  22. speaker = new TextToSpeech(ctx, this);
  23. }
  24. else
  25. {
  26. var p = new Dictionary<string, string>();
  27. speaker.Speak(toSpeak, QueueMode.Flush, p);
  28. }
  29. }
  30.  
  31. public void OnInit([GeneratedEnum] OperationResult status)
  32. {
  33. if (status.Equals(OperationResult.Success))
  34. {
  35.  
  36. System.Diagnostics.Debug.WriteLine("speaker init");
  37.  
  38. var p = new Dictionary<string, string>();
  39.  
  40. speaker.Speak(toSpeak, QueueMode.Flush, p);
  41.  
  42. }
  43. else
  44. {
  45.  
  46. System.Diagnostics.Debug.WriteLine("was quiet");
  47.  
  48. }
  49. }
  50. }
  51. }

iOS平台实现ITextToSpeech接口:

最重要的[assembly: Dependency(typeof(TextToSpeech_iOS))] 这句注册Dependency属性。

  1. using AVFoundation;
  2. using TextToSpeechDemo.iOS;
  3. using Xamarin.Forms;
  4.  
  5. [assembly: Dependency(typeof(TextToSpeech_iOS))]
  6. namespace TextToSpeechDemo.iOS
  7. {
  8. class TextToSpeech_iOS : ITextToSpeech
  9. {
  10. public void Speak(string text)
  11. {
  12. var speechSynthesizer = new AVSpeechSynthesizer();
  13.  
  14. var speechUtterance = new AVSpeechUtterance(text)
  15. {
  16. Rate = AVSpeechUtterance.MaximumSpeechRate / ,
  17. Voice = AVSpeechSynthesisVoice.FromLanguage("en-US"),
  18. Volume = 0.5f,
  19. PitchMultiplier = 1.0f
  20. };
  21.  
  22. speechSynthesizer.SpeakUtterance(speechUtterance);
  23. }
  24. }
  25. }

UWP平台实现ITextToSpeech接口:

最重要的[assembly: Dependency(typeof(TextToSpeech_UWP))] 这句注册Dependency属性。

  1. using System;
  2. using TextToSpeechDemo.UWP;
  3. using Windows.Media.SpeechSynthesis;
  4. using Windows.UI.Xaml.Controls;
  5. using Xamarin.Forms;
  6.  
  7. [assembly: Dependency(typeof(TextToSpeech_UWP))]
  8. namespace TextToSpeechDemo.UWP
  9. {
  10. class TextToSpeech_UWP : ITextToSpeech
  11. {
  12. public async void Speak(string text)
  13. {
  14. MediaElement mediaElement = new MediaElement();
  15.  
  16. var synth = new SpeechSynthesizer();
  17. var stream = await synth.SynthesizeTextToStreamAsync(text);
  18. mediaElement.SetSource(stream, stream.ContentType);
  19. mediaElement.Play();
  20. }
  21. }
  22. }

调用平台特性的时候通过DependencyService.Get<T>()实现:

  1. public void btnSpeak_Clicked(object sender, EventArgs args)
  2. {
  3. DependencyService.Get<ITextToSpeech>().Speak(txtData.Text.Trim());
  4. }

整体效果:

IPlatformInitializer

1、简介

IPlatformInitializer其实为Prism.Forms共通类库里面的一个接口,代码如下:

  1. namespace Prism
  2. {
  3.  
  4. public interface IPlatformInitializer<T>
  5. {
  6.  
  7. void RegisterTypes(T container);
  8. }
  9. }

包含一个注册类型函数(注册实现了平台特性的类型)。至于为什么是泛型接口?这是为了支持多种IOC容器(AutoFac,Unity,DryIoc,Ninject等),主流为Unity。Unity的IPlatformInitializer代码如下:传入了Unity的容器类型IUnityContainer

  1. using Microsoft.Practices.Unity;
  2.  
  3. namespace Prism.Unity
  4. {
  5. public interface IPlatformInitializer : IPlatformInitializer<IUnityContainer>
  6. {
  7. }
  8. }

2、工作原理

  • 接口:定义功能接口在PCL类库或者共享类库
  • 接口实现:各个平台实现接口功能
  • 注册:各个平台实现IPlatformInitializer接口,并在RegisterTypes方法中将实现接口的类注册到IOC容器内
  • 调用:ViewModel的构造函数添加接口为参数(Prism.Forms会自动从IOC容器加载)

调用RegisterTypes是在Prism.Forms共通类库里面PrismApplicationBase<T>的构造函数中:

IPlatformInitializer<T> _platformInitializer = null;

  1. protected PrismApplicationBase(IPlatformInitializer<T> initializer = null)
  2. {
  3.  
  4. base.ModalPopping += PrismApplicationBase_ModalPopping;
  5. base.ModalPopped += PrismApplicationBase_ModalPopped;
  6.  
  7. _platformInitializer = initializer;
  8. InitializeInternal();
  9. }
  10. /// <summary>
  11. /// Run the intialization process.
  12. /// </summary>
  13. void InitializeInternal()
  14. {
  15.  
  16. ConfigureViewModelLocator();
  17. Initialize();
  18. OnInitialized();
  19. }
  20.  
  21. /// <summary>
  22. /// Run the bootstrapper process.
  23. /// </summary>
  24. public virtual void Initialize()
  25. {
  26. Logger = CreateLogger();
  27. ModuleCatalog = CreateModuleCatalog();
  28. ConfigureModuleCatalog();
  29. Container = CreateContainer();
  30. ConfigureContainer();
  31. NavigationService = CreateNavigationService();
  32. RegisterTypes();
  33. _platformInitializer

?

  1. .RegisterTypes(Container);
  2. InitializeModules();
  3. }

3,实例使用

使用PrismTextToSpeech(文本语音)实例讲解如何使用IPlatformInitializer。

项目结构:

接口定义:

  1. namespacePrismTextToSpeech.Services
  1. {
  2. public interface ITextToSpeech
  3. {
  4. void Speak(string text);
  5. }
  6. }

Android平台实现ITextToSpeech接口:API定义

与DependencyService的区别是没有Dependency属性。

  1. using Android.Runtime;
  2. using Android.Speech.Tts;
  3. using PrismTextToSpeech.Services;
  4. using System.Collections.Generic;
  5. using Xamarin.Forms;
  6.  
  7. namespace PrismTextToSpeech.Droid
  8. {
  9. public class TextToSpeech_Android : Java.Lang.Object, ITextToSpeech, TextToSpeech.IOnInitListener
  10. {
  11. TextToSpeech speaker;
  12. string toSpeak;
  13. public TextToSpeech_Android() { }
  14.  
  15. public void Speak(string text)
  16. {
  17. var ctx = Forms.Context;
  18. toSpeak = text;
  19. if (speaker == null)
  20. {
  21. speaker = new TextToSpeech(ctx, this);
  22. }
  23. else
  24. {
  25. var p = new Dictionary<string, string>();
  26. speaker.Speak(toSpeak, QueueMode.Flush, p);
  27. }
  28. }
  29.  
  30. public void OnInit([GeneratedEnum] OperationResult status)
  31. {
  32. if (status.Equals(OperationResult.Success))
  33. {
  34.  
  35. System.Diagnostics.Debug.WriteLine("speaker init");
  36.  
  37. var p = new Dictionary<string, string>();
  38.  
  39. speaker.Speak(toSpeak, QueueMode.Flush, p);
  40.  
  41. }
  42. else
  43. {
  44.  
  45. System.Diagnostics.Debug.WriteLine("was quiet");
  46.  
  47. }
  48. }
  49. }
  50. }

注册类型到IOC容器:

  1. using Android.App;
  2. using Android.Content.PM;
  3. using Android.OS;
  4. using Microsoft.Practices.Unity;
  5. using Prism.Unity;
  6. using PrismTextToSpeech.Services;
  7.  
  8. namespace PrismTextToSpeech.Droid
  9. {
  10. [Activity(Label = "PrismTextToSpeech", Icon = "@drawable/icon", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
  11. public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
  12. {
  13. protected override void OnCreate(Bundle bundle)
  14. {
  15. TabLayoutResource = Resource.Layout.tabs;
  16. ToolbarResource = Resource.Layout.toolbar;
  17.  
  18. base.OnCreate(bundle);
  19.  
  20. global::Xamarin.Forms.Forms.Init(this, bundle);
  21. LoadApplication(new App(new AndroidInitializer()));
  22. }
  23. }
  24.  
  25. public class AndroidInitializer : IPlatformInitializer
  26. {
  27. public void RegisterTypes(IUnityContainer container)
  28. {
  29. container.RegisterType

<ITextToSpeech, TextToSpeech_Android>

  1. ();
  2. }
  3. }
  4. }

iOS与UWP的接口实现与DependencyService的一样,唯独就是没有Dependency属性,这里略过。

调用的时候:

  1. using Prism.Commands;
  2. using Prism.Mvvm;
  3. using PrismTextToSpeech.Services;
  4.  
  5. namespace PrismTextToSpeech.ViewModels
  6. {
  7. public class MainPageViewModel : BindableBase
  8. {
  9. private ITextToSpeech _textToSpeech;
  10.  
  11. private string _speakText;
  12. public string SpeakText
  13. {
  14. get { return _speakText; }
  15. set
  16. {
  17. SetProperty(ref _speakText, value);
  18. SpeakCommand.RaiseCanExecuteChanged();
  19. }
  20. }
  21.  
  22. public MainPageViewModel(ITextToSpeech textToSpeech)
  23. {
  24. _textToSpeech

=

  1. textToSpeech;
  2. }
  3.  
  4. public DelegateCommand SpeakCommand => new DelegateCommand(
  5. () =>
  6. {
  7. _textToSpeech.Speak(SpeakText);
  8. },
  9. () => !string.IsNullOrEmpty(SpeakText)).ObservesProperty(() => this.SpeakText);
  10. }
  11. }

Prism就是这么简单,效果更佳:

DependencyAttribute+IPlatformInitializer

1、简介

这种方式是Prism为了兼容DepdencyService而创建的,及Prism内部封装了DependencyService。

  1. namespace Prism.Services
  2.  
  3. {
  4.  
  5. /// <summary>
  6.  
  7. /// A service that provides acess to platform-specific implementations of a specified type
  8.  
  9. /// </summary>
  10.  
  11. public class DependencyService : IDependencyService
  12.  
  13. {
  14.  
  15. /// <summary>
  16.  
  17. /// Returns a platform-specific implementation of a type registered with the Xamarin.Forms.DependencyService
  18.  
  19. /// </summary>
  20.  
  21. /// <typeparam name="T">The type of class to get</typeparam>
  22.  
  23. /// <returns>The class instance</returns>
  24.  
  25. public T Get<T>() where T : class
  26.  
  27. {
  28.  
  29. return Xamarin.Forms.DependencyService.Get<T>();
  30.  
  31. }
  32.  
  33. }
  34.  
  35. }

2、使用方法

  • 接口:与DependencyService或者IPlatformInitializer实例一样
  • 接口实现:与DependencyService实例一样
  • 注册:与DependencyService实例一样,各个平台实现接口的类库注册DependencyAttribute属性
  • 调用:与IPlatformInitializer实例一样,ViewModel的构造函数添加接口为参数(Prism.Forms会自动从IOC容器加载)

总结

DependencyService其实就是依赖注入的自我实现,而Prism的IPlatformInitializer则巧妙的借助Unity等容器达到同样的目的。不过从应用以后扩展角度也好,性能角度也好还是建议使用IOC容器技术(Prism创始人Brian Lagunas也是这么建议的)。特别是在使用Mvvm模式开发的时候,更加需要依赖IOC容器来管理ViewModel与Service,这也是我选择Prism做Xamarin开发的原因之一。

Xamarin+Prism开发详解六:DependencyService与IPlatformInitializer的关系的更多相关文章

  1. Xamarin+Prism开发详解七:Plugin开发与打包测试

    有了上章[Xamarin+Prism开发详解六:DependencyService与IPlatformInitializer的关系]的基础,现在来理解Plugin开发就简单了. 本文实例代码地址:ht ...

  2. Xamarin+Prism开发详解四:简单Mac OS 虚拟机安装方法与Visual Studio for Mac 初体验

    Mac OS 虚拟机安装方法 最近把自己的电脑升级了一下SSD固态硬盘,总算是有容量安装Mac 虚拟机了!经过心碎的安装探索,尝试了国内外的各种安装方法,最后在youtube上找到了一个好方法. 简单 ...

  3. Xamarin+Prism开发详解一:PCL跨平台类库与Profile的关系

    在[Xamarin+Prism小试牛刀:定制跨平台Outlook邮箱应用]中提到过以下错误,不知道大伙还记得不: 无法安装程序包"Microsoft.Identity.Client 1.0. ...

  4. Xamarin+Prism开发详解三:Visual studio 2017 RC初体验

    Visual studio 2017 RC出来一段时间了,最近有时间就想安装试试,随带分享一下安装使用体验. 1,卸载visual studio 2015 虽然可以同时安装visual studio ...

  5. Xamarin+Prism开发详解五:页面布局基础知识

    说实在的研究Xamarin到现在,自己就没设计出一款好的UI,基本都在研究后台逻辑之类的!作为Xamarin爱好者,一些简单的页面布局知识还是必备的. 布局常见标签: StackLayout Abso ...

  6. Xamarin+Prism开发详解二:Xaml文件如何简单绑定Resources资源文件内容

    我们知道在UWP里面有Resources文件xxx.resx,在Android里面有String.Xml文件等.那跨平台如何统一这些类别不一的资源文件以及Xaml设计文件如何绑定这些资源?应用支持多国 ...

  7. Xamarin+Prism开发详解八:自动化测试之NUnit实践

    自动化测试很重要!很重要!以前多是手动测试,没有写过测试用例.这样的结果就是发现bug改了之后关联的其他功能又要从新测一遍.这样既浪费时间与成本,而且很无聊.之所以选择NUnit是公司需要,现在.ne ...

  8. 在【Xamarin+Prism开发详解三:Visual studio 2017 RC初体验】中分享了Visual studio 2017RC的大致情况,同时也发现大家对新的Visual Studio很是感兴趣。于是发时间深入研究了一下Visual Studio 2017RC 是不是和微软Connect()://2016上说得一样神。

    总共列出了12点,耐心点慢慢看! 1,添加了不少[代码样式]的设置项目. 通过合理的设置每个人都能写出优美的代码,而且团队项目也可以达到统一代码风格. this首选项:可以设置[字段,属性,方法,事件 ...

  9. EasyPR--开发详解(6)SVM开发详解

    在前面的几篇文章中,我们介绍了EasyPR中车牌定位模块的相关内容.本文开始分析车牌定位模块后续步骤的车牌判断模块.车牌判断模块是EasyPR中的基于机器学习模型的一个模块,这个模型就是作者前文中从机 ...

随机推荐

  1. MVVM设计模式和WPF中的实现(四)事件绑定

    MVVM设计模式和在WPF中的实现(四) 事件绑定 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在WPF中 ...

  2. JavaScript中Math对象的方法介绍

    1.比较最值方法 比较最值有两种方法,max() 和 min() 方法. 1.1 max() 方法,比较一组数值中的最大值,返回最大值. var maxnum = Math.max(12,6,43,5 ...

  3. PhotoView实现图片随手势的放大缩小的效果

    项目需求:在listView的条目中如果有图片,点击条目,实现图片的放大,并且图片可以根据手势来控制图片放大缩小的比例.类似于微信朋友圈中查看好友发布的照片所实现的效果. 思路是这样的:当点击条目的时 ...

  4. 谈谈一些有趣的CSS题目(四)-- 从倒影说起,谈谈 CSS 继承 inherit

    开本系列,讨论一些有趣的 CSS 题目,抛开实用性而言,一些题目为了拓宽一下解决问题的思路,此外,涉及一些容易忽视的 CSS 细节. 解题不考虑兼容性,题目天马行空,想到什么说什么,如果解题中有你感觉 ...

  5. Xamarin+Prism小试牛刀:定制跨平台Outlook邮箱应用

    通过本文你将学会如下内容: 1,如何使用Xamarin开发跨平台(Windows,Android,iOS)应用. 2,如何使用微软的登录界面登入Microsoft账号. 3,如何使用Outlook邮箱 ...

  6. 前端HTML5几种存储方式的总结

    接下来要好好总结一些知识,秋招来啦...虽然有好多知识都不大会,但是还是要努力一下,运气这种东西,谁知道呢~ 总体情况 h5之前,存储主要是用cookies.cookies缺点有在请求头上带着数据,大 ...

  7. 【Big Data】HADOOP集群的配置(一)

    Hadoop集群的配置(一) 摘要: hadoop集群配置系列文档,是笔者在实验室真机环境实验后整理而得.以便随后工作所需,做以知识整理,另则与博客园朋友分享实验成果,因为笔者在学习初期,也遇到不少问 ...

  8. Java中,异常的处理及抛出

    首先我们需要知道什么是异常? 常通常指,你的代码可能在编译时没有错误,可是运行时会出现异常.比如常见的空指针异常.也可能是程序可能出现无法预料的异常,比如你要从一个文件读信息,可这个文件不存在,程序无 ...

  9. BPM费控管理解决方案分享

    一.方案概述费用是除经营成本外企业的最主要支出,费用管理是财务管理的核心之一,加强企业内控管理如:费用申请.费用报销.费用分摊.费用审批.费用控制和费用支付等,通过科学有效的管理方法规范企业费用管理, ...

  10. Android Studio-—使用OpenCV的配置方法和demo以及开发过程中遇到的问题解决

    前提: 1.安装Android Studio(过程略) 2.官网下载OpenCV for Android 网址:http:opencv.org/downloads.html 我下载的是下图的版本 3. ...