Architecture

Xamarin.Android アプリケーションは Mono 実行環境内で実行されます。 この実行環境は、Android ランタイム (ART) 仮想マシンと並行して実行されます。 どちらのランタイム環境も Linux カーネル上で実行され、開発者が基になるシステムにアクセスできるさまざまな API をユーザー コードに公開します。 Mono ランタイムは C 言語で記述されます。

システムSystem.IOSystem.Net、その他の .NET クラス ライブラリを使用して、基になる Linux オペレーティング システム機能にアクセスできます。

Android では、オーディオ、グラフィックス、OpenGL、テレフォニーなどのほとんどのシステム機能はネイティブ アプリケーションでは直接使用できません。これらは、Java.* 名前空間または Android.* 名前空間のいずれかに存在する Android ランタイム Java API を介してのみ公開されます。 アーキテクチャは大まかに次のようになります。

カーネルの上および .NET/Java + バインドの下にある Mono と ART の図

Xamarin.Android 開発者は、認識している .NET API を呼び出すか (低レベルのアクセスのために) 呼び出すか、Android ランタイムによって公開される Java API へのブリッジを提供する Android 名前空間で公開されているクラスを使用して、オペレーティング システムのさまざまな機能にアクセスします。

Android クラスが Android ランタイム クラスと通信する方法の詳細については、 API 設計 に関するドキュメントを参照してください。

アプリケーション パッケージ

Android アプリケーション パッケージは、 .apk ファイル拡張子を持つ ZIP コンテナーです。 Xamarin.Android アプリケーション パッケージは、通常の Android パッケージと同じ構造とレイアウトを持ち、次の機能が追加されています。

  • アプリケーション アセンブリ (IL を含む) は、アセンブリ フォルダー内に非圧縮で格納されます。 リリースでのプロセスの起動中に 、.apkプロセスに mmap() が組み込まれ、アセンブリはメモリから読み込まれます。 これにより、実行前にアセンブリを抽出する必要がないように、アプリの起動を高速化できます。

  • メモ:Assembly.LocationAssembly.CodeBase などのアセンブリの場所の情報は、リリース ビルドでは利用できません。 これらは個別のファイルシステム エントリとして存在せず、使用可能な場所もありません。

  • Mono ランタイムを含むネイティブ ライブラリは 、.apk 内に存在します。 Xamarin.Android アプリケーションには、目的の Android アーキテクチャ用のネイティブ ライブラリ (armeabi、armeabi-v7ax86 など) が含まれている必要があります。 Xamarin.Android アプリケーションは、適切なランタイム ライブラリが含まれている場合を除き、プラットフォーム上で実行できません。

Xamarin.Android アプリケーションには、Android がマネージド コードを 呼び出せるようにする Android 呼び出し可能ラッパー も含まれています。

Android 呼び出し可能ラッパー

  • Android 呼び出し可能ラッパー は、Android ランタイムがマネージド コードを呼び出す必要がある場合にいつでも使用される JNI ブリッジです。 Android 呼び出し可能ラッパーは、仮想メソッドをオーバーライドし、Java インターフェイスを実装する方法です。 詳細については、 Java 統合の概要 に関するドキュメントを参照してください。

マネージド呼び出し可能ラッパー

マネージド呼び出し可能ラッパーは JNI ブリッジであり、マネージド コードが Android コードを呼び出し、仮想メソッドのオーバーライドと Java インターフェイスの実装をサポートするために必要な場合にいつでも使用されます。 Android.* と関連する名前空間全体は、.jar バインディングを介して生成されたマネージド呼び出し可能ラッパーです。 マネージド呼び出し可能ラッパーは、マネージド型と Android 型の間で変換し、JNI を介して基になる Android プラットフォーム メソッドを呼び出す役割を担います。

