Android ランタイムでマネージド コードを呼び出すときは、常に Android 呼び出し可能ラッパー (ACW) が必要です。 これらのラッパーが必要なのは、実行時に ART (Android ランタイム) にクラスを登録する方法がないためです (具体的には、Android ランタイムでは JNI DefineClass() 関数はサポートされていません)。 そのため、Android 呼び出し可能ラッパーによって、ランタイム型登録のサポートの欠如が補われます。
マネージド コードで overridden
または実装される virtual
メソッドまたは interface メソッドを Android コードで使用する必要があるときは、Xamarin.Android で Java プロキシを "毎回" 提供して、このメソッドが適切なマネージド型にディスパッチされるようにする必要があります。 これらの Java プロキシ型は、マネージド型と "同じ" 基底クラスと Java インターフェイス リストを持つ Java コードであり、同じコンストラクターを実装し、オーバーライドされた基底クラスとインターフェイス メソッドを宣言します。
Android 呼び出し可能ラッパーは、ビルド プロセスの間に monodroid.exe プログラムによって生成され、(直接的または間接的に) Java.Lang.Object を継承するすべての型に対して生成されます。
Android 呼び出し可能ラッパーの名前付け規則
Android 呼び出し可能ラッパーのパッケージ名は、エクスポートされる型のアセンブリ修飾名の MD5SUM に基づきます。 この名前付け方法により、パッケージ化エラーを発生させることなく、異なるアセンブリで同じ完全修飾名の使用が可能になります。
この MD5SUM 名前付けスキームのため、お使いの型に名前で直接アクセスすることはできません。 たとえば、次の adb
コマンドでは型名 my.ActivityType
が既定で生成されないため、このコマンドは機能しません。
adb shell am start -n My.Package.Name/my.ActivityType
また、型を名前で参照しようとすると、次のようなエラーが表示される可能性があります。
java.lang.ClassNotFoundException: Didn't find class "com.company.app.MainActivity"
on path: DexPathList[[zip file "/data/app/com.company.App-1.apk"] ...
型に名前でアクセスする必要が "ある" 場合は、その型の名前を属性宣言内に宣言できます。 たとえば、次に示すのは、完全修飾名が My.ActivityType
であるアクティビティを宣言するコードです。
namespace My {
[Activity]
public partial class ActivityType : Activity {
/* ... */
}
}
このアクティビティの名前を明示的に宣言するために、ActivityAttribute.Name
プロパティを設定できます。
namespace My {
[Activity(Name="my.ActivityType")]
public partial class ActivityType : Activity {
/* ... */
}
}
このプロパティの設定を追加すると、外部コードと adb
スクリプトから名前を使用して my.ActivityType
にアクセスできます。 Activity
、Application
、Service
、BroadcastReceiver
、ContentProvider
を含むさまざまな型に対して Name
属性を設定できます。
- ActivityAttribute.Name
- ApplicationAttribute.Name
- ServiceAttribute.Name
- BroadcastReceiverAttribute.Name
- ContentProviderAttribute.Name
MD5SUM ベースの ACW の名前付け規則は、Xamarin. Android 5.0 で導入されました。 属性の名前付け規則の詳細については、「RegisterAttribute」を参照してください。
インターフェイスの実装
Android インターフェイス (Android.Content.IComponentCallbacks など) の実装が必要になる場合があります。
Android のすべてのクラスとインターフェイスでは、Android.Runtime.IJavaObject インターフェイスが拡張されるため、IJavaObject
をどのように実装するかという疑問が生じます。
この疑問には上記で答えています。つまり、Android のすべての型で IJavaObject
を実装する必要がある理由は、Android に提供される Android 呼び出し可能ラッパー (つまり、特定の種類の Java プロキシ) を Xamarin.Android に提供できるようにすることです。 monodroid.exe では Java.Lang.Object
サブクラスだけが検索され、Java.Lang.Object
によって IJavaObject
が実装されるため、答えは明らかにサブクラス Java.Lang.Object
になります。
class MyComponentCallbacks : Java.Lang.Object, Android.Content.IComponentCallbacks {
public void OnConfigurationChanged (Android.Content.Res.Configuration newConfig)
{
// implementation goes here...
}
public void OnLowMemory ()
{
// implementation goes here...
}
}
実装の詳細
"この記事の残りの部分で説明する実装の詳細は、予告なしに変更される可能性があります" (開発者が内部の処理に関心があるという理由でのみ、ここに記載されています)。
たとえば、次のような C# ソースについて考えます。
using System;
using Android.App;
using Android.OS;
namespace Mono.Samples.HelloWorld
{
public class HelloAndroid : Activity
{
protected override void OnCreate (Bundle savedInstanceState)
{
base.OnCreate (savedInstanceState);
SetContentView (R.layout.main);
}
}
}
mandroid.exe プログラムでは、次のような Android 呼び出し可能ラッパーが生成されます。
package mono.samples.helloWorld;
public class HelloAndroid
extends android.app.Activity
{
static final String __md_methods;
static {
__md_methods = "n_onCreate:(Landroid/os/Bundle;)V:GetOnCreate_Landroid_os_Bundle_Handler\n" + "";
mono.android.Runtime.register (
"Mono.Samples.HelloWorld.HelloAndroid, HelloWorld, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null", HelloAndroid.class, __md_methods);
}
public HelloAndroid ()
{
super ();
if (getClass () == HelloAndroid.class)
mono.android.TypeManager.Activate (
"Mono.Samples.HelloWorld.HelloAndroid, HelloWorld, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null", "", this, new java.lang.Object[] { });
}
@Override
public void onCreate (android.os.Bundle p0)
{
n_onCreate (p0);
}
private native void n_onCreate (android.os.Bundle p0);
}
基底クラスが保持されており、マネージド コード内でオーバーライドされるメソッドごとに native
メソッド宣言が提供されていることに注意してください。