Xamarin.Forms での依存関係の解決
この記事では、アプリケーションの依存関係挿入コンテナーがカスタム レンダラー、効果、DependencyService 実装の作成と有効期間を制御できるように、依存関係解決メソッド Xamarin.Forms を に挿入する方法について説明します。 この記事のコード例は、コンテナーを使用した 依存関係の解決 のサンプルから取得しています。
Model-View-ViewModel (MVVM) パターンを使用するアプリケーションの Xamarin.Forms コンテキストでは、依存関係挿入コンテナーを使用して、ビュー モデルの登録と解決、およびサービスの登録とビュー モデルへの挿入を行うことができます。 ビュー モデルの作成時に、コンテナーは必要な依存関係を挿入します。 これらの依存関係が作成されていない場合、コンテナーは最初に依存関係を作成して解決します。 依存関係の挿入の詳細 (ビュー モデルへの依存関係の挿入の例など) については、「 依存関係の挿入」を参照してください。
プラットフォーム プロジェクトでの型の作成と有効期間の制御は、従来、 メソッドをActivator.CreateInstance
使用してXamarin.Formsカスタム レンダラー、効果、実装DependencyService
のインスタンスを作成することで実行されます。 残念ながら、これにより、開発者はこれらの型の作成と有効期間、およびそれらに依存関係を挿入する機能を制限します。 この動作を変更するには、型の作成方法を制御する依存関係解決メソッドを に Xamarin.Forms 挿入します(アプリケーションの依存関係挿入コンテナーまたは によって Xamarin.Forms)。 ただし、依存関係解決メソッドを に挿入する必要がないことに Xamarin.Forms注意してください。 Xamarin.Forms は、依存関係解決メソッドが挿入されていない場合、プラットフォーム プロジェクトで型の有効期間を作成および管理し続けます。
注意
この記事では、依存関係挿入コンテナーを使用して登録済み型を解決する に依存関係解決メソッド Xamarin.Forms を挿入することに重点を置いていますが、ファクトリ メソッドを使用して登録済み型を解決する依存関係解決メソッドを挿入することもできます。 詳細については、「 Factory メソッドを使用した依存関係の解決 」サンプルを参照してください。
依存関係解決メソッドの挿入
クラスは DependencyResolver
、 メソッドを使用して に依存関係解決メソッド Xamarin.Formsを挿入する機能を ResolveUsing
提供します。 その後、特定の型のインスタンスが必要な場合 Xamarin.Forms は、依存関係解決メソッドにインスタンスを提供する機会が与えられます。 要求された型に対して依存関係解決メソッドが null
を返す場合は、 Xamarin.Forms メソッドを使用して Activator.CreateInstance
型インスタンス自体を作成しようとします。
次の例は、 メソッドを使用して依存関係解決メソッドを設定する方法を ResolveUsing
示しています。
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);
...
}
...
}
この例では、依存関係解決メソッドは、Autofac 依存関係挿入コンテナーを使用して、コンテナーに登録されている型を解決するラムダ式に設定されています。 それ以外の場合は が null
返され、その結果 Xamarin.Forms 、型の解決が試みられます。
注意
依存関係挿入コンテナーによって使用される API は、コンテナーに固有です。 この記事のコード例では、 型と ContainerBuilder
型を提供する依存関係挿入コンテナーとして Autofac をIContainer
使用します。 別の依存関係挿入コンテナーも同様に使用できますが、ここに示されている API とは異なる API を使用します。
アプリケーションの起動時に依存関係解決方法を設定する必要がないことに注意してください。 いつでも設定できます。 唯一の制約は、アプリケーションが Xamarin.Forms 依存関係挿入コンテナーに格納されている型の使用を試みるまでに、依存関係解決メソッドについて知る必要があることです。 したがって、起動時にアプリケーションが必要とする依存関係挿入コンテナーにサービスがある場合、依存関係解決方法は、アプリケーションのライフサイクルの早い段階で設定する必要があります。 同様に、依存関係挿入コンテナーが特定Effect
Xamarin.Formsの の作成と有効期間を管理する場合は、その Effect
を使用するビューの作成を試みる前に、依存関係解決方法について知る必要があります。
警告
依存関係挿入コンテナーを使用した型の登録と解決にはパフォーマンス コストがかかります。これは、コンテナーが各型の作成にリフレクションを使用するためです。特に、アプリケーション内の各ページ ナビゲーションに対して依存関係が再構築されている場合です。 依存関係が多いか深い場合、作成コストが大幅に増加する可能性があります。
型の登録
型は、依存関係解決メソッドを使用して解決する前に、依存関係挿入コンテナーに登録する必要があります。 次のコード例は、サンプル アプリケーションが Autofac コンテナーの クラスで App
公開する登録メソッドを示しています。
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();
}
...
}
アプリケーションで依存関係解決メソッドを使用してコンテナーから型を解決する場合、型の登録は通常、プラットフォーム プロジェクトから実行されます。 これにより、プラットフォーム プロジェクトでカスタム レンダラー、効果、実装 DependencyService
の型を登録できます。
プラットフォーム プロジェクトからの型登録に従って、 オブジェクトを IContainer
ビルドする必要があります。これは、 メソッドを BuildContainer
呼び出すことによって実現されます。 このメソッドは、 インスタンスで Autofac の Build
メソッドを ContainerBuilder
呼び出します。これにより、作成された登録を含む新しい依存関係挿入コンテナーがビルドされます。
次のセクションでは、インターフェイスを Logger
実装 ILogger
するクラスがクラス コンストラクターに挿入されます。 クラスは Logger
、 メソッドを使用して単純なログ機能を Debug.WriteLine
実装し、カスタム レンダラー、効果、実装 DependencyService
にサービスを挿入する方法を示すために使用されます。
カスタム レンダラーの登録
サンプル アプリケーションには、次の例に示す XAML ソースを含む Web ビデオを再生するページが含まれています。
<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>
ビューは VideoPlayer
、ビデオを再生するための機能を VideoPlayerRenderer
提供する クラスによって各プラットフォームに実装されます。 これらのカスタム レンダラー クラスの詳細については、「 ビデオ プレーヤーの実装」を参照してください。
iOS および ユニバーサル Windows プラットフォーム (UWP) VideoPlayerRenderer
では、クラスには次のコンストラクターがあり、これには引数がILogger
必要です。
public VideoPlayerRenderer(ILogger logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
すべてのプラットフォームで、依存関係挿入コンテナーへの型登録は メソッドによって RegisterTypes
実行されます。これは、 メソッドを使用してアプリケーション LoadApplication(new App())
を読み込む前に、プラットフォームによって呼び出されます。 次の例は、 RegisterTypes
iOS プラットフォームの メソッドを示しています。
void RegisterTypes()
{
App.RegisterType<ILogger, Logger>();
App.RegisterType<FormsVideoLibrary.iOS.VideoPlayerRenderer>();
App.BuildContainer();
}
この例では、 Logger
具象型はインターフェイス型に対するマッピングを介して登録され VideoPlayerRenderer
、型はインターフェイス マッピングなしで直接登録されます。 ユーザーがビューを含むVideoPlayer
ページに移動すると、依存関係解決メソッドが呼び出されて依存関係挿入コンテナーから型が解決VideoPlayerRenderer
されます。これにより、型も解決され、コンストラクターにVideoPlayerRenderer
挿入Logger
されます。
VideoPlayerRenderer
Android プラットフォーム上のコンストラクターは、引数に加えてILogger
引数をContext
必要とするため、少し複雑になります。
public VideoPlayerRenderer(Context context, ILogger logger) : base(context)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
次の例は、 RegisterTypes
Android プラットフォームの メソッドを示しています。
void RegisterTypes()
{
App.RegisterType<ILogger, Logger>();
App.RegisterTypeWithParameters<FormsVideoLibrary.Droid.VideoPlayerRenderer>(typeof(Android.Content.Context), this, typeof(ILogger), "logger");
App.BuildContainer();
}
この例では、 メソッドは App.RegisterTypeWithParameters
依存関係挿入コンテナーに を VideoPlayerRenderer
登録します。 登録メソッドを使用すると MainActivity
、インスタンスが引数として Context
挿入され、型が Logger
引数として ILogger
挿入されます。
効果の登録
サンプル アプリケーションには、タッチ追跡効果を使用してページの周囲にインスタンスをドラッグ BoxView
するページが含まれています。 Effect
は、次のBoxView
コードを使用して に追加されます。
var boxView = new BoxView { ... };
var touchEffect = new TouchEffect();
boxView.Effects.Add(touchEffect);
TouchEffect
クラスは、 RoutingEffect
のクラスによってTouchEffect
各プラットフォームに実装される PlatformEffect
です。 プラットフォーム TouchEffect
クラスは、ページの周囲をドラッグするための BoxView
機能を提供します。 これらの効果クラスの詳細については、「効果 からのイベントの呼び出し」を参照してください。
すべてのプラットフォームで、 TouchEffect
クラスには次のコンストラクターがあり、これには引数が ILogger
必要です。
public TouchEffect(ILogger logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
すべてのプラットフォームで、依存関係挿入コンテナーへの型登録は メソッドによって RegisterTypes
実行されます。これは、 メソッドを使用してアプリケーション LoadApplication(new App())
を読み込む前に、プラットフォームによって呼び出されます。 次の例は、 RegisterTypes
Android プラットフォームの メソッドを示しています。
void RegisterTypes()
{
App.RegisterType<ILogger, Logger>();
App.RegisterType<TouchTracking.Droid.TouchEffect>();
App.BuildContainer();
}
この例では、 Logger
具象型はインターフェイス型に対するマッピングを介して登録され TouchEffect
、型はインターフェイス マッピングなしで直接登録されます。 アタッチされている インスタンスを含むBoxView
ページにユーザーがTouchEffect
移動すると、依存関係解決メソッドが呼び出され、依存関係挿入コンテナーからプラットフォームTouchEffect
の種類が解決されます。これにより、型も解決され、コンストラクターにTouchEffect
挿入Logger
されます。
DependencyService 実装の登録
サンプル アプリケーションには、各プラットフォームの実装を使用 DependencyService
して、ユーザーがデバイスの画像ライブラリから写真を選択できるようにするページが含まれています。 インターフェイスは IPhotoPicker
、 実装によって実装される機能を DependencyService
定義し、次の例に示します。
public interface IPhotoPicker
{
Task<Stream> GetImageStreamAsync();
}
各プラットフォーム プロジェクトでは、 クラスは PhotoPicker
プラットフォーム API を使用して IPhotoPicker
インターフェイスを実装します。 これらの依存関係サービスの詳細については、「 画像ライブラリから写真を選択する」を参照してください。
iOS および UWP では、 PhotoPicker
クラスには次のコンストラクターがあり、これには引数が ILogger
必要です。
public PhotoPicker(ILogger logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
すべてのプラットフォームで、依存関係挿入コンテナーへの型登録は メソッドによって RegisterTypes
実行されます。これは、 メソッドを使用してアプリケーション LoadApplication(new App())
を読み込む前に、プラットフォームによって呼び出されます。 次の例は、UWP の RegisterTypes
メソッドを示しています。
void RegisterTypes()
{
DIContainerDemo.App.RegisterType<ILogger, Logger>();
DIContainerDemo.App.RegisterType<IPhotoPicker, Services.UWP.PhotoPicker>();
DIContainerDemo.App.BuildContainer();
}
この例では、 Logger
具象型はインターフェイス型に対するマッピングを介して登録され PhotoPicker
、型もインターフェイス マッピングを介して登録されます。
PhotoPicker
Android プラットフォーム上のコンストラクターは、引数に加えてILogger
引数をContext
必要とするため、少し複雑になります。
public PhotoPicker(Context context, ILogger logger)
{
_context = context ?? throw new ArgumentNullException(nameof(context));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
次の例は、 RegisterTypes
Android プラットフォームの メソッドを示しています。
void RegisterTypes()
{
App.RegisterType<ILogger, Logger>();
App.RegisterTypeWithParameters<IPhotoPicker, Services.Droid.PhotoPicker>(typeof(Android.Content.Context), this, typeof(ILogger), "logger");
App.BuildContainer();
}
この例では、 メソッドは App.RegisterTypeWithParameters
依存関係挿入コンテナーに を PhotoPicker
登録します。 登録メソッドを使用すると MainActivity
、インスタンスが引数として Context
挿入され、型が Logger
引数として ILogger
挿入されます。
ユーザーが写真の選択ページに移動し、写真の選択を選択すると、 OnSelectPhotoButtonClicked
ハンドラーが実行されます。
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);
}
...
}
メソッドがDependencyService.Resolve<T>
呼び出されると、依存関係解決メソッドが呼び出され、依存関係挿入コンテナーから型が解決PhotoPicker
されます。これにより、型も解決され、コンストラクターにPhotoPicker
挿入Logger
されます。
注意
メソッドは Resolve<T>
、 を介してアプリケーションの依存関係挿入コンテナーから型を解決するときに使用する DependencyService
必要があります。