Xamarin.Android バインディング プロジェクトの移行

.NET には、別個のプロジェクト タイプとしての Android バインディング プロジェクトの概念はありません。 Xamarin.Android バインディング プロジェクトで動作する MSBuild 項目グループまたはビルド アクションは、.NET for Android アプリまたはライブラリを通じてサポートされます。

Xamarin.Android バインディング ライブラリを .NET for Android クラス ライブラリに移行するには、次の手順を行います。

  1. Visual Studio で、Xamarin.Android バインディング プロジェクトと同じ名前の新しい Android Java ライブラリ バインディング プロジェクトを作成します。

    Visual Studio で Android Java ライブラリ バインディング プロジェクトを作成するスクリーンショット。

    プロジェクト ファイルを開くと、.NET SDK スタイルのプロジェクトがあることが確認されます。

    <Project Sdk="Microsoft.NET.Sdk">
      <PropertyGroup>
        <TargetFramework>net8.0-android</TargetFramework>
        <SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
        <Nullable>enable</Nullable>
        <ImplicitUsings>enable</ImplicitUsings>
      </PropertyGroup>
    </Project>
    

    Note

    Android バインディング ライブラリのプロジェクト ファイルは、Android クラス ライブラリのプロジェクト ファイルと同じです。

  2. Java Archive (JAR) または Android Archive (AAR) をプロジェクトに追加し、そのビルド アクションが AndroidLibrary に設定されていることを確認します。

  3. Xamarin.Android バインディング ライブラリから変換または追加をコピーします。

サポートされていない従来のオプション

以下の従来のオプションはサポートされなくなりました。 サポートされている代替手段が数年間使用できます。最もスムーズな移行オプションは、これらのオプションを使用した現在のプロジェクトを更新しテストしてから .NET に移行することです。

AndroidClassParser

jar2xml は、$(AndroidClassParser) プロパティの有効なオプションではなくなりました。 class-parse が既定で唯一サポートされるオプションになりました。

class-parse は、次のような、jar2xml で使用できない多くの新しい最新機能を利用しています。

  • クラス メソッドの自動パラメーター名 (Java コードが javac -parameters でコンパイルされている 場合)。
  • Kotlin のサポート。
  • 静的/既定のインターフェイス メンバー (DIM) のサポート。
  • Java Null 許容参照型 (NRT) 注釈のサポート。

AndroidCodegenTarget

XamarinAndroid は、$(AndroidCodegenTarget) プロパティの有効なオプションではなくなりました。 XAJavaInterop1 が既定で唯一サポートされるオプションになりました。

生成されたバインディング コードとやり取りするハンドバインド コードが Additions ファイルにある場合は、XAJavaInterop1 との互換性を持つように更新する必要がある場合があります。

既定のファイル インクルージョン

次のファイル構造があるとします。

Transforms/
    Metadata.xml
foo.jar

Transforms\*.xml ファイルは @(TransformFile) 項目として自動的に組み込まれ、.jar/.aar ファイルは @(AndroidLibrary) 項目として自動的に組み込まれます。 この結果、Transforms\Metadata.xml からのメタデータ修正を使用して、foo.jar で見つかった Java 型に C# 型がバインドされます。

既定の Android 関連ファイルのグロブ動作は、AutoImport.props で定義されています。 $(EnableDefaultAndroidItems) プロパティを false に設定すれば、この動作を Android 項目に対して無効にできます。または、$(EnableDefaultItems) プロパティを false に設定すれば、既定項目のインクルージョンの動作をすべて無効にできます。

既定のワイルドカードで、望ましくない .jar ファイルや .aar ファイルが組み込まれる可能性もあります。 たとえば、次の C# コンパイラ エラーは、AndroidStudio\gradle\wrapper\gradle-wrapper.jar ファイルが意図せずにバインドされたことが原因で発生します。

Org.Gradle.Cli.AbstractCommandLineConverter.cs(11,89): error CS0535: 'Download' does not implement interface member 'IDownload.Download(URI, File)'
Org.Gradle.Wrapper.Download.cs(10,60): error CS0535: 'AbstractCommandLineConverter' does not implement interface member 'ICommandLineConverter.Convert(ParsedCommandLine, Object)'

この問題を解決するには、次のように、プロジェクト ファイルで特定のファイルを削除します。

