依赖服务和可移植类库
PlatInfoSap2程序中说明的技术是否可以在具有可移植类库的解决方案中实现? 起初,它似乎不可能。 虽然应用程序项目一直调用库,但是除了事件或回调函数的上下文之外,库通常不能调用应用程序。 PCL与独立于设备的.NET版本捆绑在一起,只能在其自身或其他可能引用的PCL中执行代码。
但是等待:当Xamarin.Forms应用程序运行时,它可以使用.NET反射来访问自己的程序集和程序中的任何其他程序集。这意味着PCL中的代码可以使用反射来访问存在于引用PCL的平台程序集中的类。当然,这些类必须定义为公共类,但这只是唯一的要求。
在开始编写利用此技术的代码之前,您应该知道此解决方案已经以名为Dependency Service的Xamarin.Forms类的形式存在。此类使用.NET反射来搜索应用程序中的所有其他程序集(包括特定平台程序集本身),并提供对特定于平台的代码的访问。
DisplayPlatformInfo解决方案中说明了DependencyService的使用,该解决方案使用可移植类库作为共享代码。您可以通过在PCL项目中定义接口类型来开始使用DependencyService的过程,该接口类型声明要在平台项目中实现的方法的签名。这是IPlatformInfo:
namespace DisplayPlatformInfo
{
public interface IPlatformInfo
{
string GetModel();
string GetVersion();
}
}
你以前见过这两种方法。 它们与PlatInfoSap2中平台项目中的PlatformInfo类中实现的方法相同。
以与PlatInfoSap2非常相似的方式,DisplayPlatformInfo中的所有三个平台项目现在必须具有实现IPlatformInfo接口的类。 这是iOS项目中的类,名为PlatformInfo:
using System;
using UIKit;
using Xamarin.Forms;
[assembly: Dependency(typeof(DisplayPlatformInfo.iOS.PlatformInfo))]
namespace DisplayPlatformInfo.iOS
{
public class PlatformInfo : IPlatformInfo
{
UIDevice device = new UIDevice();
public string GetModel()
{
return device.Model.ToString();
}
public string GetVersion()
{
return String.Format("{0} {1}", device.SystemName,
device.SystemVersion);
}
}
}
此类不是直接从PCL引用的,因此命名空间名称可以是您想要的任何名称。 在这里,它设置为与iOS项目中的其他代码相同的命名空间。 班级名称也可以是您想要的任何名称。 无论您如何命名,该类必须显式实现PCL中定义的IPlatformInfo接口:
public class PlatformInfo : IPlatformInfo
此外,必须在命名空间块之外的特殊属性中引用此类。 您将在using指令后面的文件顶部附近看到它:
[assembly: Dependency(typeof(DisplayPlatformInfo.iOS.PlatformInfo))]
定义此Dependency属性的DependencyAttribute类是Xamarin.Forms的一部分,专门用于与DependencyService连接。 参数是平台项目中类的Type对象,可供PCL访问。 在这种情况下,它就是这个PlatformInfo类。 此属性附加到平台程序集本身,因此在PCL中执行的代码不必搜索整个库以查找它。
这是PlatformInfo的Android版本:
using System;
using Android.OS;
using Xamarin.Forms;
[assembly: Dependency(typeof(DisplayPlatformInfo.Droid.PlatformInfo))]
namespace DisplayPlatformInfo.Droid
{
public class PlatformInfo : IPlatformInfo
{
public string GetModel()
{
return String.Format("{0} {1}", Build.Manufacturer,
Build.Model);
}
public string GetVersion()
{
return Build.VERSION.Release.ToString();
}
}
}
这是UWP项目的一个:
using System;
using Windows.Security.ExchangeActiveSyncProvisioning;
using Xamarin.Forms;
[assembly: Dependency(typeof(DisplayPlatformInfo.UWP.PlatformInfo))]
namespace DisplayPlatformInfo.UWP
{
public class PlatformInfo : IPlatformInfo
{
EasClientDeviceInformation devInfo = new EasClientDeviceInformation();
public string GetModel()
{
return String.Format("{0} {1}", devInfo.SystemManufacturer,
devInfo.SystemProductName);
}
public string GetVersion()
{
return devInfo.OperatingSystem;
}
}
}
Windows 8.1和Windows Phone 8.1项目具有类似的文件,这些文件仅由命名空间不同。
然后,PCL中的代码可以使用DependencyService类访问特定平台的IPlatformInfo实现。 这是一个带有三个公共方法的静态类,其中最重要的是名为Get。 Get是一个泛型方法,其参数是您定义的接口,在本例中为IPlatformInfo。
IPlatformInfo platformInfo = DependencyService.Get<IPlatformInfo>();
Get方法返回实现IPlatformInfo接口的特定于平台的类的实例。 然后,您可以使用此对象进行特定于平台的调用。 这在DisplayPlatformInfo项目的代码隐藏文件中得到了证明:
namespace DisplayPlatformInfo
{
public partial class DisplayPlatformInfoPage : ContentPage
{
public DisplayPlatformInfoPage()
{
InitializeComponent();
IPlatformInfo platformInfo = DependencyService.Get<IPlatformInfo>();
modelLabel.Text = platformInfo.GetModel();
versionLabel.Text = platformInfo.GetVersion();
}
}
}
Dependency Service缓存通过Get方法获取的对象的实例。 这加速了Get的后续使用,并且还允许接口的平台实现维护状态:平台实现中的任何字段和属性将在多个Get调用中保留。 这些类还可以包含事件或实现回调方法。
与PlatInfoSap2项目中显示的方法相比,依赖服务只需要更多的开销,并且由于各个平台类实现了在共享代码中定义的接口,因此更加结构化。
DependencyService不是在PCL中实现特定于平台的调用的唯一方法。 Adventurous开发人员可能希望使用依赖注入技术来配置PCL以调用平台项目。 但是DependencyService非常易于使用,它消除了在Xamarin.Forms应用程序中使用共享资产项目的大多数原因。