Xamarinを使用したクロスプラットフォームアプリの構築:Android開発者の視点

公開: 2022-03-11

コードを一度作成して複数のプラットフォームで使用することは、多くのソフトウェア開発者の夢でした。 これはしばらくの間可能でしたが、保守性、テストの容易さ、さらに悪いことに、ユーザーエクスペリエンスの低下という犠牲を払って常に発生していました。

ネイティブSDKを使用してモバイルアプリケーションを開発することは、デスクトップアプリケーション開発の領域にルーツを持つすべての開発者にとっておそらく出発点です。 プログラミング言語は一部の人にとって障壁になります。Javaデスクトップまたはバックエンドアプリケーションの開発経験がある場合は、モバイルアプリ開発会社に移動してAndroidを操作する方が、iOS用のObjective-Cを最初から始めるよりもはるかに簡単です。

私は常にクロスプラットフォームのアプリケーション開発に懐疑的でした。 Sencha、Cordova、TitaniumなどのJavaScriptベースのフレームワークは、パフォーマンスが重要な場合に賢明な選択であるとは決して言えません。 これらのフレームワークでは、APIの欠如と風変わりなユーザーエクスペリエンスが提供されました。

しかし、その後、Xamarinに出くわしました。

Xamarinを使用したクロスプラットフォーム開発

この記事では、Xamarinを使用して、モバイルアプリケーション開発の他の側面を損なうことなく、複数のプラットフォーム間でコードを共有する方法を学習します。 この記事では特にAndroidとiOSに焦点を当てますが、Xamarinがサポートする他のプラットフォームのサポートを追加する同様のアプローチを使用できます。

Xamarinとは何ですか?

Xamarinは、iOS、Android、およびWindows Phone用のクロスプラットフォーム(ただしネイティブ)のアプリケーションをC#および.NETで作成できるようにする開発プラットフォームです。

Xamarinは、ネイティブのAndroidおよびiOS APIへのC#バインディングを提供します。 これにより、AndroidとiOSのネイティブユーザーインターフェイス、通知、グラフィックス、アニメーション、その他の電話機能をすべてC#を使用して使用できるようになります。

AndroidとiOSの新しいリリースはそれぞれ、Xamarinと一致し、新しいAPIのバインディングを含む新しいリリースがあります。

Xamarinの.NETの移植版には、データ型、ジェネリック、ガベージコレクション、統合言語クエリ(LINQ)、非同期プログラミングパターン、デリゲート、Windows Communication Foundation(WCF)のサブセットなどの機能が含まれています。 ライブラリは、参照されるコンポーネントのみを含むように長引くように管理されます。

Xamarin.Formsは、他のUIバインディングとWindows Phone APIの上にあるレイヤーであり、完全にクロスプラットフォームのユーザーインターフェイスライブラリを提供します。

Xamarinの範囲

クロスプラットフォームアプリケーションの作成

Xamarinを使用してクロスプラットフォームアプリケーションを作成するには、開発者は2つの利用可能なタイプのプロジェクトのいずれかを選択する必要があります。

  • ポータブルクラスライブラリ(PCL)
  • 共有プロジェクト

PCLを使用すると、複数のプラットフォーム間で共有できるコードを記述できますが、1つの制限があります。 すべての.NETAPIがすべてのプラットフォームで利用できるわけではないため、PCLプロジェクトでは、対象となるプラットフォームでの実行に制限されます。

Xamarinの接続と制限

次の表は、どのAPIがどのプラットフォームで利用できるかを示しています。

特徴。ネットフレームワークWindowsストアアプリSilverlight ウインドウズの電話Xamarin
Y Y Y Y Y
LINQ Y Y Y Y Y
IQueryable Y Y Y 7.5+ Y
シリアル化Y Y Y Y Y
データ注釈4.0.3+ Y Y Y Y

ビルドプロセス中に、PCLは個別のDLLにコンパイルされ、実行時にMonoによってロードされます。 同じインターフェイスの異なる実装を実行時に提供できます。

一方、共有プロジェクトでは、サポートするプラットフォームごとにプラットフォーム固有のコードを記述できるため、より詳細に制御できます。 共有プロジェクトのコードには、コードを使用しているアプリケーションプロジェクトに応じて、コードのセクションを有効または無効にするコンパイラ指令を含めることができます。

