zoukankan      html  css  js  c++  java
  • Xamarin+Prism开发详解七:Plugin开发与打包测试

    有了上章【Xamarin+Prism开发详解六:DependencyService与IPlatformInitializer的关系】的基础,现在来理解Plugin开发就简单了。

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

    简介

    Plugin其实就是各类相对独立的功能提取出来的Package,一般都不引用Xamarin相关类库,比如上章的Text To Speech功能(Github库地址https://github.com/jamesmontemagno/TextToSpeechPlugin):

    image

    xamarin的Plugin有一个专门的统计库【xamarin/XamarinComponents】,目前统计结果如下:

    NameDescriptionNuGetDocs & SourceCreator
    Battery Status Gather battery level, charging status, and type. NuGet GitHub @JamesMontemagno
    Barcode Scanner Scan and create barcodes with ZXing.NET.Mobile. NuGet GitHub @Redth
    Bluetooth LE Scan and connect to Bluetooth devices. NuGet GitHub @allanritchie911
    Calendar Query and modify device calendars NuGet GitHub Caleb Clarke
    Compass Access device compass heading. NuGet GitHub @cbartonnh & @JamesMontemagno
    Connectivity Get network connectivity info such as type and if connection is available. NuGet GitHub @JamesMontemagno
    Cryptography PCL Crypto provides a consistent, portable set of crypto APIs. NuGet GitHub @aarnott
    Device Info Properties about device such as OS, Model, and Id. NuGet GitHub @JamesMontemagno
    Device Motion Provides access to Accelerometer, Gyroscope, Magnetometer, and Compass. NuGet GitHub @rdelrosario
    Embedded Resource Unpack embedded resource cross-platform. NuGet GitHub @JosephHill
    External Maps Launch external maps from Lat/Long or Address. NuGet GitHub @JamesMontemagno
    File Storage/File System PCL Storage offers cross platform storage APIs. NuGet GitHub @dsplaisted
    File Picker Pick and save files. NuGet GitHub @studyxnet
    Fingerprint Access Fingerprint sensor on iOS, Android, and Windows. NuGet GitHub @smstuebe
    FFImageLoading Image loading with caching, placeholders, transformations and more NuGet GitHub @molinch, @daniel-luberda
    Geofencing Monitor regions when user enters/exits. NuGet GitHub @allanritchie911
    Geolocator Easily detect GPS location of device. NuGet GitHub @JamesMontemagno
    iBeacon & Estimote Range and monitor Bluetooth beacons. NuGet GitHub @allanritchie911
    Lamp Access to LED NuGet GitHub @kphillpotts
    Local Notifications Show local notifications NuGet GitHub @EdSnider, @JamesMontemagno
    Manage Sleep Manage auto sleep/auto lock. NuGet GitHub @molinch0
    Media Take or pick photos and videos. NuGet GitHub @JamesMontemagno
    Media Manager Playback for Audio. NuGet GitHub @mhvdijk
    Messaging Make phone call, send sms, and send e-mail NuGet GitHub @cjlotz
    Microsoft Band Connect and communicate with the Microsoft Band from shared code! NuGet GitHub @mattleibow
    Mono.Data.Sqlite Add Mono.Data.Sqlite to any Xamarin or Windows .NET app. NuGet GitHub @mattleibow
    Permissions Easily check and request runtime permissions. NuGet GitHub @JamesMontemagno
    Persistent key-value store Akavache is an asynchronous, persistent (i.e. writes to disk) key-value store. NuGet GitHub @paulcbetts
    Portable Razor Lightweight implemenation of ASP.NET MVC APIs for mobile. NuGet GitHub @JosephHill
    Push Notifications Cross platform iOS and Android Push Notifications. NuGet GitHub @rdelrosario
    Secure Storage Provides secure storage for key value pairs Data NuGet GitHub @sameerIOTApps
    Settings Simple & Consistent cross platform settings API. NuGet GitHub @JamesMontemagno
    Share Easily share text, links, or open a browser. NuGet GitHub @JamesMontemagno & @Jakob Gürtl
    Sockets TCP & UDP Listeners and Clients + UDP multicast. NuGet GitHub @rdavis_au
    Speech Recognition Speech to Text. NuGet GitHub @allanritchie911
    Text To Speech Talk back text from shared code. NuGet GitHub @JamesMontemagno
    Toast A simple way of showing toast/pop-up notifications. NuGet GitHub @AdamPed & @EgorBo
    User Dialogs Message box style dialogs. NuGet GitHub @allanritchie911
    Version Tracking Track which versions of your app a user has previously installed. NuGet GitHub @ColbyLWilliams
    Vibrate Vibrate any device. NuGet GitHub @JamesMontemagno
     

    开发实践

    Xamarin为Plugin开发提供了一个Visual Studio模板,通过它你可以快速开发各类Plugin。

    1、安装Plugin for Xamarin Templates

    image

    2、从Plugin for Xamarin模板新建项目

    image

    整体项目结构:

    image

    通过模板创建的项目包含三类工程文件:

    • Plugin.功能名(PCL):懒汉式实例创建文件,生成Plugin.功能名.dll。(共享类文件)
    • Plugin.功能名.Abstractions(PCL):接口和Enums的定义,生成Plugin.功能名.Abstractions.dll。
    • Plugin.功能名.平台名(n个):接口实现,生成Plugin.功能名.dll。

    image

    3、添加接口

    namespace Plugin.Speecher.Abstractions
    {
        /// <summary>
        /// Interface for Speecher
        /// </summary>
        public interface ISpeecher
        {
            void Speak(string text);
        }
    }

    4、各个平台接口实现

    iOS平台

    using AVFoundation;
    using Plugin.Speecher.Abstractions;
    
    
    namespace Plugin.Speecher
    {
        /// <summary>
        /// Implementation for Speecher
        /// </summary>
        public class SpeecherImplementation : ISpeecher
        {
            public void Speak(string text)
            {
                var speechSynthesizer = new AVSpeechSynthesizer();
    
                var speechUtterance = new AVSpeechUtterance(text)
                {
                    Rate = AVSpeechUtterance.MaximumSpeechRate / 4,
                    Voice = AVSpeechSynthesisVoice.FromLanguage("en-US"),
                    Volume = 0.5f,
                    PitchMultiplier = 1.0f
                };
    
                speechSynthesizer.SpeakUtterance(speechUtterance);
            }
        }
    }

    Android平台(由于没有Xamarin.Forms,所以这里使用Application.Context)

    using Android.App;
    using Android.Runtime;
    using Android.Speech.Tts;
    using Plugin.Speecher.Abstractions;
    using System.Collections.Generic;
    
    namespace Plugin.Speecher
    {
        /// <summary>
        /// Implementation for Feature
        /// </summary>
        public class SpeecherImplementation : Java.Lang.Object, ISpeecher, TextToSpeech.IOnInitListener
        {
            TextToSpeech speaker;
            string toSpeak;
            public SpeecherImplementation() { }
    
            public void Speak(string text)
            {
                var ctx = Application.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))
                {
                    var p = new Dictionary<string, string>();
                    speaker.Speak(toSpeak, QueueMode.Flush, p);
                }
                else
                {
                    System.Diagnostics.Debug.WriteLine("was quiet");
                }
            }
        }
    }

    UWP平台

    using Plugin.Speecher.Abstractions;
    using System;
    using Windows.Media.SpeechSynthesis;
    using Windows.UI.Xaml.Controls;
    
    namespace Plugin.Speecher
    {
        /// <summary>
        /// Implementation for Feature
        /// </summary>
        public class SpeecherImplementation : ISpeecher
        {
            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();
            }
        }
    }

    备注

    如果确定一直使用Prism做开发的话,只需要定义接口与接口的实现就可以了。然后在实际项目各个平台的RegisterTypes方法中注册接口到IOC容器,

    public void RegisterTypes(IUnityContainer container)
    {
        container.RegisterType<ISpeecher, SpeecherImplementation>();
    }

    使用IOC容器管理对象生存周期,比懒汉式加载更加自由方便。比如在Prism.Forms中只需构造函数添加接口作为参数就可以自动创建对象。

    打包并测试

    在安装Plugin for Xamarin Templates的时候其实已经安装了另外一个打包用的模板【Plugin for Xamarin NuSpec】,通过它可以快速打包。

    1、添加Package配置文件【Speecher.Plugin.nuspec】

    image

    默认生成的文件如下,包含所有平台:

    <?xml version="1.0" encoding="utf-8"?>
    <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
        <metadata minClientVersion="2.8.3">
            <id>Plugin.Speecher</id>
            <version>1.0.0</version>
            <title>Speecher Plugin for Xamarin and Windows</title>
            <authors>Your Name</authors>
            <owners>Your Name</owners>
            <licenseUrl/>
            <projectUrl/>
            <!--Default Icon, a template can be found:  https://raw.githubusercontent.com/jamesmontemagno/Xamarin-Templates/master/Plugins-Templates/icons/plugin_icon.png-->  
            <iconUrl>https://raw.githubusercontent.com/jamesmontemagno/Xamarin-Templates/master/Plugins-Templates/icons/plugin_icon_nuget.png</iconUrl>
    
            <requireLicenseAcceptance>false</requireLicenseAcceptance>
            <description>
             Long description for your plugin.
            </description>
            <summary>Short description for your plugin.</summary>
            <tags>xamarin, pcl, xam.pcl, plugin, plugin for xamarin, windows phone, winphone, wp8, winrt, android, xamarin.forms, ios</tags>
            <dependencies>
                <group targetFramework="net">
                </group>
                <group targetFramework="win">
                </group>
                <group targetFramework="wp">
                </group>
                <group targetFramework="wpa">
                </group>
                <group targetFramework="MonoAndroid">
                </group>
                <group targetFramework="Xamarin.iOS10">
                </group>
                <group targetFramework="Xamarin.Mac20">
                </group>
                <group targetFramework="portable-net45+win+wpa81+wp80">
                </group>
                <group targetFramework="uap">
                </group>
                <group targetFramework="dotnet">
                </group>
            </dependencies>
        </metadata>
        <files>
            <!--Core-->
            <file src="SpeecherPlugin.SpeecherinReleasePlugin.Speecher.dll" target="libportable-net45+wp8+wpa81+win8+MonoAndroid10+MonoTouch10+Xamarin.iOS10+UAP10Plugin.Speecher.dll" />
            <file src="SpeecherPlugin.SpeecherinReleasePlugin.Speecher.xml" target="libportable-net45+wp8+wpa81+win8+MonoAndroid10+MonoTouch10+Xamarin.iOS10+UAP10Plugin.Speecher.xml" />
            <file src="SpeecherPlugin.SpeecherinReleasePlugin.Speecher.pdb" target="libportable-net45+wp8+wpa81+win8+MonoAndroid10+MonoTouch10+Xamarin.iOS10+UAP10Plugin.Speecher.pdb" />
            <file src="SpeecherPlugin.Speecher.AbstractionsinReleasePlugin.Speecher.Abstractions.dll" target="libportable-net45+wp8+wpa81+win8+MonoAndroid10+MonoTouch10+Xamarin.iOS10+UAP10Plugin.Speecher.Abstractions.dll" />
            <file src="SpeecherPlugin.Speecher.AbstractionsinReleasePlugin.Speecher.Abstractions.xml" target="libportable-net45+wp8+wpa81+win8+MonoAndroid10+MonoTouch10+Xamarin.iOS10+UAP10Plugin.Speecher.Abstractions.xml" />
            <file src="SpeecherPlugin.Speecher.AbstractionsinReleasePlugin.Speecher.Abstractions.pdb" target="libportable-net45+wp8+wpa81+win8+MonoAndroid10+MonoTouch10+Xamarin.iOS10+UAP10Plugin.Speecher.Abstractions.pdb" />        
            
            <!--dotnet-->
            <file src="SpeecherPlugin.SpeecherinReleasePlugin.Speecher.dll" target="libdotnetPlugin.Speecher.dll" />
            <file src="SpeecherPlugin.SpeecherinReleasePlugin.Speecher.xml" target="libdotnetPlugin.Speecher.xml" />
            <file src="SpeecherPlugin.SpeecherinReleasePlugin.Speecher.pdb" target="libdotnetPlugin.Speecher.pdb" />
            <file src="SpeecherPlugin.Speecher.AbstractionsinReleasePlugin.Speecher.Abstractions.dll" target="libdotnetPlugin.Speecher.Abstractions.dll" />
            <file src="SpeecherPlugin.Speecher.AbstractionsinReleasePlugin.Speecher.Abstractions.xml" target="libdotnetPlugin.Speecher.Abstractions.xml" />
            <file src="SpeecherPlugin.Speecher.AbstractionsinReleasePlugin.Speecher.Abstractions.pdb" target="libdotnetPlugin.Speecher.Abstractions.pdb" />
            
            <!--Win Phone Silverlight-->
            <file src="SpeecherPlugin.Speecher.WindowsPhone8inReleasePlugin.Speecher.dll" target="libwp8Plugin.Speecher.dll" />
            <file src="SpeecherPlugin.Speecher.WindowsPhone8inReleasePlugin.Speecher.xml" target="libwp8Plugin.Speecher.xml" />
            <file src="SpeecherPlugin.Speecher.WindowsPhone8inReleasePlugin.Speecher.pdb" target="libwp8Plugin.Speecher.pdb" />
            <file src="SpeecherPlugin.Speecher.AbstractionsinReleasePlugin.Speecher.Abstractions.dll" target="libwp8Plugin.Speecher.Abstractions.dll" />
            <file src="SpeecherPlugin.Speecher.AbstractionsinReleasePlugin.Speecher.Abstractions.xml" target="libwp8Plugin.Speecher.Abstractions.xml" />
            <file src="SpeecherPlugin.Speecher.AbstractionsinReleasePlugin.Speecher.Abstractions.pdb" target="libwp8Plugin.Speecher.Abstractions.pdb" />
            
            <!--Win Phone 81-->
            <file src="SpeecherPlugin.Speecher.WindowsPhone81inReleasePlugin.Speecher.dll" target="libwpa81Plugin.Speecher.dll" />
            <file src="SpeecherPlugin.Speecher.WindowsPhone81inReleasePlugin.Speecher.xml" target="libwpa81Plugin.Speecher.xml" />
            <file src="SpeecherPlugin.Speecher.WindowsPhone81inReleasePlugin.Speecher.pdb" target="libwpa81Plugin.Speecher.pdb" />
            <file src="SpeecherPlugin.Speecher.AbstractionsinReleasePlugin.Speecher.Abstractions.dll" target="libwpa81Plugin.Speecher.Abstractions.dll" />
            <file src="SpeecherPlugin.Speecher.AbstractionsinReleasePlugin.Speecher.Abstractions.xml" target="libwpa81Plugin.Speecher.Abstractions.xml" />
            <file src="SpeecherPlugin.Speecher.AbstractionsinReleasePlugin.Speecher.Abstractions.pdb" target="libwpa81Plugin.Speecher.Abstractions.pdb" />
            
            <!--WinStore-->
            <file src="SpeecherPlugin.Speecher.WindowsStoreinReleasePlugin.Speecher.dll" target="libwin8Plugin.Speecher.dll" />
            <file src="SpeecherPlugin.Speecher.WindowsStoreinReleasePlugin.Speecher.xml" target="libwin8Plugin.Speecher.xml" />
            <file src="SpeecherPlugin.Speecher.WindowsStoreinReleasePlugin.Speecher.pdb" target="libwin8Plugin.Speecher.pdb" />
            <file src="SpeecherPlugin.Speecher.AbstractionsinReleasePlugin.Speecher.Abstractions.dll" target="libwin8Plugin.Speecher.Abstractions.dll" />
            <file src="SpeecherPlugin.Speecher.AbstractionsinReleasePlugin.Speecher.Abstractions.xml" target="libwin8Plugin.Speecher.Abstractions.xml" />
            <file src="SpeecherPlugin.Speecher.AbstractionsinReleasePlugin.Speecher.Abstractions.pdb" target="libwin8Plugin.Speecher.Abstractions.pdb" />
    
            <!--Xamarin.Android-->
            <file src="SpeecherPlugin.Speecher.AndroidinReleasePlugin.Speecher.dll" target="libMonoAndroid10Plugin.Speecher.dll" />
            <file src="SpeecherPlugin.Speecher.AndroidinReleasePlugin.Speecher.xml" target="libMonoAndroid10Plugin.Speecher.xml" />
            <file src="SpeecherPlugin.Speecher.AndroidinReleasePlugin.Speecher.pdb" target="libMonoAndroid10Plugin.Speecher.pdb" />
            <file src="SpeecherPlugin.Speecher.AbstractionsinReleasePlugin.Speecher.Abstractions.dll" target="libMonoAndroid10Plugin.Speecher.Abstractions.dll" />
            <file src="SpeecherPlugin.Speecher.AbstractionsinReleasePlugin.Speecher.Abstractions.xml" target="libMonoAndroid10Plugin.Speecher.Abstractions.xml" />
            <file src="SpeecherPlugin.Speecher.AbstractionsinReleasePlugin.Speecher.Abstractions.pdb" target="libMonoAndroid10Plugin.Speecher.Abstractions.pdb" />
    
            <!--Xamarin.iOS-->
            <file src="SpeecherPlugin.Speecher.iOSiniPhoneReleasePlugin.Speecher.dll" target="libXamarin.iOS10Plugin.Speecher.dll" />
            <file src="SpeecherPlugin.Speecher.iOSiniPhoneReleasePlugin.Speecher.xml" target="libXamarin.iOS10Plugin.Speecher.xml" />
            <file src="SpeecherPlugin.Speecher.AbstractionsinReleasePlugin.Speecher.Abstractions.dll" target="libXamarin.iOS10Plugin.Speecher.Abstractions.dll" />
            <file src="SpeecherPlugin.Speecher.AbstractionsinReleasePlugin.Speecher.Abstractions.xml" target="libXamarin.iOS10Plugin.Speecher.Abstractions.xml" />
    
            <!--uap-->
            <file src="SpeecherPlugin.Speecher.UWPinReleasePlugin.Speecher.dll" target="libUAP10Plugin.Speecher.dll" />
            <file src="SpeecherPlugin.Speecher.UWPinReleasePlugin.Speecher.xml" target="libUAP10Plugin.Speecher.xml" />
            <file src="SpeecherPlugin.Speecher.UWPinReleasePlugin.Speecher.pdb" target="libUAP10Plugin.Speecher.pdb" />
            <file src="SpeecherPlugin.Speecher.AbstractionsinReleasePlugin.Speecher.Abstractions.dll" target="libUAP10Plugin.Speecher.Abstractions.dll" />
            <file src="SpeecherPlugin.Speecher.AbstractionsinReleasePlugin.Speecher.Abstractions.xml" target="libUAP10Plugin.Speecher.Abstractions.xml" />
            <file src="SpeecherPlugin.Speecher.AbstractionsinReleasePlugin.Speecher.Abstractions.pdb" target="libUAP10Plugin.Speecher.Abstractions.pdb" />
            
            <!--Xamarin.Mac
            <file src="SpeecherPlugin.Speecher.MaciniPhoneReleasePlugin.Speecher.dll" target="libXamarin.Mac20Plugin.Speecher.dll" />
            <file src="SpeecherPlugin.Speecher.MaciniPhoneReleasePlugin.Speecher.xml" target="libXamarin.Mac20Plugin.Speecher.xml" />
            <file src="SpeecherPlugin.Speecher.AbstractionsinReleasePlugin.Speecher.Abstractions.dll" target="libXamarin.Mac20Plugin.Speecher.Abstractions.dll" />
            <file src="SpeecherPlugin.Speecher.AbstractionsinReleasePlugin.Speecher.Abstractions.xml" target="libXamarin.Mac20Plugin.Speecher.Abstractions.xml" />
            -->
        </files>
    </package>
    • 由于只正对iOS,Android,UWP三个平台,其他的配置都删除掉
    • 固定文件名用*代替

    修改后的文件如下:

    <?xml version="1.0" encoding="utf-8"?>
    <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
      <metadata minClientVersion="2.8.3">
        <id>Plugin.Speecher</id>
        <version>1.0.0</version>
        <title>Speecher Plugin for Xamarin and Windows</title>
        <authors>NewBlifes</authors>
        <owners>NewBlifes</owners>
        <iconUrl>https://github.com/NewBLife/NewBlife.Core/blob/master/logo.png</iconUrl>
        <requireLicenseAcceptance>false</requireLicenseAcceptance>
        <description>
          Text to speech plugin.
        </description>
        <summary>Text to speech plugin.</summary>
        <tags>xamarin, pcl, xam.pcl, plugin, plugin for xamarin, windows phone, UWP, android, xamarin.forms, ios</tags>
        <dependencies>
          <group targetFramework="MonoAndroid">
          </group>
          <group targetFramework="Xamarin.iOS10">
          </group>
          <group targetFramework="portable-net45+win+wpa81+wp80">
          </group>
          <group targetFramework="uap">
          </group>
        </dependencies>
      </metadata>
      <files>
        <!--Core-->
        <file src="SpeecherPlugin.SpeecherinReleasePlugin.Speecher.*" target="libportable-net45+wp8+wpa81+win8+MonoAndroid10+MonoTouch10+Xamarin.iOS10+UAP10" />
    
        <!--Xamarin.Android-->
        <file src="SpeecherPlugin.Speecher.AndroidinReleasePlugin.Speecher.*" target="libMonoAndroid10" />
    
        <!--Xamarin.iOS-->
        <file src="SpeecherPlugin.Speecher.iOSiniPhoneReleasePlugin.Speecher.*" target="libXamarin.iOS10" />
    
        <!--uap-->
        <file src="SpeecherPlugin.Speecher.UWPinReleasePlugin.Speecher.*" target="libUAP10" />
    
      </files>
    </package>

    image

    2、编译解决方案的Release版本,使用【nuget pack】命令打包

    image

    image

    如果没有Nuget的命令行工具记得安装。

    3、测试

    添加本地包路径

    image

    创建测试项目,添加Package引用

    image

    测试代码

    using Plugin.Speecher;
    using Prism.Commands;
    using Prism.Mvvm;
    
    namespace SpeecherTest.ViewModels
    {
        public class MainPageViewModel : BindableBase
        {
            private string _speakText;
            public string SpeakText
            {
                get { return _speakText; }
                set
                {
                    SetProperty(ref _speakText, value);
                }
            }
    
            public MainPageViewModel()
            {
            }
    
            public DelegateCommand SpeakCommand => new DelegateCommand(
                () =>
                {
                    CrossSpeecher.Current.Speak(SpeakText);
                },
                () => !string.IsNullOrEmpty(SpeakText)).ObservesProperty(() => this.SpeakText);
        }
    }

    pluginTextToSpeech1

    总结

    通过使用Plugin For xamarin模块来开发Plugin简单又快速,你只需要关注真正的逻辑就可以。

  • 相关阅读:
    IO模型
    协程
    线程
    进程
    网络编程
    模块二
    面向对象(二)
    面向对象(一)
    优化异常报错
    python 模块
  • 原文地址:https://www.cnblogs.com/lixiaobin/p/XamarinPlugin.html
Copyright © 2011-2022 走看看