共用方式為


架構

Xamarin.Android 應用程式會在Mono執行環境中執行。 此執行環境會與 Android 執行時間 (ART) 虛擬機並存執行。 這兩個運行時間環境都會在 Linux 核心之上執行,並將各種 API 公開給使用者程式代碼,讓開發人員存取基礎系統。 Mono 運行時間是以 C 語言撰寫。

您可以使用 SystemSystem.IOSystem.Net 和 .NET 類別庫的其餘部分來存取基礎 Linux 操作系統設施。

在 Android 上,大部分的系統設施,例如音訊、圖形、OpenGL 和 Telephony 都無法直接提供給原生應用程式,它們只會透過位於其中一個 Java.* 命名空間或 Android.* 命名空間的 Android 運行時間 Java API 公開。 架構大致如下:

Diagram of Mono and ART above the kernel and below .NET/Java + bindings

Xamarin.Android 開發人員會呼叫他們知道的 .NET API 來存取作業系統中的各種功能,或使用 Android 命名空間中公開的類別,以提供 Android 運行時間所公開之 Java API 的網橋。

如需 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 呼叫 Managed 程式代碼。

Android 可呼叫包裝函式

  • Android 可呼叫包裝函 式是 JNI 網橋,可在 Android 運行時間需要叫用 Managed 程式代碼時使用。 Android 可呼叫包裝函式是如何覆寫虛擬方法,以及實作 Java 介面的方式。 如需詳細資訊, 請參閱 Java 整合概觀 檔。

Managed 可呼叫包裝函式

Managed 可呼叫包裝函式是 JNI 網橋,可在任何時間使用 Managed 程式代碼來叫用 Android 程式代碼,並提供覆寫虛擬方法和實作 Java 介面的支援。 整個 Android.* 和相關命名空間都是透過 .jar系結產生的 Managed 可呼叫包裝函式。 受控可呼叫包裝函式負責在 Managed 和 Android 類型之間轉換,並透過 JNI 叫用基礎 Android 平臺方法。

每個建立的 Managed 可呼叫包裝函式都會保存 Java 全域參考,可透過 Android.Runtime.IJavaObject.Handle 屬性存取。 全域參考可用來提供Java實例與受控實例之間的對應。 全域參考是有限的資源:模擬器一次只允許有 2000 個全域參考存在,而大部分的硬體一次允許超過 52,000 個全域參考存在。

若要追蹤建立和終結全域參考的時間,您可以將debug.mono.log系統屬性設定為包含 gref

全域參考可以藉由在Managed可呼叫包裝函式上呼叫 Java.Lang.Object.Dispose() 來明確釋放。 這會移除 Java 實例與受控實例之間的對應,並允許收集 Java 實例。 如果從 Managed 程式代碼重新存取 Java 實例,將會為其建立新的 Managed 可呼叫包裝函式。

如果實例在線程之間不小心共用,則處置 Managed 可呼叫包裝函式時必須小心,因為處置實例會影響任何其他線程的參考。 為了達到最大安全性,只有 Dispose() 已透過 new 來源配置實例的實例,您 知道 一律配置新的實例,而不是快取的實例,這可能會導致線程之間意外共享實例。

Managed 可呼叫包裝函式子類別