PCLとは異なり、共有プロジェクトはDLLを生成しません。 コードは最終プロジェクトに直接含まれています。

MvvmCrossを使用してクロスプラットフォームコードに構造を与える

再利用可能なコードは、開発チームの費用と時間を節約する可能性があります。 ただし、適切に構造化されたコードを使用すると、開発者の作業がはるかに楽になります。 うまく書かれたバグのないコードを開発者ほど高く評価する人は誰もいません。

Xamarin自体は、再利用可能なクロスプラットフォームコードの記述をはるかに簡単にするメカニズムを提供します。

モバイル開発者は、iOS、Android、およびその他のプラットフォームをサポートするために、同じロジックを2回以上作成する必要があるシナリオに精通しています。 ただし、Xamarinを使用すると、前の章で説明したように、あるプラットフォーム用に記述されたコードを他のプラットフォームでも簡単に再利用できます。

では、MvvmCrossはどこに配置されますか?

MvvmCrossは、その名前が示唆しているように、XamarinアプリケーションでMVVMパターンを使用できるようにします。 クロスプラットフォームのアプリケーション開発に非常に便利なライブラリ、API、ユーティリティが多数付属しています。

MvvmCrossを使用すると、アプリケーション開発の他のアプローチで作成するボイラープレートコードの量を大幅に減らすことができます(異なる言語で複数回作成することもあります)。

MvvmCrossソリューションの構造

MvvmCrossコミュニティは、MvvmCrossソリューションを構築するための非常にシンプルで効率的な方法を推奨しています。

 <ProjectName>.Core <ProjectName>.UI.Droid <ProjectName>.UI.iOS

MvvmCrossソリューションのコアプロジェクトは、再利用可能なコードに関連しています。 コアプロジェクトはXamarinPCLプロジェクトであり、主な焦点は再利用性です。

Coreで記述されたコードは、可能な限りプラットフォームに依存しないようにする必要があります。 すべてのプラットフォームで再利用できるロジックのみを含める必要があります。 Coreプロジェクトでは、AndroidまたはiOS APIを使用したり、プラットフォームに固有のものにアクセスしたりしてはなりません。

ビジネスロジック層、データ層、およびバックエンド通信はすべて、コアプロジェクトに含めるのに最適な候補です。 ビュー階層(アクティビティ、フラグメントなど)のナビゲーションは、コアで実現されます。

続行する前に、MvvmCrossとその動作を理解するために重要な1つのアーキテクチャデザインパターンを理解する必要があります。 名前からわかるように、MvvmCrossはMVVMパターンに大きく依存しています。

MVVMは、ビジネスロジックおよびバックエンドデータからのグラフィカルユーザーインターフェイスの分離を容易にするアーキテクチャデザインパターンです。

このパターンはMvvmCrossでどのように使用されますか?

コードの高い再利用性を実現したいので、PCLプロジェクトであるCoreにできるだけ多くのコードを入れたいと考えています。 ビューはプラットフォームごとに異なるコードの唯一の部分であるため、プラットフォーム間でビューを再利用することはできません。 その部分は、プラットフォームに関連するプロジェクトで実装されます。

MvvmCross構造

MvvmCrossを使用すると、ViewModelsを使用してコアからのアプリケーションナビゲーションを調整できます。

基本と技術的な詳細がわからなくなったら、独自のMvvmCrossCoreプロジェクトを作成してXamarinの使用を開始しましょう。

MvvmCrossコアプロジェクトの作成

Xamarin Studioを開き、 ToptalExampleSolutionという名前のソリューションを作成します。

ソリューションの作成

コアプロジェクトを作成しているので、命名規則に従うことをお勧めします。 Coreサフィックスがプロジェクト名に追加されていることを確認してください。

MvvmCrossをサポートするには、プロジェクトにMvvmCrossライブラリを追加する必要があります。 さらに、XamarinStudioでNuGetの組み込みサポートを使用できます。

ライブラリを追加するには、Packagesフォルダーを右クリックし、[パッケージの追加... ]オプションを選択します。

検索フィールドで、MvvmCrossを検索できます。これにより、以下に示すように、MvvmCrossに関連する結果が除外されます。

結果のフィルタリング