<ItemGroup>
  <AndroidLibrary Remove="AndroidStudio\gradle\wrapper\gradle-wrapper.jar" />
</ItemGroup>

または、フォルダー内のすべてのファイルを除外することもできます。

<AndroidLibrary Remove="AndroidStudio\**\*" />

新しい項目グループ名

<AndroidLibrary> は、すべての .jar ファイルおよび .aar ファイルに使用する推奨の項目グループになりました。 Xamarin.Android では、次の項目グループが使用されていました。代わりに、項目メタデータを使用して同じ結果を得ることができます。

従来の項目グループ 新しい項目グループ 項目メタデータ 従来のプロジェクト タイプ
AndroidAarLibrary AndroidLibrary Bind="false" アプリケーション
AndroidJavaLibrary AndroidLibrary Bind="false" アプリケーションまたはクラス ライブラリ
EmbeddedJar AndroidLibrary 該当なし プロジェクトをバインドする
EmbeddedReferenceJar AndroidLibrary Bind="false" プロジェクトをバインドする
InputJar AndroidLibrary Pack="false" プロジェクトをバインドする
LibraryProjectZip AndroidLibrary 該当なし プロジェクトをバインドする

C# バインディングを含めるのに関心がない .aar ファイルまたは .jar ファイルを検討してください。 これは、C# から呼び出す必要のない Java または Kotlin の依存関係がある場合に一般的です。 この場合、Bind のメタデータを false に設定できます。 既定では、ファイルは既定のワイルドカードによって取得されます。 Update 属性を使用して Bind メタデータを設定することもできます。

<ItemGroup>
  <AndroidLibrary Update="foo.jar" Bind="false">
</ItemGroup>

Android クラス ライブラリ プロジェクトでは、結果の NuGet パッケージ内の .jar ファイルがそのまま再配布されます。 Android アプリケーション プロジェクトでは、結果の .apk ファイルまたは .aab ファイルに .jar ファイルが含まれます。 この Java ライブラリの C# バインドも含まれていません。

埋め込み JAR/AAR ファイル

Xamarin.Android では、Java の .jar または .aar が、頻繫に埋め込みリソースとしてバインディング .dll に埋め込まれることがありました。 ただし、各 .dll を開いて Java コードをスキャンする必要があるため、ビルドが遅くなります。 見つかった場合は、使用するディスクに抽出する必要があります。

.NET では、Java コードは .dll に埋め込まれなくなりました。 アプリのビルド プロセスでは、参照される .dll と同じディレクトリ内で見つかった .jar ファイルまたは .aar ファイルが自動的に組み込まれます。

プロジェクトが <PackageReference> または <ProjectReference> を介してバインディングを参照している場合は、すべてが機能するため、追加の考慮事項は必要ありません。 ただし、プロジェクトが <Reference> 経由でバインディングを参照する場合、.jar/.aar.dll の隣に配置する必要があります。 つまり、次の参照の場合:

<Reference Include="MyBinding.dll" />

次の例のようなディレクトリは機能しません。

lib/
    MyBinding.dll

代わりに、ディレクトリにはネイティブ コードも含まれている必要があります。

lib/
    MyBinding.dll
    mybinding.jar

移行に関する注意事項

Java に対応するバインディングの生成に役立つ新しい機能がいくつか既定で設定されています。 ただし、既存のバインディング プロジェクトを移行する場合、これらの機能によって、既存のバインディングと API との互換性のないバインディングが作成される場合があります。 互換性を維持するには、これらの新機能を無効にするか、変更することができます。

インターフェイス定数

従来、C# では interface で定数を宣言することは許可されていません (Java では一般的なパターンです)。

public interface Foo {
     public static int BAR = 1;
}

このパターンは、以前は定数を含む代替の class を作成することでサポートされていました。

public abstract class Foo : Java.Lang.Object
{
   public static int Bar = 1;
}

C# 8 では、これらの定数は interface に配置されます。

public interface IFoo
{
    public static int Bar = 1;
}

ただし、これは、既存のコードが依存する可能性がある代替クラスが生成されなくなることを意味します。

プロジェクト ファイルで $(AndroidBoundInterfacesContainConstants) プロパティを false に設定すると、従来の動作に戻ります。

入れ子になったインターフェイスの種類

