Xamarin+Prism开发详解六:DependencyService与IPlatformInitializer的关系
祝各位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文件,定义了程序集属性标签:
- using System;
- namespace Xamarin.Forms
- {
- [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
- public class DependencyAttribute : Attribute
- {
- public DependencyAttribute(Type implementorType)
- {
- Implementor = implementorType;
- }
- internal Type Implementor { get; private set; }
- }
- }
DependencyService.cs文件的Get方法(实体对象默认是单例形式存在)。
- static bool s_initialized;
- static readonly List<Type> DependencyTypes = new List<Type>();
- static readonly Dictionary<Type, DependencyData> DependencyImplementations = new Dictionary<Type, DependencyData>();
- public static T Get<T>(DependencyFetchTarget fetchTarget = DependencyFetchTarget.GlobalInstance) where T : class
- {
- if (!s_initialized)
- Initialize();
- Type targetType = typeof(T);
- if (!DependencyImplementations.ContainsKey(targetType))
- {
- Type implementor = FindImplementor(targetType);
- DependencyImplementations[targetType] = implementor != null ? new DependencyData { ImplementorType = implementor } : null;
- }
- DependencyData dependencyImplementation = DependencyImplementations[targetType];
- if (dependencyImplementation == null)
- return null;
- if (fetchTarget == DependencyFetchTarget.GlobalInstance)
- {
- if (dependencyImplementation.GlobalInstance == null)
- {
- dependencyImplementation.GlobalInstance = Activator.CreateInstance(dependencyImplementation.ImplementorType);
- }
- return (T)dependencyImplementation.GlobalInstance;
- }
- return (T)Activator.CreateInstance(dependencyImplementation.ImplementorType);
- }
DependencyService.cs文件的Initialize方法,遍历所有程序集获取标记了DependencyAttribute属性的类型。这是不太好的地方,这样的做法性能会大打折扣,这也是为什么不推荐使用DependencyService的一个方面。
- static void Initialize()
- {
- Assembly[] assemblies = Device.GetAssemblies();
- if (Registrar.ExtraAssemblies != null)
- {
- assemblies = assemblies.Union(Registrar.ExtraAssemblies).ToArray();
- }
- Type targetAttrType = typeof(DependencyAttribute);
- // Don't use LINQ for performance reasons
- // Naive implementation can easily take over a second to run
- foreach (Assembly assembly in assemblies)
- {
- Attribute[] attributes = assembly.GetCustomAttributes(targetAttrType).ToArray();
- if (attributes.Length == )
- continue;
- foreach (DependencyAttribute attribute in attributes)
- {
- if (!DependencyTypes.Contains(attribute.Implementor))
- {
- DependencyTypes.Add(attribute.Implementor);
- }
- }
- }
- s_initialized = true;
- }
3,实例使用
使用TextToSpeechDemo(文本语音)实例讲解如何使用DependencyService。
项目结构:
接口定义:
- namespace TextToSpeechDemo
- {
- public interface ITextToSpeech
- {
- void Speak(string text);
- }
- }
Android平台实现ITextToSpeech接口:API定义
最重要的[assembly: Dependency(typeof(TextToSpeech_Android))] 这句注册Dependency属性。
- using Android.Runtime;
- using Android.Speech.Tts;
- using System.Collections.Generic;
- using TextToSpeechDemo.Droid;
- using Xamarin.Forms;
- [assembly: Dependency(typeof(TextToSpeech_Android))]
- namespace TextToSpeechDemo.Droid
- {
- public class TextToSpeech_Android : Java.Lang.Object, ITextToSpeech, TextToSpeech.IOnInitListener
- {
- TextToSpeech speaker;
- string toSpeak;
- public TextToSpeech_Android() { }
- public void Speak(string text)
- {
- var ctx = Forms.Context;
- toSpeak = text;
- if (speaker == null)
- {
- speaker = new TextToSpeech(ctx, this);
- }
- else
- {
- var p = new Dictionary<string, string>();
- speaker.Speak(toSpeak, QueueMode.Flush, p);
- }
- }
- public void OnInit([GeneratedEnum] OperationResult status)
- {
- if (status.Equals(OperationResult.Success))
- {
- System.Diagnostics.Debug.WriteLine("speaker init");
- var p = new Dictionary<string, string>();
- speaker.Speak(toSpeak, QueueMode.Flush, p);
- }
- else
- {
- System.Diagnostics.Debug.WriteLine("was quiet");
- }
- }
- }
- }
iOS平台实现ITextToSpeech接口:
最重要的[assembly: Dependency(typeof(TextToSpeech_iOS))] 这句注册Dependency属性。
- using AVFoundation;
- using TextToSpeechDemo.iOS;
- using Xamarin.Forms;
- [assembly: Dependency(typeof(TextToSpeech_iOS))]
- namespace TextToSpeechDemo.iOS
- {
- class TextToSpeech_iOS : ITextToSpeech
- {
- public void Speak(string text)
- {
- var speechSynthesizer = new AVSpeechSynthesizer();
- var speechUtterance = new AVSpeechUtterance(text)
- {
- Rate = AVSpeechUtterance.MaximumSpeechRate / ,
- Voice = AVSpeechSynthesisVoice.FromLanguage("en-US"),
- Volume = 0.5f,
- PitchMultiplier = 1.0f
- };
- speechSynthesizer.SpeakUtterance(speechUtterance);
- }
- }
- }
UWP平台实现ITextToSpeech接口:
最重要的[assembly: Dependency(typeof(TextToSpeech_UWP))] 这句注册Dependency属性。
- using System;
- using TextToSpeechDemo.UWP;
- using Windows.Media.SpeechSynthesis;
- using Windows.UI.Xaml.Controls;
- using Xamarin.Forms;
- [assembly: Dependency(typeof(TextToSpeech_UWP))]
- namespace TextToSpeechDemo.UWP
- {
- class TextToSpeech_UWP : ITextToSpeech
- {
- public async void Speak(string text)
- {
- MediaElement mediaElement = new MediaElement();
- var synth = new SpeechSynthesizer();
- var stream = await synth.SynthesizeTextToStreamAsync(text);
- mediaElement.SetSource(stream, stream.ContentType);
- mediaElement.Play();
- }
- }
- }
调用平台特性的时候通过DependencyService.Get<T>()实现:
- public void btnSpeak_Clicked(object sender, EventArgs args)
- {
- DependencyService.Get<ITextToSpeech>().Speak(txtData.Text.Trim());
- }
整体效果:
IPlatformInitializer
1、简介
IPlatformInitializer其实为Prism.Forms共通类库里面的一个接口,代码如下:
- namespace Prism
- {
- public interface IPlatformInitializer<T>
- {
- void RegisterTypes(T container);
- }
- }
包含一个注册类型函数(注册实现了平台特性的类型)。至于为什么是泛型接口?这是为了支持多种IOC容器(AutoFac,Unity,DryIoc,Ninject等),主流为Unity。Unity的IPlatformInitializer代码如下:传入了Unity的容器类型IUnityContainer
- using Microsoft.Practices.Unity;
- namespace Prism.Unity
- {
- public interface IPlatformInitializer : IPlatformInitializer<IUnityContainer>
- {
- }
- }
2、工作原理
- 接口:定义功能接口在PCL类库或者共享类库
- 接口实现:各个平台实现接口功能
- 注册:各个平台实现IPlatformInitializer接口,并在RegisterTypes方法中将实现接口的类注册到IOC容器内
- 调用:ViewModel的构造函数添加接口为参数(Prism.Forms会自动从IOC容器加载)
调用RegisterTypes是在Prism.Forms共通类库里面PrismApplicationBase<T>的构造函数中:
- protected PrismApplicationBase(IPlatformInitializer<T> initializer = null)
- {
- base.ModalPopping += PrismApplicationBase_ModalPopping;
- base.ModalPopped += PrismApplicationBase_ModalPopped;
- _platformInitializer = initializer;
- InitializeInternal();
- }
- /// <summary>
- /// Run the intialization process.
- /// </summary>
- void InitializeInternal()
- {
- ConfigureViewModelLocator();
- Initialize();
- OnInitialized();
- }
- /// <summary>
- /// Run the bootstrapper process.
- /// </summary>
- public virtual void Initialize()
- {
- Logger = CreateLogger();
- ModuleCatalog = CreateModuleCatalog();
- ConfigureModuleCatalog();
- Container = CreateContainer();
- ConfigureContainer();
- NavigationService = CreateNavigationService();
- RegisterTypes();
- _platformInitializer
?
- .RegisterTypes(Container);
- InitializeModules();
- }
3,实例使用
使用PrismTextToSpeech(文本语音)实例讲解如何使用IPlatformInitializer。
项目结构:
接口定义:
- namespacePrismTextToSpeech.Services
- {
- public interface ITextToSpeech
- {
- void Speak(string text);
- }
- }
Android平台实现ITextToSpeech接口:API定义
与DependencyService的区别是没有Dependency属性。
- using Android.Runtime;
- using Android.Speech.Tts;
- using PrismTextToSpeech.Services;
- using System.Collections.Generic;
- using Xamarin.Forms;
- namespace PrismTextToSpeech.Droid
- {
- public class TextToSpeech_Android : Java.Lang.Object, ITextToSpeech, TextToSpeech.IOnInitListener
- {
- TextToSpeech speaker;
- string toSpeak;
- public TextToSpeech_Android() { }
- public void Speak(string text)
- {
- var ctx = Forms.Context;
- toSpeak = text;
- if (speaker == null)
- {
- speaker = new TextToSpeech(ctx, this);
- }
- else
- {
- var p = new Dictionary<string, string>();
- speaker.Speak(toSpeak, QueueMode.Flush, p);
- }
- }
- public void OnInit([GeneratedEnum] OperationResult status)
- {
- if (status.Equals(OperationResult.Success))
- {
- System.Diagnostics.Debug.WriteLine("speaker init");
- var p = new Dictionary<string, string>();
- speaker.Speak(toSpeak, QueueMode.Flush, p);
- }
- else
- {
- System.Diagnostics.Debug.WriteLine("was quiet");
- }
- }
- }
- }
注册类型到IOC容器:
- using Android.App;
- using Android.Content.PM;
- using Android.OS;
- using Microsoft.Practices.Unity;
- using Prism.Unity;
- using PrismTextToSpeech.Services;
- namespace PrismTextToSpeech.Droid
- {
- [Activity(Label = "PrismTextToSpeech", Icon = "@drawable/icon", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
- public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
- {
- protected override void OnCreate(Bundle bundle)
- {
- TabLayoutResource = Resource.Layout.tabs;
- ToolbarResource = Resource.Layout.toolbar;
- base.OnCreate(bundle);
- global::Xamarin.Forms.Forms.Init(this, bundle);
- LoadApplication(new App(new AndroidInitializer()));
- }
- }
- public class AndroidInitializer : IPlatformInitializer
- {
- public void RegisterTypes(IUnityContainer container)
- {
- container.RegisterType
<ITextToSpeech, TextToSpeech_Android>
- ();
- }
- }
- }
iOS与UWP的接口实现与DependencyService的一样,唯独就是没有Dependency属性,这里略过。
调用的时候:
- using Prism.Commands;
- using Prism.Mvvm;
- using PrismTextToSpeech.Services;
- namespace PrismTextToSpeech.ViewModels
- {
- public class MainPageViewModel : BindableBase
- {
- private ITextToSpeech _textToSpeech;
- private string _speakText;
- public string SpeakText
- {
- get { return _speakText; }
- set
- {
- SetProperty(ref _speakText, value);
- SpeakCommand.RaiseCanExecuteChanged();
- }
- }
- public MainPageViewModel(ITextToSpeech textToSpeech)
- {
- _textToSpeech
=
- textToSpeech;
- }
- public DelegateCommand SpeakCommand => new DelegateCommand(
- () =>
- {
- _textToSpeech.Speak(SpeakText);
- },
- () => !string.IsNullOrEmpty(SpeakText)).ObservesProperty(() => this.SpeakText);
- }
- }
Prism就是这么简单,效果更佳:
DependencyAttribute+IPlatformInitializer
1、简介
这种方式是Prism为了兼容DepdencyService而创建的,及Prism内部封装了DependencyService。
- namespace Prism.Services
- {
- /// <summary>
- /// A service that provides acess to platform-specific implementations of a specified type
- /// </summary>
- public class DependencyService : IDependencyService
- {
- /// <summary>
- /// Returns a platform-specific implementation of a type registered with the Xamarin.Forms.DependencyService
- /// </summary>
- /// <typeparam name="T">The type of class to get</typeparam>
- /// <returns>The class instance</returns>
- public T Get<T>() where T : class
- {
- return Xamarin.Forms.DependencyService.Get<T>();
- }
- }
- }
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的关系的更多相关文章
- Xamarin+Prism开发详解七:Plugin开发与打包测试
有了上章[Xamarin+Prism开发详解六:DependencyService与IPlatformInitializer的关系]的基础,现在来理解Plugin开发就简单了. 本文实例代码地址:ht ...
- Xamarin+Prism开发详解四:简单Mac OS 虚拟机安装方法与Visual Studio for Mac 初体验
Mac OS 虚拟机安装方法 最近把自己的电脑升级了一下SSD固态硬盘,总算是有容量安装Mac 虚拟机了!经过心碎的安装探索,尝试了国内外的各种安装方法,最后在youtube上找到了一个好方法. 简单 ...
- Xamarin+Prism开发详解一:PCL跨平台类库与Profile的关系
在[Xamarin+Prism小试牛刀:定制跨平台Outlook邮箱应用]中提到过以下错误,不知道大伙还记得不: 无法安装程序包"Microsoft.Identity.Client 1.0. ...
- Xamarin+Prism开发详解三:Visual studio 2017 RC初体验
Visual studio 2017 RC出来一段时间了,最近有时间就想安装试试,随带分享一下安装使用体验. 1,卸载visual studio 2015 虽然可以同时安装visual studio ...
- Xamarin+Prism开发详解五:页面布局基础知识
说实在的研究Xamarin到现在,自己就没设计出一款好的UI,基本都在研究后台逻辑之类的!作为Xamarin爱好者,一些简单的页面布局知识还是必备的. 布局常见标签: StackLayout Abso ...
- Xamarin+Prism开发详解二:Xaml文件如何简单绑定Resources资源文件内容
我们知道在UWP里面有Resources文件xxx.resx,在Android里面有String.Xml文件等.那跨平台如何统一这些类别不一的资源文件以及Xaml设计文件如何绑定这些资源?应用支持多国 ...
- Xamarin+Prism开发详解八:自动化测试之NUnit实践
自动化测试很重要!很重要!以前多是手动测试,没有写过测试用例.这样的结果就是发现bug改了之后关联的其他功能又要从新测一遍.这样既浪费时间与成本,而且很无聊.之所以选择NUnit是公司需要,现在.ne ...
- 在【Xamarin+Prism开发详解三:Visual studio 2017 RC初体验】中分享了Visual studio 2017RC的大致情况,同时也发现大家对新的Visual Studio很是感兴趣。于是发时间深入研究了一下Visual Studio 2017RC 是不是和微软Connect()://2016上说得一样神。
总共列出了12点,耐心点慢慢看! 1,添加了不少[代码样式]的设置项目. 通过合理的设置每个人都能写出优美的代码,而且团队项目也可以达到统一代码风格. this首选项:可以设置[字段,属性,方法,事件 ...
- EasyPR--开发详解(6)SVM开发详解
在前面的几篇文章中,我们介绍了EasyPR中车牌定位模块的相关内容.本文开始分析车牌定位模块后续步骤的车牌判断模块.车牌判断模块是EasyPR中的基于机器学习模型的一个模块,这个模型就是作者前文中从机 ...
随机推荐
- MVVM设计模式和WPF中的实现(四)事件绑定
MVVM设计模式和在WPF中的实现(四) 事件绑定 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在WPF中 ...
- JavaScript中Math对象的方法介绍
1.比较最值方法 比较最值有两种方法,max() 和 min() 方法. 1.1 max() 方法,比较一组数值中的最大值,返回最大值. var maxnum = Math.max(12,6,43,5 ...
- PhotoView实现图片随手势的放大缩小的效果
项目需求:在listView的条目中如果有图片,点击条目,实现图片的放大,并且图片可以根据手势来控制图片放大缩小的比例.类似于微信朋友圈中查看好友发布的照片所实现的效果. 思路是这样的:当点击条目的时 ...
- 谈谈一些有趣的CSS题目(四)-- 从倒影说起,谈谈 CSS 继承 inherit
开本系列,讨论一些有趣的 CSS 题目,抛开实用性而言,一些题目为了拓宽一下解决问题的思路,此外,涉及一些容易忽视的 CSS 细节. 解题不考虑兼容性,题目天马行空,想到什么说什么,如果解题中有你感觉 ...
- Xamarin+Prism小试牛刀:定制跨平台Outlook邮箱应用
通过本文你将学会如下内容: 1,如何使用Xamarin开发跨平台(Windows,Android,iOS)应用. 2,如何使用微软的登录界面登入Microsoft账号. 3,如何使用Outlook邮箱 ...
- 前端HTML5几种存储方式的总结
接下来要好好总结一些知识,秋招来啦...虽然有好多知识都不大会,但是还是要努力一下,运气这种东西,谁知道呢~ 总体情况 h5之前,存储主要是用cookies.cookies缺点有在请求头上带着数据,大 ...
- 【Big Data】HADOOP集群的配置(一)
Hadoop集群的配置(一) 摘要: hadoop集群配置系列文档,是笔者在实验室真机环境实验后整理而得.以便随后工作所需,做以知识整理,另则与博客园朋友分享实验成果,因为笔者在学习初期,也遇到不少问 ...
- Java中,异常的处理及抛出
首先我们需要知道什么是异常? 常通常指,你的代码可能在编译时没有错误,可是运行时会出现异常.比如常见的空指针异常.也可能是程序可能出现无法预料的异常,比如你要从一个文件读信息,可这个文件不存在,程序无 ...
- BPM费控管理解决方案分享
一.方案概述费用是除经营成本外企业的最主要支出,费用管理是财务管理的核心之一,加强企业内控管理如:费用申请.费用报销.费用分摊.费用审批.费用控制和费用支付等,通过科学有效的管理方法规范企业费用管理, ...
- Android Studio-—使用OpenCV的配置方法和demo以及开发过程中遇到的问题解决
前提: 1.安装Android Studio(过程略) 2.官网下载OpenCV for Android 网址:http:opencv.org/downloads.html 我下载的是下图的版本 3. ...