祝各位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>的构造函数中:

IPlatformInitializer<T> _platformInitializer = null;

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的关系的更多相关文章

  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. 谈谈一些有趣的CSS题目(十一)-- reset.css 知多少?

    开本系列,谈谈一些有趣的 CSS 题目,题目类型天马行空,想到什么说什么,不仅为了拓宽一下解决问题的思路,更涉及一些容易忽视的 CSS 细节. 解题不考虑兼容性,题目天马行空,想到什么说什么,如果解题 ...

  2. 如何一步一步用DDD设计一个电商网站(一)—— 先理解核心概念

    一.前言     DDD(领域驱动设计)的一些介绍网上资料很多,这里就不继续描述了.自己使用领域驱动设计摸滚打爬也有2年多的时间,出于对知识的总结和分享,也是对自我理解的一个公开检验,介于博客园这个平 ...

  3. 在Linux虚拟机下配置tomcat

    1.到Apache官网下载tomcat http://tomcat.apache.org/download-80.cgi 博主我下载的是tomcat8 博主的jdk是1.8 如果你们的jdk是1.7或 ...

  4. HTML5 sessionStorage会话存储

    sessionStorage 是HTML5新增的一个会话存储对象,用于临时保存同一窗口(或标签页)的数据,在关闭窗口或标签页之后将会删除这些数据.本篇主要介绍 sessionStorage(会话存储) ...

  5. ExtJS 4.2 介绍

    本篇介绍ExtJS相关知识,是以ExtJS4.2.1版本为基础进行说明,包括:ExtJS的特点.MVC模式.4.2.1GPL版本资源的下载和说明以及4种主题的演示. 目录 1. 介绍 1.1 说明 1 ...

  6. SQL Server-聚焦UNIOL ALL/UNION查询(二十三)

    前言 本节我们来看看有关查询中UNION和UNION ALL的问题,简短的内容,深入的理解,Always to review the basics. 初探UNION和UNION ALL 首先我们过一遍 ...

  7. 多线程条件通行工具——AbstractQueuedSynchronizer

    本文原创,转载请注明出处! 参考文章: <"JUC锁"03之 公平锁(一)> <"JUC锁"03之 公平锁(二)> AbstractOw ...

  8. 界面设计技法之css布局

    css布局之于页面就如同ECMAScript之于JS一般,细想一番,html就如同语文,css就如同数学,js呢,就是物理,有些扯远,这里就先不展开了. 回到主题,从最开始的css到如今的sass(l ...

  9. 走进缓存的世界(三) - Memcache

    系列文章 走进缓存的世界(一) - 开篇 走进缓存的世界(二) - 缓存设计 走进缓存的世界(三) - Memcache 简介 Memcache是一个高性能的分布式内存对象缓存系统,用于动态Web应用 ...

  10. Mysql - 性能优化之子查询

    记得在做项目的时候, 听到过一句话, 尽量不要使用子查询, 那么这一篇就来看一下, 这句话是否是正确的. 那在这之前, 需要介绍一些概念性东西和mysql对语句的大致处理. 当Mysql Server ...