Resolución de dependencias Xamarin.Forms
En este artículo se explica cómo insertar un método de resolución de dependencias en para que el contenedor de inserción de dependencias de una aplicación tenga control sobre la creación y duración de representadores personalizados, efectos e implementaciones Xamarin.Forms de DependencyService. Los ejemplos de código de este artículo se toman del ejemplo De resolución de dependencias mediante contenedores.
En el contexto de una aplicación que usa el patrón Xamarin.Forms Model-View-ViewModel (MVVM), se puede usar un contenedor de inserción de dependencias para registrar y resolver modelos de vista, y para registrar servicios e insertarlos en modelos de vista. Durante la creación del modelo de vista, el contenedor inserta las dependencias necesarias. Si esas dependencias no se han creado, el contenedor crea y resuelve primero las dependencias. Para obtener más información sobre la inserción de dependencias, incluidos ejemplos de inserción de dependencias en los modelos de vista, vea Inserción de dependencias.
El control sobre la creación y duración de los tipos en proyectos de plataforma lo realiza tradicionalmente , que usa el método para crear instancias de representadores Xamarin.FormsActivator.CreateInstance personalizados, efectos e DependencyService implementaciones. Desafortunadamente, esto limita el control del desarrollador sobre la creación y duración de estos tipos, así como la capacidad de insertar dependencias en ellos. Este comportamiento se puede cambiar insertando un método de resolución de dependencias en que controla cómo se crearán los tipos, ya sea mediante el contenedor de inserción de dependencias de la aplicación Xamarin.Forms o mediante Xamarin.Forms . Sin embargo, tenga en cuenta que no hay ningún requisito para insertar un método de resolución de dependencias en Xamarin.Forms . Xamarin.Forms continuará con la creación y administración de la duración de los tipos en los proyectos de plataforma si no se inserta un método de resolución de dependencias.
Nota:
Aunque este artículo se centra en insertar un método de resolución de dependencias en que resuelva los tipos registrados mediante un contenedor de inserción de dependencias, también es posible insertar un método de resolución de dependencias que use métodos de generador para resolver los tipos Xamarin.Forms registrados. Para obtener más información, consulte el ejemplo Dependency Resolution using Factory Methods (Resolución de dependencias mediante métodos de fábrica).
Inserción de un método de resolución de dependencias
La DependencyResolver clase proporciona la capacidad de insertar un método de resolución de dependencias en Xamarin.Forms , mediante el método ResolveUsing . A continuación, cuando necesita una instancia de un tipo determinado, el método de resolución de dependencias tiene Xamarin.Forms la oportunidad de proporcionar la instancia. Si el método de resolución de dependencias devuelve para un tipo solicitado, vuelve a intentar crear la propia instancia null de tipo mediante el método Xamarin.FormsActivator.CreateInstance .
En el ejemplo siguiente se muestra cómo establecer el método de resolución de dependencias con el ResolveUsing método :
using Autofac;
using Xamarin.Forms.Internals;
...
public partial class App : Application
{
// IContainer and ContainerBuilder are provided by Autofac
static IContainer container;
static readonly ContainerBuilder builder = new ContainerBuilder();
public App()
{
...
DependencyResolver.ResolveUsing(type => container.IsRegistered(type) ? container.Resolve(type) : null);
...
}
...
}
En este ejemplo, el método de resolución de dependencias se establece en una expresión lambda que usa el contenedor de inserción de dependencias Autofac para resolver los tipos que se han registrado con el contenedor. De lo null contrario, se devolverá , lo que dará lugar Xamarin.Forms a un intento de resolver el tipo.
Nota:
La API que usa un contenedor de inserción de dependencias es específica del contenedor. Los ejemplos de código de este artículo usan Autofac como contenedor de inserción de dependencias, que proporciona los IContainer tipos ContainerBuilder y . También se podrían usar contenedores de inserción de dependencias alternativos, pero usarían API diferentes de las que se presentan aquí.
Tenga en cuenta que no es necesario establecer el método de resolución de dependencias durante el inicio de la aplicación. Se puede establecer en cualquier momento. La única restricción es que debe conocer el método de resolución de dependencias en el momento en que la aplicación intenta consumir tipos almacenados en el contenedor de inserción Xamarin.Forms de dependencias. Por lo tanto, si hay servicios en el contenedor de inserción de dependencias que la aplicación necesitará durante el inicio, el método de resolución de dependencias tendrá que establecerse al principio del ciclo de vida de la aplicación. De forma similar, si el contenedor de inserción de dependencias administra la creación y duración de un determinado , deberá conocer el método de resolución de dependencias antes de intentar crear una vista que EffectXamarin.Forms use ese Effect .
Advertencia
Registrar y resolver tipos con un contenedor de inserción de dependencias tiene un costo de rendimiento debido al uso del contenedor de reflexión para crear cada tipo, especialmente si se reconstruyen dependencias para cada navegación por páginas de la aplicación. Si hay muchas dependencias, o estas son muy amplias, el costo de la creación puede aumentar significativamente.
Registro de tipos
Los tipos se deben registrar con el contenedor de inserción de dependencias para poder resolverlos mediante el método de resolución de dependencias. En el ejemplo de código siguiente se muestran los métodos de registro que la aplicación de ejemplo expone en la App clase para el contenedor Autofac:
using Autofac;
using Autofac.Core;
...
public partial class App : Application
{
static IContainer container;
static readonly ContainerBuilder builder = new ContainerBuilder();
...
public static void RegisterType<T>() where T : class
{
builder.RegisterType<T>();
}
public static void RegisterType<TInterface, T>() where TInterface : class where T : class, TInterface
{
builder.RegisterType<T>().As<TInterface>();
}
public static void RegisterTypeWithParameters<T>(Type param1Type, object param1Value, Type param2Type, string param2Name) where T : class
{
builder.RegisterType<T>()
.WithParameters(new List<Parameter>()
{
new TypedParameter(param1Type, param1Value),
new ResolvedParameter(
(pi, ctx) => pi.ParameterType == param2Type && pi.Name == param2Name,
(pi, ctx) => ctx.Resolve(param2Type))
});
}
public static void RegisterTypeWithParameters<TInterface, T>(Type param1Type, object param1Value, Type param2Type, string param2Name) where TInterface : class where T : class, TInterface
{
builder.RegisterType<T>()
.WithParameters(new List<Parameter>()
{
new TypedParameter(param1Type, param1Value),
new ResolvedParameter(
(pi, ctx) => pi.ParameterType == param2Type && pi.Name == param2Name,
(pi, ctx) => ctx.Resolve(param2Type))
}).As<TInterface>();
}
public static void BuildContainer()
{
container = builder.Build();
}
...
}
Cuando una aplicación usa un método de resolución de dependencias para resolver tipos de un contenedor, los registros de tipos normalmente se realizan desde proyectos de plataforma. Esto permite a los proyectos de plataforma registrar tipos para representadores, efectos e DependencyService implementaciones personalizados.
Tras el registro de tipos de un proyecto de plataforma, se debe crear el objeto , lo que se logra IContainer mediante una llamada al método BuildContainer . Este método invoca el método de Autofac en la instancia de , que crea un nuevo contenedor de inserción de dependencias que contiene los registros BuildContainerBuilder realizados.
En las secciones siguientes, una Logger clase que implementa la interfaz se inserta en ILogger constructores de clase. La clase implementa la funcionalidad de registro simple mediante el método y se usa para mostrar cómo se pueden insertar servicios en representadores, efectos LoggerDebug.WriteLine e DependencyService implementaciones personalizados.
Registro de representadores personalizados
La aplicación de ejemplo incluye una página que reproduce vídeos web, cuyo origen XAML se muestra en el ejemplo siguiente:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:video="clr-namespace:FormsVideoLibrary"
...>
<video:VideoPlayer Source="https://archive.org/download/BigBuckBunny_328/BigBuckBunny_512kb.mp4" />
</ContentPage>
La VideoPlayer vista se implementa en cada plataforma mediante una clase que proporciona la funcionalidad para reproducir el VideoPlayerRenderer vídeo. Para obtener más información sobre estas clases de representador personalizadas, vea Implementación de un reproductor de vídeo.
En iOS y la Plataforma Windows universal (UWP), las clases tienen VideoPlayerRenderer el constructor siguiente, que requiere un ILogger argumento :
public VideoPlayerRenderer(ILogger logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
En todas las plataformas, el registro de tipos con el contenedor de inserción de dependencias se realiza mediante el método , que se invoca antes de que la plataforma cargue la RegisterTypes aplicación con el método LoadApplication(new App()) . En el ejemplo siguiente se muestra RegisterTypes el método en la plataforma iOS:
void RegisterTypes()
{
App.RegisterType<ILogger, Logger>();
App.RegisterType<FormsVideoLibrary.iOS.VideoPlayerRenderer>();
App.BuildContainer();
}
En este ejemplo, el tipo concreto se registra a través de una asignación en su tipo de interfaz y el tipo Logger se registra directamente sin una VideoPlayerRenderer asignación de interfaz. Cuando el usuario navega a la página que contiene la vista, se invoca el método de resolución de dependencias para resolver el tipo desde el contenedor de inserción de dependencias, que también resolverá e insertará el tipo en el VideoPlayerVideoPlayerRendererLoggerVideoPlayerRenderer constructor.
El VideoPlayerRenderer constructor de la plataforma Android es ligeramente más complicado, ya que requiere un argumento además del argumento ContextILogger :
public VideoPlayerRenderer(Context context, ILogger logger) : base(context)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
En el ejemplo siguiente se muestra RegisterTypes el método en la plataforma Android:
void RegisterTypes()
{
App.RegisterType<ILogger, Logger>();
App.RegisterTypeWithParameters<FormsVideoLibrary.Droid.VideoPlayerRenderer>(typeof(Android.Content.Context), this, typeof(ILogger), "logger");
App.BuildContainer();
}
En este ejemplo, el App.RegisterTypeWithParameters método registra con el contenedor de VideoPlayerRenderer inserción de dependencias. El método de registro garantiza que la instancia se insertará como argumento y que el tipo MainActivityContext se Logger insertará como ILogger argumento.
Registro de efectos
La aplicación de ejemplo incluye una página que usa un efecto de seguimiento táctil para arrastrar BoxView instancias alrededor de la página. se Effect agrega a mediante el código BoxView siguiente:
var boxView = new BoxView { ... };
var touchEffect = new TouchEffect();
boxView.Effects.Add(touchEffect);
La TouchEffect clase es un que se implementa en cada plataforma mediante una clase que es un RoutingEffectTouchEffectPlatformEffect . La clase TouchEffect de plataforma proporciona la funcionalidad para arrastrar alrededor de la BoxView página. Para obtener más información sobre estas clases de efecto, vea Invocación de eventos a partir de efectos.
En todas las plataformas, TouchEffect la clase tiene el constructor siguiente, que requiere un argumento ILogger :
public TouchEffect(ILogger logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
En todas las plataformas, el registro de tipos con el contenedor de inserción de dependencias se realiza mediante el método , que se invoca antes de que la plataforma cargue la RegisterTypes aplicación con el método LoadApplication(new App()) . En el ejemplo siguiente se muestra RegisterTypes el método en la plataforma Android:
void RegisterTypes()
{
App.RegisterType<ILogger, Logger>();
App.RegisterType<TouchTracking.Droid.TouchEffect>();
App.BuildContainer();
}
En este ejemplo, el tipo concreto se registra a través de una asignación en su tipo de interfaz y el tipo Logger se registra directamente sin una TouchEffect asignación de interfaz. Cuando el usuario navega a la página que contiene una instancia de que tiene asociado el objeto , se invocará el método de resolución de dependencias para resolver el tipo de plataforma desde el contenedor de inserción de dependencias, que también resolverá e insertará el tipo en el BoxViewTouchEffectTouchEffectLoggerTouchEffect constructor.
Registro de implementaciones de DependencyService
La aplicación de ejemplo incluye una página que usa implementaciones en cada plataforma para permitir que el usuario elija una foto de la biblioteca de DependencyService imágenes del dispositivo. La interfaz define la funcionalidad que implementan las implementaciones IPhotoPicker de y se muestra en el ejemplo DependencyService siguiente:
public interface IPhotoPicker
{
Task<Stream> GetImageStreamAsync();
}
En cada proyecto de plataforma, PhotoPicker la clase implementa la interfaz mediante las API de la IPhotoPicker plataforma. Para obtener más información sobre estos servicios de dependencia, consulte Selección de una foto de la biblioteca de imágenes.
En iOS y UWP, las PhotoPicker clases tienen el constructor siguiente, que requiere un argumento ILogger :
public PhotoPicker(ILogger logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
En todas las plataformas, el registro de tipos con el contenedor de inserción de dependencias se realiza mediante el método , que se invoca antes de que la plataforma cargue la RegisterTypes aplicación con el método LoadApplication(new App()) . En el ejemplo siguiente se muestra RegisterTypes el método en UWP:
void RegisterTypes()
{
DIContainerDemo.App.RegisterType<ILogger, Logger>();
DIContainerDemo.App.RegisterType<IPhotoPicker, Services.UWP.PhotoPicker>();
DIContainerDemo.App.BuildContainer();
}
En este ejemplo, el tipo concreto se registra a través de una asignación en su tipo de interfaz y el tipo también se registra a través LoggerPhotoPicker de una asignación de interfaz.
El PhotoPicker constructor de la plataforma Android es ligeramente más complicado, ya que requiere un argumento además del argumento ContextILogger :
public PhotoPicker(Context context, ILogger logger)
{
_context = context ?? throw new ArgumentNullException(nameof(context));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
En el ejemplo siguiente se muestra RegisterTypes el método en la plataforma Android:
void RegisterTypes()
{
App.RegisterType<ILogger, Logger>();
App.RegisterTypeWithParameters<IPhotoPicker, Services.Droid.PhotoPicker>(typeof(Android.Content.Context), this, typeof(ILogger), "logger");
App.BuildContainer();
}
En este ejemplo, el App.RegisterTypeWithParameters método registra con el contenedor de PhotoPicker inserción de dependencias. El método de registro garantiza que la instancia se insertará como argumento y que el tipo MainActivityContext se Logger insertará como ILogger argumento.
Cuando el usuario navega a la página de selección de fotos y elige seleccionar una foto, se OnSelectPhotoButtonClicked ejecuta el controlador:
async void OnSelectPhotoButtonClicked(object sender, EventArgs e)
{
...
var photoPickerService = DependencyService.Resolve<IPhotoPicker>();
var stream = await photoPickerService.GetImageStreamAsync();
if (stream != null)
{
image.Source = ImageSource.FromStream(() => stream);
}
...
}
Cuando se invoca el método , se invoca el método de resolución de dependencias para resolver el tipo desde el contenedor de inserción de dependencias, que también resolverá e insertará el tipo DependencyService.Resolve<T>PhotoPicker en el LoggerPhotoPicker constructor.
Nota:
El método debe usarse al resolver un tipo desde el contenedor de inserción de dependencias de Resolve<T> la aplicación a través de DependencyService .
Descarga del ejemplo