我在Mono for Android项目中创建了一个相当简单的Activity:
[Activity(Label = "AndroidApplication1", MainLauncher = true, Icon = "@drawable/icon")]
public class Activity1 : Activity
{
private string value = "intitial";
[Export]
public string GetString()
{
return value;
}
[Export]
public void SetString(string newValue)
{
value = newValue;
}
}
活动应仅作为概念验证,因此其简单性。现在,我希望能够从基于Java的“普通”Android应用程序调用方法GetString()。
在Xamarin's docs中,我已经阅读了Java to Managed interop,这似乎正是我正在寻找的。如果我理解正确,当Mono for Android应用程序编译时,它会生成称为Android Callable Wrappers(ACW)的Java类。然后我应该可以从基于Java的Android应用程序调用这些ACW上的方法。
问题是,我如何从基于Java的Android应用程序中引用编译的Mono for Android应用程序( apk 文件)?
这是我现在被困住的地方,无法找到任何具体的例子。 SO(this one和this one)和一些博客上也有类似的问题,但它们只是归结为“使用ACW”。 但究竟是怎么回事?也许我错过了一些明显的东西,不是Android家伙。
我尝试过的是动态加载我从我的Mono for Android apk 中拉出的 dex 文件。我只是将它放在存储卡上,然后尝试使用基于Java的Android应用程序中的DexClassLoader
加载它(我已按照此blog post)。找到了ACW类,但是当我尝试创建它的实例时,我收到了以下错误:
找不到本机Lmno / android / Runtime; .register(Ljava / lang / String; Ljava / lang / Class; Ljava / lang / String;)
的实现
我想我必须以某种方式将Mono for Android运行时包含到基于Java的应用程序中,但我不知道如何。
修改 这是我尝试使用
加载 dex 的代码DexClassLoader cl = new DexClassLoader(dexInternalStoragePath.getAbsolutePath(),
optimizedDexOutputPath.getAbsolutePath(),
null,
getClassLoader());
try {
Class<?> classActivity1 = cl.loadClass("androidapplication1.Activity1");
// the following line throws the exception
Object a = classActivity1.newInstance();
Method getStringMethod = classActivity1.getMethod("GetString");
Object result = getStringMethod.invoke(angel);
result = null;
} catch (Exception e) {
e.printStackTrace();
}
EDIT2: 我现在正在阅读here,应该可以直接从Java开始用Mono for Android编写的活动。我仍然不清楚如何从Java引用Android的Mono和谷歌搜索没有相关的命中。真的很难过。
答案 0 :(得分:5)
如果我正确理解你想要做什么,这实际上是不可能的。正如您所暗示的错误消息所示,Mono for Android应用程序中的Activity依赖于Mono运行时才能正常运行。在这种情况下,可调用包装器本身并没有用,因为它只是一个调用Mono运行时的瘦Java包装器类。如果在构建项目后查看obj / Debug / android / src文件夹,您实际上可以自己查看生成的可调用包装器。例如:
package androidapplication9;
public class Activity1
extends android.app.Activity
implements
mono.android.IGCUserPeer
{
static final String __md_methods;
static {
__md_methods =
"n_onCreate:(Landroid/os/Bundle;)V:GetOnCreate_Landroid_os_Bundle_Handler\n" +
"";
mono.android.Runtime.register ("AndroidApplication9.Activity1, AndroidApplication9, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", Activity1.class, __md_methods);
}
public Activity1 ()
{
super ();
if (getClass () == Activity1.class)
mono.android.TypeManager.Activate ("AndroidApplication9.Activity1, AndroidApplication9, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", "", this, new java.lang.Object[] { });
}
public void onCreate (android.os.Bundle p0)
{
n_onCreate (p0);
}
private native void n_onCreate (android.os.Bundle p0);
java.util.ArrayList refList;
public void monodroidAddReference (java.lang.Object obj)
{
if (refList == null)
refList = new java.util.ArrayList ();
refList.add (obj);
}
public void monodroidClearReferences ()
{
if (refList != null)
refList.clear ();
}
}
也就是说,由于Android的工作方式,您可以让Java应用程序启动一个在Mono for Android应用程序中定义的活动,就像您启动外部Java活动一样。当然,这依赖于正在安装的两个应用程序,但会导致Mono for Android应用程序和Mono运行时实际启动以运行该活动。
修改强>
更新以回答您在评论中提出的问题。 ExportAttribute
基本上只是让您对ACW的生成方式有了更多的控制,允许您指定ACW中应该存在特定的方法或字段以及它应该具有的名称。当你想在布局中使用像android:onClick属性这样的东西时,这很有用,例如,默认情况下,ACW不包含你想要引用的方法。
在Mono for Android应用程序的上下文之外,你无法使用ACW,因为Mono运行时不会出现。用C#编写的代码在Mono运行时之上执行,在编译期间不会在幕后翻译成Java或类似的东西。在运行时,有两个并行的运行时,Dalvik(Android的运行时)和Mono,并且可调用的包装器允许两者来回通信。因此,即使Mono for Android类库仍然依赖于Mono运行时,因此您无法独立于该运行时使用它。
此图显示了体系结构的外观,以及运行时如何相互关联:
希望这有助于清理事情!