祝各位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. ImageView缩放选项

    ImageView.ScaleType 将图片边界缩放到所在view边界时的缩放选项. Options for scaling the bounds of an image to the bounds ...

  2. SQL Server相关书籍

    SQL Server相关书籍 (排名不分先后) Microsoft SQL Server 企业级平台管理实践 SQL Server 2008数据库技术内幕 SQL Server性能调优实战 SQL S ...

  3. SQL Server 数据加密功能解析

    SQL Server 数据加密功能解析 转载自: 腾云阁 https://www.qcloud.com/community/article/194 数据加密是数据库被破解.物理介质被盗.备份被窃取的最 ...

  4. 隐私泄露杀手锏 —— Flash 权限反射

    [简版:http://weibo.com/p/1001603881940380956046] 前言 一直以为该风险早已被重视,但最近无意中发现,仍有不少网站存在该缺陷,其中不乏一些常用的邮箱.社交网站 ...

  5. Socket聊天程序——服务端

    写在前面: 昨天在博客记录自己抽空写的一个Socket聊天程序的初始设计,那是这个程序的整体设计,为了完整性,今天把服务端的设计细化记录一下,首页贴出Socket聊天程序的服务端大体设计图,如下图: ...

  6. myeclipse学习总结一(在MyEclipse中设置生成jsp页面时默认编码为utf-8编码)

    1.每次我们在MyEclispe中创建Jsp页面,生成的Jsp页面的默认编码是"ISO-8859-1".在这种情况下,当我们在页面中编写的内容存在中文的时候,就无法进行保存.如下图 ...

  7. Python学习--04条件控制与循环结构

    Python学习--04条件控制与循环结构 条件控制 在Python程序中,用if语句实现条件控制. 语法格式: if <条件判断1>: <执行1> elif <条件判断 ...

  8. 算法与数据结构(八) AOV网的关键路径

    上篇博客我们介绍了AOV网的拓扑序列,请参考<数据结构(七) AOV网的拓扑排序(Swift面向对象版)>.拓扑序列中包括项目的每个结点,沿着拓扑序列将项目进行下去是肯定可以将项目完成的, ...

  9. MongoDB系列(一):简介及安装

    什么是MongoDB MongoDB 是由C++语言编写的,是一个基于分布式文件存储的开源数据库系统. 在高负载的情况下,添加更多的节点,可以保证服务器性能. MongoDB 旨在为应用提供可扩展的高 ...

  10. Oracle SQL Developer 连接 MySQL

    1. 在ORACLE官网下载Oracle SQL Developer第三方数据库驱动 下载页面:http://www.oracle.com/technetwork/developer-tools/sq ...