Managed 可呼叫包裝函式子類別是所有「有趣」應用程式特定邏輯可能都存在的地方。 其中包括自定義 Android.App.Activity 子類別(例如 預設專案範本中的 Activity1 類型)。 (具體來說,這些都是任何不包含 RegisterAttribute 自定義屬性或 RegisterAttribute.DoNotGenerateAcw 的 Java.Lang.Object 子類別為 false,這是預設值。

如同 Managed 可呼叫包裝函式,Managed 可呼叫包裝函式子類別也包含全域參考,可透過 Java.Lang.Object.Handle 屬性存取。 就像使用 Managed 可呼叫包裝函式一樣,呼叫 Java.Lang.Object.Dispose()即可明確釋放全域參考。 與 Managed 可呼叫包裝函式不同, 在處置這類實例之前應該非常小心 ,因為 Instance Dispose()-ing 會中斷 Java 實例(Android 可呼叫包裝函式的實例)與受控實例之間的對應。

Java 啟用

從 Java 建立 Android 可呼叫包裝函式 (ACW) 時,ACW 建構函式會導致叫用對應的 C# 建構函式。 例如,MainActivity 的 ACW 將包含預設建構函式,其會叫用 MainActivity 的預設建構函式。 (這是透過 完成的 ACW 建構函式內的 TypeManager.Activate() 呼叫。

另外還有一個建構函式簽章: (IntPtr、JniHandleOwnership) 建構函式。 每當 Java 物件公開至 Managed 程式代碼,而且必須建構 Managed 可呼叫包裝函式來管理 JNI 句柄時,就會叫用 (IntPtr、JniHandleOwnership) 建構函式。 這通常會自動完成。

在 Managed 可呼叫包裝函式子類別上必須手動提供 (IntPtr, JniHandleOwnership) 建構函式的案例有兩種:

  1. Android.App.Application 已子類別化。 應用程式是特殊的;永遠不會叫用預設的 Applicaton 建構函式,而且必須改為提供 (IntPtr、 JniHandleOwnership) 建構函式

  2. 基類建構函式的虛擬方法調用。

請注意,(2)是洩漏的抽象概念。 在 Java 中,如同 C#,從建構函式呼叫虛擬方法一律會叫用最衍生的方法實作。 例如,TextView(Context、AttributeSet、int) 建構函式會叫用虛擬方法 TextView.getDefaultMovementMethod(),該方法系結為 TextView.DefaultMovementMethod 屬性。 因此,如果 LogTextBox 類型是 (1) 子類別 TextView,(2) 會覆寫 TextView.DefaultMovementMethod,而 (3) 會透過 XML 啟動該類別的實例,則會在 ACW 建構函式有機會執行之前叫用覆寫 DefaultMovementMethod 屬性,而且會在 C# 建構函式有機會執行之前發生。

當 ACW LogTextBox 實例第一次進入 Managed 程式代碼時,透過 LogTextView(IntPtr、JniHandleOwnership) 建構函式具現化 LogTextBox,然後在 ACW 建構函式執行時叫用相同實例上的 LogTextBox(Context、IAttributeSet、int) 建構函式,即可支援此功能。

事件順序:

  1. 版面配置 XML 會 載入 ContentView

  2. Android 會具現化 Layout 物件圖形,並具現化 monodroid.apidemo.LogTextBox 的實例,這是 LogTextBoxACW 。

  3. monodroid.apidemo.LogTextBox 建構函式會執行 android.widget.TextView 建構函式。

  4. TextView構函式會叫用 monodroid.apidemo.LogTextBox.getDefaultMovementMethod()

  5. monodroid.apidemo.LogTextBox.getDefaultMovementMethod() 會叫用 LogTextBox.n_getDefaultMovementMethod() ,它會叫用 TextView.n_GetDefaultMovementMethod() ,它會叫用 Java.Lang.Object.GetObject<TextView> (handle,JniHandleOwnership.DoNotTransfer)

  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)。

過早處置() 呼叫

JNI 句柄與對應的 C# 實例之間有對應。 Java.Lang.Object.Dispose() 會中斷此對應。 如果 JNI 句柄在對應中斷之後輸入 Managed 程式代碼,它看起來像 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);
    }
}

如果我們建立實例,請處置它,並導致重新建立Managed可呼叫包裝函式:

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) 建構函式,則會 建立類型的新 實例。 因此,實例會顯示為「遺失」所有實例數據,因為它是新的實例。 (請注意,Value 為 null。

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

當您知道不再使用 Java 物件時,只有 Managed 可呼叫包裝函式子類別的 Dispose(), 或子類別未包含任何實例數據 ,而且已提供 (IntPtr, JniHandleOwnership) 建構函式。

應用程式啟動

啟動活動、服務等時,Android 會先檢查是否有進程正在執行以裝載活動/服務/等。如果沒有這類進程存在,則會建立新的進程、讀取AndroidManifest.xml,以及載入和具現化 /manifest/application/@android:name 屬性中指定的類型。 接下來,會具 現化 /manifest/application/provider/@android:name 屬性值的所有類型,並叫用其 ContentProvider.attachInfo%28) 方法。 Xamarin.Android 會藉由新增 mono 來連結至此。MonoRuntimeProvider ContentProvider 在建置程式期間AndroidManifest.xml。 單 聲道。MonoRuntimeProvider.attachInfo() 方法負責將Mono運行時間載入進程。 在此點之前使用Mono的任何嘗試都會失敗。 ( 注意:這就是為什麼類型哪些子類別 Android.App.Application 需要提供 (IntPtr, JniHandleOwnership) 建構函式,因為應用程式實例是在可以初始化 Mono 之前建立的。

完成程式初始化之後, AndroidManifest.xml 會查閱 以尋找要啟動之活動/服務/等的類別名稱。 例如, 會使用 /manifest/application/activity/@android:name 屬性 來判斷要載入之 Activity 的名稱。 針對 [活動],此類型必須繼承 android.app.Activity。 指定的類型是透過 Class.forName() 載入(這需要類型為 Java 類型,因此 Android 可呼叫包裝函式),然後具現化。 建立 Android 可呼叫包裝函式實例會觸發建立對應 C# 類型的實例。 Android 接著會叫 用 Activity.onCreate(Bundle) ,這會導致叫用對應的 Activity.OnCreate(Bundle), 而您即將參加比賽。