作成された各マネージド呼び出し可能ラッパーには、 Android.Runtime.IJavaObject.Handle プロパティを介してアクセスできる Java グローバル参照が保持されます。 グローバル参照は、Java インスタンスとマネージド インスタンスの間のマッピングを提供するために使用されます。 グローバル参照は限られたリソースです。エミュレーターでは一度に 2000 個のグローバル参照しか存在できませんが、ほとんどのハードウェアでは一度に 52,000 を超えるグローバル参照を存在できます。

グローバル参照がいつ作成および破棄されるかを追跡するには、 debug.mono.log システム プロパティに gref を含める設定を行います。

グローバル参照を明示的に解放するには、マネージド呼び出し可能ラッパーで Java.Lang.Object.Dispose() を呼び出します。 これにより、Java インスタンスとマネージド インスタンス間のマッピングが削除され、Java インスタンスを収集できるようになります。 Java インスタンスにマネージド コードから再アクセスすると、新しいマネージド呼び出し可能ラッパーが作成されます。

インスタンスを誤ってスレッド間で共有できる場合は、マネージド呼び出し可能ラッパーを破棄するときに注意が必要です。インスタンスを破棄すると、他のスレッドからの参照に影響が出ます。 最大限の安全性を確保するために、常に新しいインスタンスを割り当てず、キャッシュされたインスタンスを割り当てず、スレッド間でインスタンスが誤って共有される可能性があることがわかっているメソッドを介してnew、またはメソッドから割り当てられたインスタンスのみDispose()

マネージド呼び出し可能ラッパー サブクラス

マネージド呼び出し可能ラッパー サブクラスは、"興味深い" アプリケーション固有のロジックがすべて存在する可能性がある場所です。 これには、カスタム Android.App.Activity サブクラス (既定のプロジェクト テンプレートの Activity1 型など) が含まれます。 (具体的には、RegisterAttribute カスタム属性または RegisterAttribute.DoNotGenerateAcw既定値である false を含まないJava.Lang.Object サブクラスです)。

マネージド呼び出し可能ラッパーと同様に、マネージド呼び出し可能ラッパー サブクラスにも、 Java.Lang.Object.Handle プロパティを介してアクセスできるグローバル参照が含まれています。 マネージド呼び出し可能ラッパーと同様に、 Java.Lang.Object.Dispose() を呼び出すことでグローバル参照を明示的に解放できます。 マネージド呼び出し可能ラッパーとは異なり、インスタンスの Dispose()-ing によって Java インスタンス (Android 呼び出し可能ラッパーのインスタンス) とマネージド インスタンス間のマッピングが中断されるため、このようなインスタンスを破棄する前に細心の注意を払う必要があります。

Java のアクティブ化

Android 呼び出し可能ラッパー (ACW) が Java から作成されると、ACW コンストラクターによって対応する C# コンストラクターが呼び出されます。 たとえば、 MainActivity の ACW には、 MainActivity の既定のコンストラクターを呼び出す既定のコンストラクターが含まれます。 (これは、ACW コンストラクター内の TypeManager.Activate() 呼び出しによって行われます)。

結果のコンストラクターシグネチャは他に 1 つあります。 (IntPtr, JniHandleOwnership) コンストラクター。 (IntPtr、JniHandleOwnership) コンストラクターは、Java オブジェクトがマネージド コードに公開され、JNI ハンドルを管理するためにマネージド呼び出し可能ラッパーを構築する必要がある場合に常に呼び出されます。 これは通常、自動的に行われます。

マネージド呼び出し可能ラッパー サブクラスで (IntPtr、JniHandleOwnership) コンストラクターを手動で指定する必要があるシナリオは 2 つあります。

  1. Android.App.Application はサブクラス化されています。 アプリケーション は特別です。既定の Applicaton コンストラクターは呼び出 されません代わりに (IntPtr、JniHandleOwnership) コンストラクターを指定する必要があります

  2. 基底クラス コンストラクターからの仮想メソッド呼び出し。

