Android ライブラリ (.aar または .jar) ファイルのバインドが簡単な作業であることはほとんどありません。通常は、Java と .NET の違いに起因する問題を軽減するための追加作業が必要となります。 これらの問題により、.NET for Android は Android ライブラリをバインドできず、ビルド ログにエラー メッセージとして表示されます。 このガイドでは、問題のトラブルシューティングのヒント、より一般的な問題/シナリオ、Android ライブラリを正常にバインドするための考えられる解決策を示します。
既存の Android ライブラリをバインドする場合は、次の点に注意する必要があります。
ライブラリの外部依存関係 – Android ライブラリに必要なすべての Java 依存関係は、NuGet パッケージまたは AndroidLibrary を介して Android 用 .NET プロジェクトに含める必要があります。
Android ライブラリがターゲットとする Android API レベル – Android API レベルを "ダウングレード" することはできません。.NET for Android バインド プロジェクトが Android ライブラリと同じ API レベル (またはそれ以上) をターゲットにしていることを確認してください。
ヒント
Binding Tooling GitHub リポジトリ wiki は優れたリソースであり、特定のケースに役立つ可能性のある追加のトラブルシューティング情報が含まれています。
.NET for Android ライブラリのバインドに関する問題をトラブルシューティングする最初の手順は、 診断 MSBuild 出力を有効にすることです。 診断出力を有効にした後、.NET for Android バインド プロジェクトをリビルドし、ビルド ログを調べて、問題の原因に関する手掛かりを見つけます。
また、Android ライブラリを逆コンパイルし、.NET for Android がバインドしようとしている型とメソッドを調べることも役立ちます。 これについては、このガイドで後ほど詳しく説明します。
Android ライブラリの逆コンパイル
Java のクラスと Java クラスのメソッドを調べると、ライブラリのバインドに役立つ有用な情報が得られます。 JD-GUI は、JAR に含まれる CLASS ファイルから Java ソース コードを表示できるグラフィカル ユーティリティです。
Android ライブラリを逆コンパイルするには、Java 逆コンパイラで .JAR ファイルを開きます。 ライブラリが の場合。AAR ファイルでは、Java ソース コードはアーカイブ ファイルの classes.jar エントリに含まれます。 JD-GUI を使用した Picasso JAR の解析のサンプル スクリーンショットを次に示します。
Android ライブラリを逆コンパイルしたら、ソース コードを調べます。 一般的に言って、次の点を探してください。
難読化の特性を持つクラス – 難読化されたクラスの特性は次のとおりです。
- クラス名に $ が含まれ、a$.class のようになります。
- クラス名は、a.class のように小文字の場合は完全に危険にさらされています。
import参照されていないライブラリのステートメント – 参照されていないライブラリを識別し、NuGet から適切なバインドを使用するか、AndroidLibrary のBuild アクションを使用して、それらの依存関係を .NET for Android バインド プロジェクトに追加します。
注
Java ライブラリの逆コンパイルは、現地の法律または Java ライブラリを公開する際のライセンスに基づき、禁止されている場合や法的規制の対象となる場合があります。 Java ライブラリを逆コンパイルしてソース コードを調べる前に、必要に応じて法律専門家に相談してください。
api.xmlを検査する
バインド プロジェクトのビルドの一環として、.NET for Android では xml ファイル名 obj/Debug/api.xmlが生成されます。
このファイルには、.NET for Android がバインドしようとしているすべての Java API の一覧が表示されます。 このファイルの内容は、欠落している型またはメソッドや、重複するバインドの特定に役立ちます。 このファイルの検査は面倒で時間がかかりますが、バインドの問題の考えられる原因に関する手掛かりが得られます。 たとえば、api.xml によって、プロパティから不適切な型が返されていることや、同じマネージド名を共有する 2 つの型が存在することが明らかになります。
既知の問題
このセクションでは、Android ライブラリをバインドしようとしたときに発生する可能性のある一般的なエラー メッセージや症状について説明します。
問題: 生成された出力に C# の型がない。
バインド .dll はビルドされますが、一部の Java の型が欠落しています。または、欠落している型があることを示すエラーによって、生成された C# ソースがビルドされません。
考えられる原因:
このエラーは、次のようないくつかの理由で発生する可能性があります。
バインドされているライブラリが、別の Java ライブラリを参照している可能性があります。 バインドされたライブラリのパブリック API が別のライブラリの型を使用する場合、そのライブラリのマネージド バインドも参照する必要があります。
Java では非パブリック クラスからパブリック クラスを派生できますが、これは .NET ではサポートされていません。 バインド ジェネレーターは非パブリック クラスのバインドを生成しないため、このような派生クラスを正しく生成することはできません。 これを修正するには、Metadata.xml で remove-node を使用してこれらの派生クラスのメタデータ エントリを削除するか、非パブリック クラスをパブリックにするようにメタデータを修正します。 後者の解決策では、C# ソースがビルドされるようにバインドが作成されますが、非パブリック クラスを使用することはできません。
次に例を示します。
<attr path="/api/package[@name='com.some.package']/class[@name='SomeClass']" name="visibility">public</attr>Java ライブラリを難読化するツールは、.NET for Android Binding Generator とその C# ラッパー クラスを生成する機能に干渉する可能性があります。 次のスニペットは、Metadata.xml を更新してクラス名の難読化を解除する方法を示しています。
<attr path="/api/package[@name='{package_name}']/class[@name='{name}']" name="obfuscated">false</attr>
問題: パラメーターの型の不一致が原因で、生成された C# ソースがビルドされない
生成された C# ソースがビルドされません。 オーバーライドされたメソッドのパラメーターの型が一致しません。
考えられる原因:
.NET for Android には、C# バインドの列挙型にマップされるさまざまな Java フィールドが含まれています。 これらによって、生成されたバインディングで型の非互換性が発生する可能性があります。 これを解決するには、バインド ジェネレーターから作成されたメソッド シグネチャを変更して、列挙型を使用する必要があります。 詳細については、 列挙体の作成を参照してください。
問題: カスタム EventArgs 型が重複している
カスタム EventArgs 型の重複が原因でビルドが失敗します。 次のようなエラーが発生します。
error CS0102: The type `Com.Google.Ads.Mediation.DismissScreenEventArgs' already contains a definition for `p0'
考えられる原因:
これは、同じ名前のメソッドを共有する複数の "リスナー" 型のインターフェイスによって、イベントの型の競合が発生するためです。 たとえば、次の例のように 2 つの Java インターフェイスがある場合、ジェネレーターは、DismissScreenEventArgs と MediationBannerListener の両方に対して MediationInterstitialListener を作成するため、エラーが発生します。
// Java:
public interface MediationBannerListener {
void onDismissScreen(MediationBannerAdapter p0);
}
public interface MediationInterstitialListener {
void onDismissScreen(MediationInterstitialAdapter p0);
}
これは、イベントの引数の型の長い名前を回避するための仕様です。 これらの競合を回避するには、何らかのメタデータ変換が必要です。
Transforms\Metadata.xml を編集し、いずれかのインターフェイス (またはインターフェイス メソッド)に argsType 属性を追加します)。
<attr path="/api/package[@name='com.google.ads.mediation']/
interface[@name='MediationBannerListener']/method[@name='onDismissScreen']"
name="argsType">BannerDismissScreenEventArgs</attr>
<attr path="/api/package[@name='com.google.ads.mediation']/
interface[@name='MediationInterstitialListener']/method[@name='onDismissScreen']"
name="argsType">IntersitionalDismissScreenEventArgs</attr>
<attr path="/api/package[@name='android.content']/
interface[@name='DialogInterface.OnClickListener']"
name="argsType">DialogClickEventArgs</attr>
問題: クラスがインターフェイス メソッドを実装していない
生成されたクラスが実装しているインターフェイスに必要なメソッドをそのクラスが実装しないことを示すエラー メッセージが生成されます。 しかし、生成されたコードを見ると、そのメソッドが実装されていることがわかります。
このエラーの例を次に示します。
obj\Debug\generated\src\Oauth.Signpost.Basic.HttpURLConnectionRequestAdapter.cs(8,23):
error CS0738: 'Oauth.Signpost.Basic.HttpURLConnectionRequestAdapter' does not
implement interface member 'Oauth.Signpost.Http.IHttpRequest.Unwrap()'.
'Oauth.Signpost.Basic.HttpURLConnectionRequestAdapter.Unwrap()' cannot implement
'Oauth.Signpost.Http.IHttpRequest.Unwrap()' because it does not have the matching
return type of 'Java.Lang.Object'
考えられる原因:
この問題は、Java メソッドを共変の戻り値の型にバインドしたときに発生します。 この例では、Oauth.Signpost.Http.IHttpRequest.UnWrap() メソッドは Java.Lang.Object を返す必要があります。 しかし、Oauth.Signpost.Basic.HttpURLConnectionRequestAdapter.UnWrap() メソッドの戻り値の型は HttpURLConnection です。 この問題を修正するには、次の 2 つの方法があります。
HttpURLConnectionRequestAdapterの部分クラスの宣言を追加し、IHttpRequest.Unwrap()を明示的に実装します。namespace Oauth.Signpost.Basic { partial class HttpURLConnectionRequestAdapter { Java.Lang.Object OauthSignpost.Http.IHttpRequest.Unwrap() { return Unwrap(); } } }生成された C# コードから共変性を削除します。 これを行うには、Transforms\Metadata.xml に次の変換を追加します。これにより、生成された C# コードの戻り値の型が
Java.Lang.Objectになります。<attr path="/api/package[@name='oauth.signpost.basic']/class[@name='HttpURLConnectionRequestAdapter']/method[@name='unwrap']" name="managedReturn">Java.Lang.Object </attr>
問題: 内部クラス/プロパティでの名前の競合
継承されたオブジェクトの可視性の競合。
Java では、派生クラスの可視性がその親と同じである必要はありません。 Javaが修正してくれます。 C# では、これは明示的である必要があるため、階層内のすべてのクラスの可視性が適切であることを確認する必要があります。 次の例は、Java パッケージ名を com.evernote.android.job から Evernote.AndroidJob に変更する方法を示しています。
<!-- Change the visibility of a class -->
<attr path="/api/package[@name='namespace']/class[@name='ClassName']" name="visibility">public</attr>
<!-- Change the visibility of a method -->
<attr path="/api/package[@name='namespace']/class[@name='ClassName']/method[@name='MethodName']" name="visibility">public</attr>
問題: バインドに必要な .so ライブラリが読み込まれていない
一部のバインド プロジェクトは、.so ライブラリの機能に依存している場合があります。 .NET for Android では、 .so ライブラリが自動的に読み込まれていない可能性があります。 ラップされた Java コードが実行されると、.NET for Android は JNI 呼び出しを行うことができず、エラー メッセージ java.lang.UnsatisfiedLinkError: Native メソッド not found: がアプリケーションの logcat out に表示されます。
これを解決するには、Java.Lang.JavaSystem.LoadLibrary を呼び出して .so ライブラリを手動で読み込みます。 たとえば、.NET for Android プロジェクトに共有ライブラリ libpocketsphinx_jni.soEmbeddedNativeLibrary のビルド アクションが含まれている場合、次のスニペット (共有ライブラリを使用する前に実行) によって、 .so ライブラリが読み込まれます。
Java.Lang.JavaSystem.LoadLibrary("pocketsphinx_jni");