6

我在 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 的文档中,我已经阅读了有关 Java 到托管互操作的信息,这似乎正是我正在寻找的。如果我理解正确的话,当 Mono for Android 应用程序编译时,它会生成称为 Android 可调用包装器 (ACW) 的 Java 类。然后我应该能够从基于 Java 的 Android 应用程序调用这些 ACW 上的方法。

问题是,我究竟如何从基于 Java 的 Android 应用程序中引用已编译的 Mono for Android 应用程序( apk文件)?

这就是我现在陷入困境并且无法找到任何具体示例的地方。SO(这个这个)和一些博文也有类似的问题,但它们只是归结为“使用 ACW”。但具体如何?也许我在这里遗漏了一些明显的东西,因为我不是 Android 人。

我尝试的是动态加载我从 Mono for Android apk中提取的dex文件。我只是将它放在存储卡上,然后尝试使用基于 Java 的 Android 应用程序来加载它(我已经关注了这篇博文)。找到了 ACW 类,但是当我尝试创建它的实例时,出现以下错误:DexClassLoader

未找到本机 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: 我现在在这里读到,应该可以从 Java 直接启动用 Mono for Android 编写的活动。我仍然不清楚如何从 Java 和谷歌搜索中引用 Mono for Android 并没有产生相关的命中。现在真的难住了。

4

1 回答 1

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,可调用的包装器允许两者来回通信。因此,即使是 Android 的 Mono 类库仍然依赖于 Mono 运行时,因此您不能独立于该运行时使用它。

此图显示了架构的外观,以及运行时如何相互关联:

适用于 Android 架构的 Mono

希望这有助于澄清事情!

于 2012-08-31T18:35:59.790 回答