[パッケージの追加]ボタンをクリックすると、プロジェクトに追加されます。

MvvmCrossがプロジェクトに追加されたので、コアコードを作成する準備が整いました。

最初のViewModelを定義しましょう。 1つを作成するには、次のようにフォルダの階層を作成します。

フォルダの推奨階層

各フォルダの内容は次のとおりです。

  • モデル:実際の状態のコンテンツを表すドメインモデル
  • サービス:サービス(ビジネスロジック、データベースなど)を保持するフォルダー
  • ViewModel:モデルとの通信方法

最初のViewModelはFirstViewModel.csと呼ばれます

 public class FirstViewModel : MvxViewModel { private string _firstName; private string _lastName; private string _fullName; public string FirstName { get { return _firstName; } set { _lastName = value; RaisePropertyChanged(); } } public string LastName { get { return _lastName; } set { _lastName = value; RaisePropertyChanged(); } } public string FullName { get { return _fullName; } set { _fullName = value; RaisePropertyChanged(); } } public IMvxCommand ConcatNameCommand { get { return new MvxCommand(() => { FullName = $"{FirstName} {LastName}"; }); } public IMvxCommand NavigateToSecondViewModelCommand { get { return new MvxCommand(() => { ShowViewModel<SecondViewModel>(); }); } } }

最初のViewModelができたので、最初のビューを作成して、物事をバインドできます。

Android UI

ViewModelのコンテンツを表示するには、UIを作成する必要があります。

Android UIを作成するための最初のステップは、現在のソリューションでAndroidプロジェクトを作成することです。 これを行うには、ソリューション名を右クリックして、[追加] ->[新しいプロジェクトの追加... ]を選択します。 ウィザードで、Androidアプリを選択し、プロジェクトToptalExample.UI.Droidという名前を付けていることを確認します。

前に説明したように、AndroidのMvvmCross依存関係を追加する必要があります。 これを行うには、NuGetの依存関係を追加するためのコアプロジェクトの場合と同じ手順に従います。

MvvmCross依存関係を追加した後、そこに記述されたコードを使用できるように、コアプロジェクトへの参照を追加する必要があります。 PCLプロジェクトへの参照を追加するには、[参照]フォルダーを右クリックし、[参照の編集... ]オプションを選択します。 [プロジェクト]タブで、以前に作成したコアプロジェクトを選択し、[OK]をクリックします。

PCLプロジェクトへの参照の追加

次の部分は、理解するのが少し難しい場合があります。

次に、MvvmCrossにアプリケーションの設定方法を指示する必要があります。 そのためには、 Setupクラスを作成する必要があります。

 namespace ToptalExample.UI.Droid { public class Setup : MvxAndroidSetup { public Setup(Context context) : base(context) { } protected override IMvxApplication CreateApp() { return new Core.App(); } } }

クラスからわかるように、 Core.App実装に基づいてMvvmCrossにCreateAppを指示しています。これは、Coreで定義され、以下に示すクラスです。

 public class App : MvxApplication { public override void Initialize() { RegisterAppStart(new AppStart()); } } public class AppStart : MvxNavigatingObject, IMvxAppStart { public void Start(object hint = null) { ShowViewModel<FirstViewModel>(); } }

Appクラスでは、最初のViewModelを表示するAppStartのインスタンスを作成しています。

現在残っているのは、MvvmCrossによってバインドされるAndroidレイアウトファイルを作成することだけです。

 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:andro xmlns:local="http://schemas.android.com/apk/res-auto" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <EditText android:layout_width="match_parent" android:layout_height="match_parent" local:MvxBind="Text FirstName" /> <EditText android:layout_width="match_parent" android:layout_height="match_parent" local:MvxBind="Text LastName" /> <TextView android:layout_width="match_parent" android:layout_height="match_parent" local:MvxBind="Text FullName" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" local:MvxBind="Click ConcatNameCommand" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" local:MvxBind="Click NavigateToSecondViewModelCommand" /> </LinearLayout>

レイアウトファイルには、MvvmCrossによって自動的に解決されるバインディングがあります。 EditTextの場合、Textプロパティのバインディングを作成します。これは、双方向のバインディングになります。 ViewModel側から呼び出された変更は、ビューに自動的に反映され、その逆も同様です。

Viewクラスは、アクティビティまたはフラグメントにすることができます。 簡単にするために、指定されたレイアウトをロードするアクティビティを使用しています。

 [Activity(Label = "ToptalExample.UI.Droid", Theme = "@style/Theme.AppCompat", MainLauncher = true, Icon = "@mipmap/icon")] public class MainActivity : MvxAppCompatActivity<FirstViewModel> { protected override void OnCreate(Bundle bundle) { base.OnCreate(bundle); SetContentView(Resource.Layout.Main); } }

最初のボタンにはコマンドバインディングがあります。これは、ボタンをクリックすると、MvvmCrossがViewModelからContactNameCommandを呼び出すことを意味します。

2番目のボタンについては、別のViewModelを表示します。

iOS UI

iOSプロジェクトの作成は、Androidプロジェクトの作成と実際には違いはありません。 新しいプロジェクトを追加するには、同様の手順に従う必要があります。今回は、AndroidではなくiOSプロジェクトを作成するだけです。 命名規則の一貫性を保つようにしてください。

iOSプロジェクトを追加した後、MvvmCrossiOSの依存関係を追加する必要があります。 手順はCoreおよびAndroidの場合とまったく同じです(iOSプロジェクトで[参照]を右クリックし、[参照の追加... ]をクリックします)。

ここで、Androidの場合と同様に、 Setupクラスを作成する必要があります。このクラスは、MvvmCrossにアプリケーションのセットアップ方法を指示します。

 public class Setup : MvxIosSetup { public Setup(MvxApplicationDelegate appDelegate, IMvxIosViewPresenter presenter) : base(appDelegate, presenter) { } protected override MvvmCross.Core.ViewModels.IMvxApplication CreateApp() { return new App(); } }

SetupクラスはMvxIosSetupを拡張し、Androidの場合はMvxAndroidSetupを拡張していることに注意してください。

ここでの1つの追加は、 AppDelegateクラスを変更する必要があることです。

iOSのAppDelegateは、ユーザーインターフェイスの起動を担当するため、iOSでビューがどのように表示されるかを通知する必要があります。 プレゼンターについて詳しくは、こちらをご覧ください。

 [Register("AppDelegate")] public class AppDelegate : MvxApplicationDelegate { // class-level declarations public override UIWindow Window { get; set; } public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions) { Window = new UIWindow(UIScreen.MainScreen.Bounds); var presenter = new MvxIosViewPresenter(this, Window); var setup = new Setup(this, presenter); setup.Initialize(); var startup = Mvx.Resolve<IMvxAppStart>(); startup.Start(); Window.MakeKeyAndVisible(); return true; } }

VIewModelを表示するには、ビューを作成する必要があります。 その場合は、プロジェクトを右クリックして[追加]-> [新しいファイル]を選択し、iOSセクションから[ViewController]を選択してViewControllerを作成します。これにFirstViewControllerという名前を付けます。

Xamarinは、バインディングがどうなるかを定義する3つのファイルを作成します。 Androidとは異なり、iOSの場合、コードを使用してバインディングを別の方法で定義する必要があります(ただし、Androidでも定義でき、場合によっては定義する必要があります)。

ビュー間を移動する必要がある場合は、ViewModelを介して実行されます。 コマンドNavigateToSecondViewModelCommandで、メソッドShowViewModel<SecondViewModel>()は適切なビューを見つけて、そのビューにナビゲートします。

しかし、MVVMCrossはどのビューをロードするかをどのように知るのでしょうか?

その中に魔法はありません。 Android(ActivityまたはFragment)のビューを作成するとき、タイプパラメーター( MvxAppCompatActivity<VM> )を使用して基本クラスの1つを拡張します。 ShowViewMolel<VM>を呼び出すと、MvvmCrossは、タイプパラメーターVMを使用してActivityクラスまたはFragmentクラスを拡張するViewを検索します。 これが、同じViewModelに対して2つのビュークラスを持つことが許可されていない理由です。

制御の反転

XamarinはネイティブAPIのC#ラッパーを提供しているだけなので、依存性注入(DI)や制御の反転(IoC)メカニズムは提供していません。

依存関係の実行時インジェクションまたはコンパイル時インジェクションがなければ、緩く結合され、再利用可能で、テスト可能で、保守が容易なコンポーネントを作成することは容易ではありません。 IoCとDIのアイデアは、非常に長い間知られています。 IoCの詳細については、オンラインの多くの記事をご覧ください。 これらのパターンの詳細については、MartinFowlerの紹介記事を参照してください。

IoCは、MvvmCrossesの初期バージョンから利用可能であり、アプリケーションの起動時および必要なときにいつでも依存関係を挿入できます。

緩く結合されたコンポーネントを取得するために、クラスの具体的な実装を要求することはできません。 具体的な実装を要求すると、実行時に実装の動作を変更する機能が制限されます(別の実装に置き換えることはできません)。 これらのコンポーネントのテストは困難です。

そのため、具体的な実装を1つ行うインターフェイスを宣言します。

 public interface IPasswordGeneratorService { string Generate(int length); }

そして実装:

 public class PasswordGeneratorService : IPasswordGeneratorService { public string Generate(int length) { var val; var res = new StringBuilder(); var rnd = new Random(); while (0 < length--) { res.Append(valid[rnd.Next(valid.Length)]); } return res.ToString(); } }

これで、ViewModelは、提供する責任があるインターフェイスIPasswordGenerationServiceのインスタンスを必要とすることができます。

MvvmCrossが実行時にPasswordGeneratorService実装を挿入するには、使用する実装をMvvmCrossに指示する必要があります。 両方のプラットフォームに1つの実装を使用する場合は、アプリケーションの登録後に、 App.csに実装を登録できます。

 public override void Initialize() { RegisterAppStart(new AppStart()); Mvx.LazyConstructAndRegisterSingleton<IPasswordGeneratorService, PasswordGeneratorService>(); }

上記の静的メソッドLazyConstructAndRegisterSingleton<TInterface, TType>の呼び出しは、注入される実装を登録します。 このメソッドは適切な実装を登録しますが、オブジェクトは作成しません。

オブジェクトは、シングルトンとして登録されているため、必要な場合に1回だけ作成されます。

シングルトンオブジェクトをすぐに作成したい場合は、 Mvx.RegisterSingleton<TInterface>()を呼び出すことで実現できます。

アプリケーションにシングルトンだけを含めたくない場合があります。 オブジェクトがスレッドセーフではないか、常に新しいインスタンスが必要な他の理由がある可能性があります。 その場合、MvvmCrossはメソッドMvx.RegisterType<TInterface,TType>()を提供します。このメソッドを使用して、必要なときにいつでも新しいインスタンスをインスタンス化する方法で実装を登録できます。

プラットフォームごとに個別の具体的な実装を提供する必要がある場合は、プラットフォーム固有のプロジェクトでいつでも提供できます。

 public class DroidPasswodGeneratorService : IPasswordGeneratorService { public string Generate(int length) { return "DroidPasswordGenerator"; } }

そして、実装の登録は、DroidプロジェクトのSetup.csクラスで行われます。

 protected override void InitializePlatformServices() { base.InitializePlatformServices(); Mvx.LazyConstructAndRegisterSingleton<IPasswordGeneratorService, DroidPasswodGeneratorService>(); }

PCLコードの初期化後、MvvmCrossはInitializePlatformServicesを呼び出し、プラットフォーム固有のサービス実装を登録します。

複数のシングルトン実装を登録する場合、MvvmCrossは最後に登録された実装のみを使用します。 他のすべての登録は破棄されます。

Xamarinを使用してクロスプラットフォームアプリを構築する

この記事では、Xamarinを使用して、さまざまなプラットフォーム間でコードを共有し、アプリケーションのネイティブな感触とパフォーマンスを維持する方法について説明しました。

MvvmCrossは、Xamarinを使用してクロスプラットフォームアプリケーションを構築するエクスペリエンスをさらに強化する、別の抽象化レイヤーを提供します。 MVVMパターンは、すべてのプラットフォームに共通のナビゲーションフローとユーザーインタラクションフローを作成する方法を提供し、作成する必要のあるプラットフォーム固有のコードの量をビューのみに制限します。

この記事がXamarinを覗き見する理由を与え、Xamarinを使用して次のクロスプラットフォームアプリケーションを構築する動機を与えてくれることを願っています。