(2) はリークの抽象化であることに注意してください。 Java では、C# と同様に、コンストラクターから仮想メソッドを呼び出すと、常に最も派生したメソッドの実装が呼び出されます。 たとえば、TextView(Context, AttributeSet, int) コンストラクターは、TextView.DefaultMovementMethod プロパティとしてバインドされている仮想メソッド TextView.getDefaultMovementMethod()を呼び出します。 したがって、 LogTextBox 型が (1) サブクラス TextView、(2) TextView.DefaultMovementMethod をオーバーライドし、(3) XML を介してそのクラスのインスタンスをアクティブ化 した場合、オーバーライドされた DefaultMovementMethod プロパティは、ACW コンストラクターが実行される前に呼び出され、C# コンストラクターが実行される前に発生します。

これは、ACW LogTextBox インスタンスが最初にマネージド コードを入力した後、ACW コンストラクターの実行時に同じインスタンスで LogTextBox(Context, IAttributeSet, int) コンストラクターを呼び出すときに、LogTextView(IntPtr,JniHandleOwnership) コンストラクターを介してインスタンス LogTextBox をインスタンス化することによってサポートされます。

イベントの順序:

  1. レイアウト XML は ContentView に読み込まれます。

  2. Android は Layout オブジェクト グラフをインスタンス化し、 monodroid.apidemo.LogTextBox (LogTextBox 用 ACW) のインスタンスをインスタンス化 します

  3. monodroid.apidemo.LogTextBox コンストラクターは、android.widget.TextView コンストラクターを実行します。

  4. TextView コンストラクターは、monodroid.apidemo.LogTextBox.getDefaultMovementMethod() を呼び出します。

  5. monodroid.apidemo.LogTextBox.getDefaultMovementMethod()java.Lang.Object.GetObject<TextView> (handle, JniHandleOwnership.DoNotTransfer) を呼び出す TextView.n_GetDefaultMovementMethod() を呼び出す LogTextBox.n_getDefaultMovementMethod() を呼び出します。

  6. Java.Lang.Object.GetObject<TextView>()、ハンドル に対応する C# インスタンスが既に存在するかどうかを確認します。 ある場合は、返されます。 このシナリオでは、存在しないため、 Object.GetObject<T>() は作成する必要があります。

  7. Object.GetObject<T>()LogTextBox(IntPtr, JniHandleOwneship) コンストラクターを検索し、それを呼び出し、 ハンドル と作成されたインスタンスの間にマッピングを作成し、作成されたインスタンスを返します。

  8. TextView.n_GetDefaultMovementMethod() はLogTextBox.DefaultMovementMethod プロパティ getter を呼び出します。

  9. コントロールは android.widget.TextView コンストラクターに戻り、実行が終了します。

  10. monodroid.apidemo.LogTextBox コンストラクターが実行され、TypeManager.Activate() が呼び出されます。

  11. LogTextBox(Context, IAttributeSet, int) コンストラクターは、 (7) で作成されたのと同じインスタンスで実行されます。

  12. (IntPtr、 JniHandleOwnership) コンストラクターが見つからない場合は、System.MissingMethodException](xref:System.MissingMethodException) がスローされます。

途中の Dispose() 呼び出し

JNI ハンドルと対応する C# インスタンスの間にはマッピングがあります。 Java.Lang.Object.Dispose() はこのマッピングを中断します。 マッピングが切断された後に JNI ハンドルがマネージド コードに入ると、Java Activation のように見え、 (IntPtr、JniHandleOwnership) コンストラクターがチェックされて呼び出されます。 コンストラクターが存在しない場合は、例外がスローされます。

たとえば、次の Managed Callable Wraper サブクラスを指定します。

class ManagedValue : Java.Lang.Object {

    public string Value {get; private set;}

    public ManagedValue (string value)
    {
        Value = value;
    }

    public override string ToString ()
    {
        return string.Format ("[Managed: Value={0}]", Value);
    }
}

インスタンスの Dispose() を作成し、マネージド呼び出し可能ラッパーを再作成する場合:

var list = new JavaList<IJavaObject>();
list.Add (new ManagedValue ("value"));
list [0].Dispose ();
Console.WriteLine (list [0].ToString ());