従来、C# では入れ子になった型を interface で宣言することは許可されていません (Java では許可されています)。

public interface Foo {
     public class Bar { }
}

このパターンは、インターフェイスと入れ子になった型名で構成される生成された名前を持つ最上位の型に、入れ子になった型を移動することでサポートされていました。

public interface IFoo { }

public class IFooBar : Java.Lang.Object { }

C# 8 では、入れ子になった型を interface に配置できます。

public interface IFoo
{
    public class Bar : Java.Lang.Object { }
}

ただし、これは、既存のコードが依存する可能性がある最上位クラスが生成されなくなることを意味します。

プロジェクト ファイルで $(AndroidBoundInterfacesContainTypes) プロパティを false に設定すると、従来の動作に戻ります。

ハイブリッド アプローチを使用する場合、たとえば、既存の入れ子になった型を最上位レベルの型に移動したまま、後で入れ子になった型を入れ子にできるようにするには、metadata を使用して interface レベルでこれを指定し、unnest 属性を設定できます。 これを true に設定すると、入れ子になった型が "入れ子になっていない" ようになります (従来の動作)。

<attr path="/api/package[@name='my.package']/interface[@name='Foo']" name="unnest">true</attr>

これを false に設定すると、入れ子になった型が interface で入れ子になったままなります(.NET の動作)。

<attr path="/api/package[@name='my.package']/interface[@name='Foo']" name="unnest">false</attr>

この方法を使用すると、$(AndroidBoundInterfacesContainTypes) プロパティを true のままにして、現在入れ子になった型の interface ごとに unnesttrue に設定できます。 これらは常に最上位の型のままですが、後で導入される新しい入れ子になった型はすべて入れ子になります。

静的および既定のインターフェイス メンバー (DIM)

従来、C# ではインターフェイスに static メンバーと default メソッドを含めることはできません。

public interface Foo {
  public static void Bar () { ... }
  public default void Baz () { ... }
}

インターフェイスの静的メンバーは、兄弟 class に移動することによってサポートされています。

public interface IFoo { }

public class Foo
{
    public static void Bar () { ... }
}

default インターフェイス メソッドは必須ではなく、このインターフェイス メソッドをサポートする C# 構造がなかったため、従来はバインドされていませんでした。

C# 8 では、staticdefault メンバーがインターフェイスでサポートされ、Java インターフェイスをミラーリングします。

public interface IFoo
{
    public static void Bar () { ... }
    public default void Baz () { ... }
}

ただし、これは、static メンバーを含む代替の兄弟 class が生成されなくなることを意味します。

プロジェクト ファイルで $AndroidBoundInterfacesContainStaticAndDefaultInterfaceMethods プロパティを false に設定すると、従来の動作に戻ります。

null 許容参照型

Xamarin.Android 11.0 に Null 許容参照型 (NRT) のサポートが追加されました。 NRT のサポートは、標準の .NET メカニズムを使用して有効にできます。

<PropertyGroup>
  <Nullable>enable</Nullable>
</PropertyGroup>

.NET の既定値は disable なので、同じことが Xamarin.Android プロジェクトにも当てはまります。

Resource.designer.cs

Xamarin.Android の Java バインド プロジェクトでは、Resource.designer.cs ファイルの生成をサポートしていませんでした。 .NET では、バインド プロジェクトはクラス ライブラリに過ぎないため、このファイルが生成されます。 既存のプロジェクトを移行する場合、これは破壊的変更になる可能性があります。

この変更によるエラーの 1 つの例は、バインドによってルート名前空間に Resource という名前のクラスが生成される場合です。

error CS0101: The namespace 'MyBinding' already contains a definition for 'Resource'

または、AndroidX の場合は、androidx.window/window-extensions.csproj など、名前に - が含まれるプロジェクト ファイルがあります。 この結果、ルート名前空間が window-extensions になり、Resource.designer.cs の C# が無効になります。

error CS0116: A namespace cannot directly contain members such as fields, methods or statements
error CS1514: { expected
error CS1022: Type or namespace definition, or end-of-file expected

Resource.designer.cs の生成を無効にするには、プロジェクト ファイルで $(AndroidGenerateResourceDesigner) プロパティを false に設定します。

<PropertyGroup>
  <AndroidGenerateResourceDesigner>false</AndroidGenerateResourceDesigner>
</PropertyGroup>