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中的基于机器学习模型的一个模块,这个模型就是作者前文中从机 ...
随机推荐
- java: web应用中不经意的内存泄露
前面有一篇讲解如何在spring mvc web应用中一启动就执行某些逻辑,今天无意发现如果使用不当,很容易引起内存泄露,测试代码如下: 1.定义一个类App package com.cnblogs. ...
- 转:serialVersionUID作用
汗,以前学了还忘了... Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的.在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本 ...
- 解读发布:.NET Core RC2 and .NET Core SDK Preview 1
先看一下 .NET Core(包含 ASP.NET Core)的路线图: Beta6: 2015年7月27日 Beta7: 2015年9月2日 Beta8: 2015年10月15日 RC1: 2015 ...
- mysql进阶之存储过程
往往看别人的代码会有这样的感慨: 看不懂 理还乱 是离愁 别是一番滋味在心头 为什么要使用存储过程? 在mysql开发中使用存储过程的理由: 当希望在不同的应用程序或平台上执行相同的函数,或者封装特定 ...
- Web安全相关(五):SQL注入(SQL Injection)
简介 SQL注入攻击指的是通过构建特殊的输入作为参数传入Web应用程序,而这些输入大都是SQL语法里的一些组合,通过执行SQL语句进而执行攻击者所要的操作,其主要原因是程序没有细致地过滤用户输入的数据 ...
- C++ 11 多线程--线程管理
说到多线程编程,那么就不得不提并行和并发,多线程是实现并发(并行)的一种手段.并行是指两个或多个独立的操作同时进行.注意这里是同时进行,区别于并发,在一个时间段内执行多个操作.在单核时代,多个线程是并 ...
- 对百度WebUploader开源上传控件的二次封装,精简前端代码(两句代码搞定上传)
前言 首先声明一下,我这个是对WebUploader开源上传控件的二次封装,底层还是WebUploader实现的,只是为了更简洁的使用他而已. 下面先介绍一下WebUploader 简介: WebUp ...
- Python 正则表达式入门(中级篇)
Python 正则表达式入门(中级篇) 初级篇链接:http://www.cnblogs.com/chuxiuhong/p/5885073.html 上一篇我们说在这一篇里,我们会介绍子表达式,向前向 ...
- interpreter(解释器模式)
一.引子 其实没有什么好的例子引入解释器模式,因为它描述了如何构成一个简单的语言解释器,主要应用在使用面向对象语言开发编译器中:在实际应用中,我们可能很少碰到去构造一个语言的文法的情况. 虽然你几乎用 ...
- [转载]网站地址栏小图标favicon.ico的制作方法
有人也许会好奇,有的网址前面有个漂亮的小图标而且有的网站图标还会动,这是怎么做到的呢? 如下图所示: 那个小图标有个名字叫favicon.ico,网站图标虽小但可以起到很好的点缀作用,尤其是当浏览者将 ...