如何在Android应用程序中调用Mono for Android类?

时间:2012-08-29 08:18:46

标签: c# java android interop xamarin.android

我在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 onethis 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和谷歌搜索没有相关的命中。真的很难过。

1 个答案:

答案 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运行时,因此您无法独立于该运行时使用它。

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

Mono for Android architecture

希望这有助于清理事情!