プログラムは次の手順で実行されます。

E/mono    ( 2906): Unhandled Exception: System.NotSupportedException: Unable to activate instance of type Scratch.PrematureDispose.ManagedValue from native handle 4051c8c8 --->
System.MissingMethodException: No constructor found for Scratch.PrematureDispose.ManagedValue::.ctor(System.IntPtr, Android.Runtime.JniHandleOwnership)
E/mono    ( 2906):   at Java.Interop.TypeManager.CreateProxy (System.Type type, IntPtr handle, JniHandleOwnership transfer) [0x00000] in <filename unknown>:0
E/mono    ( 2906):   at Java.Interop.TypeManager.CreateInstance (IntPtr handle, JniHandleOwnership transfer, System.Type targetType) [0x00000] in <filename unknown>:0
E/mono    ( 2906):   --- End of inner exception stack trace ---
E/mono    ( 2906):   at Java.Interop.TypeManager.CreateInstance (IntPtr handle, JniHandleOwnership transfer, System.Type targetType) [0x00000] in <filename unknown>:0
E/mono    ( 2906):   at Java.Lang.Object.GetObject (IntPtr handle, JniHandleOwnership transfer, System.Type type) [0x00000] in <filename unknown>:0
E/mono    ( 2906):   at Java.Lang.Object._GetObject[IJavaObject] (IntPtr handle, JniHandleOwnership transfer) [0x00000

サブクラスに (IntPtr、JniHandleOwnership) コンストラクターが含まれている場合、型の 新しい インスタンスが作成されます。 その結果、インスタンスは新しいインスタンスであるため、すべてのインスタンス データを "失う" ように見えます。 (値が null であることに注意してください)。)

I/mono-stdout( 2993): [Managed: Value=]

Java オブジェクトが使用されなくなることがわかっている場合、またはサブクラスにインスタンス データが含まれていないこと、および (IntPtr、JniHandleOwnership) コンストラクターが指定されている場合は、マネージド呼び出し可能ラッパー サブクラスの Dispose() のみ。

アプリケーションの起動

アクティビティ、サービスなど。が起動すると、Android は最初にチェックして、アクティビティ/サービスなどをホストするプロセスが既に実行されているかどうかを確認します。そのようなプロセスが存在しない場合は、新しいプロセスが作成され、AndroidManifest.xmlが読み取られ、/manifest/application/@android:name 属性で指定された型が読み込まれ、インスタンス化されます。 次 に、/manifest/application/provider/@android:name 属性値によって指定されたすべての型がインスタンス化され、 ContentProvider.attachInfo%28) メソッドが呼び出されます。 Xamarin.Android では、mono を追加してこれにフック します。ビルド プロセス中にAndroidManifest.xmlする MonoRuntimeProviderContentProvidermono。MonoRuntimeProvider.attachInfo() メソッドは、Mono ランタイムをプロセスに読み込む役割を担います。 この時点より前の Mono の使用は失敗します。 ( : これは、Mono を初期化する前に Application インスタンスが作成されるため、 サブクラス Android.App.Application(IntPtr、JniHandleOwnership) コンストラクターを提供する必要がある型である理由です)。

プロセスの初期化が完了したら、 を参照して、 AndroidManifest.xml 起動するアクティビティ/サービスなどのクラス名を見つけます。 たとえば、 /manifest/application/activity/@android:name 属性 を使用して、読み込むアクティビティの名前を決定します。 アクティビティの場合、この型は android.app.Activity を継承する必要があります。 指定した型は Class.forName() を介して読み込まれ(型は Java 型である必要があるため、Android 呼び出し可能ラッパー)、インスタンス化されます。 Android Callable Wrapper インスタンスを作成すると、対応する C# 型のインスタンスの作成がトリガーされます。 その後、Android は Activity.onCreate(Bundle) を呼び出します。これにより、対応する Activity.OnCreate(Bundle) が呼び出され、レースに出ます。