Wrapper chiamabili Android per Xamarin.Android
Gli ACL (Callable Wrapper) Android sono necessari ogni volta che il runtime Android richiama codice gestito. Questi wrapper sono necessari perché non è possibile registrare classi con ART (runtime Android) in fase di esecuzione. (In particolare, il La funzione JNI DefineClass() non è supportata dal runtime Android.} I wrapper chiamabili Android costituiscono quindi la mancanza di supporto per la registrazione dei tipi di runtime.
Ogni volta cheoverridden
il codice Android deve eseguire un virtual
metodo di interfaccia o implementato nel codice gestito, Xamarin.Android deve fornire un proxy Java in modo che questo metodo venga inviato al tipo gestito appropriato. Questi tipi di proxy Java sono codice Java con la "stessa" classe di base e l'elenco di interfacce Java del tipo gestito, implementando gli stessi costruttori e dichiarando eventuali metodi di interfaccia e classe base sottoposti a override.
I wrapper chiamabili Android vengono generati dal programma monodroid.exe durante il processo di compilazione: vengono generati per tutti i tipi che (direttamente o indirettamente) ereditano Java.Lang.Object.
Denominazione del wrapper chiamabile Android
I nomi dei pacchetti per Android Callable Wrapper si basano sul md5SUM del nome completo dell'assembly del tipo esportato. Questa tecnica di denominazione consente di rendere disponibile lo stesso nome di tipo completo da assembly diversi senza introdurre un errore di creazione del pacchetto.
A causa di questo schema di denominazione MD5SUM, non è possibile accedere direttamente ai tipi in base al nome. Ad esempio, il comando seguente adb
non funzionerà perché il nome my.ActivityType
del tipo non viene generato per impostazione predefinita:
adb shell am start -n My.Package.Name/my.ActivityType
Se si tenta di fare riferimento a un tipo in base al nome, è anche possibile che vengano visualizzati errori simili al seguente:
java.lang.ClassNotFoundException: Didn't find class "com.company.app.MainActivity"
on path: DexPathList[[zip file "/data/app/com.company.App-1.apk"] ...
Se è necessario l'accesso ai tipi in base al nome, è possibile dichiarare un nome per tale tipo in una dichiarazione di attributo. Ad esempio, di seguito è riportato il codice che dichiara un'attività con il nome My.ActivityType
completo :
namespace My {
[Activity]
public partial class ActivityType : Activity {
/* ... */
}
}
La ActivityAttribute.Name
proprietà può essere impostata per dichiarare in modo esplicito il nome di questa attività:
namespace My {
[Activity(Name="my.ActivityType")]
public partial class ActivityType : Activity {
/* ... */
}
}
Dopo aver aggiunto questa impostazione di proprietà, my.ActivityType
è possibile accedere in base al nome dal codice esterno e dagli adb
script. L'attributo Name
può essere impostato per molti tipi diversi, tra cui Activity
, Application
Service
, BroadcastReceiver
, e ContentProvider
:
- ActivityAttribute.Name
- ApplicationAttribute.Name
- ServiceAttribute.Name
- BroadcastReceiverAttribute.Name
- ContentProviderAttribute.Name
La denominazione ACW basata su MD5SUM è stata introdotta in Xamarin.Android 5.0. Per altre informazioni sulla denominazione degli attributi, vedere RegisterAttribute.
Implementazione di interfacce
In alcuni casi potrebbe essere necessario implementare un'interfaccia Android, ad esempio Android.Content.IComponentCallbacks.
Poiché tutte le classi e l'interfaccia Android estendono l'interfaccia Android.Runtime.IJavaObject , sorge la domanda: come implementare IJavaObject
?
La domanda è stata risolta in precedenza: il motivo per cui tutti i tipi Android devono implementare IJavaObject
è in modo che Xamarin.Android abbia un wrapper chiamabile Android da fornire ad Android, ad esempio un proxy Java per il tipo specificato. Poiché monodroid.exe cerca Java.Lang.Object
solo sottoclassi e Java.Lang.Object
implementa IJavaObject
, la risposta è ovvia: sottoclasse 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...
}
}
Dettagli dell'implementazione
La parte restante di questa pagina fornisce dettagli di implementazione soggetti a modifiche senza preavviso (e viene presentato qui solo perché gli sviluppatori saranno curiosi di cosa sta succedendo).
Ad esempio, data l'origine C# seguente:
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);
}
}
}
Il programma mandroid.exe genererà il wrapper chiamabile Android seguente:
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);
}
Si noti che la classe base viene mantenuta e native
vengono fornite dichiarazioni di metodo per ogni metodo sottoposto a override all'interno del codice gestito.