本文介绍了Monodroid Javascript回调的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将monodroid与webkit一起使用来创建应用.我在让html页面调用javascript方法时遇到问题,该方法将成为我应用程序中方法的接口.在 http://developer.android.com/guide/webapps上有关于此的教程/webview.html 了解如何在Java中执行此操作,但相同的代码在C#上不起作用.

I'm trying to use monodroid with webkit to create an app. I am having a problem with letting the html page call a javascript method, which would be an interface to a method in my app. There is a tutorial on this at http://developer.android.com/guide/webapps/webview.html for how to do it in java, but the same code does not work on C#.

此交换在从javascript示例中调用monodroid方法链接了几个线程关于使用JNI来解决Monodroid和javascript接口方法的问题,但我一直无法使其工作.

This exchange at call monodroid method from javascript example linked a few threads about using JNI to get around a problem with monodroid and the javascript interface method, but I haven't been able to get it to work.

现在,我正在尝试使用一些代码说明,但没有成功:

Now, I am trying to use some code instructions but to no success:

// Java
class RunnableInvoker {
Runnable r;
public RunnableInvoker (Runnable r) {
this.r = r;
}
// must match the javascript name:
public void doSomething() {
r.run ();
}
}

From C#, you'd create a class that implements Java.Lang.IRunnable:

// C#
class SomeAction : Java.Lang.Object, Java.Lang.IRunnable {
Action a;
public void SomeAction(Action a) {this.a = a;}
public void Run () {a();}
}

Then to wire things up:

// The C# action to invoke
var action = new SomeAction(() => {/* ... */});

// Create the JavaScript bridge object:
IntPtr RunnableInvoker_Class = JNIEnv.FindClass("RunnableInvoker");
IntPtr RunnableInvoker_ctor = JNIEnv.GetMethodID (RunnableInvoker_Class, "<init>", "(Ljava/lang/Runnable;)V");
IntPtr instance = JNIEnv.NewObject(RunnableInvoker_Class, RunnableInvoker_ctor, new JValue (action));

// Hook up WebView to JS object
web_view.AddJavascriptInterface (new Java.Lang.Object(instance, JniHandleOwnership.TransferLocalRef), "Android");

该代码应该能够实现,因此应用程序内html页面上的某人可以单击按钮,调用Java,然后再调用C#.这没有用.

This code is supposed to be able to make it so someone on the html page inside the app can click on a button, invoke the java, which will then invoke C#. This hasnt worked.

我想知道是否有人对问题有一个想法,或者另一个想法,所以我可以使用monodroid让webkit中加载的html按钮调用ac#方法,或者让我的c#代码调用ajavascript方法.

I was wondering if anyone had an idea of what was the problem, or another idea so I can use monodroid to let a html button loaded in webkit to call a c# method, or to be able to have my c# code call a javascript method.

推荐答案

让我们退后一步.您想从JavaScript调用C#代码.如果您不介意就这样眼,那很简单.

Let's take a step back. You want to invoke C# code from JavaScript. If you don't mind squinting just-so, it's quite straightforward.

首先,让我们从布局XML开始:

First, let's start with our Layout XML:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">
    <WebView
            android:id="@+id/web"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
    />
</LinearLayout>

现在我们可以开始使用应用程序了:

Now we can get to app itself:

[Activity (Label = "Scratch.WebKit", MainLauncher = true)]
public class Activity1 : Activity
{
    const string html = @"
<html>
<body>
<p>This is a paragraph.</p>
<button type=""button"" onClick=""Foo.run()"">Click Me!</button>
</body>
</html>";

    protected override void OnCreate (Bundle bundle)
    {
        base.OnCreate (bundle);

        // Set our view from the "main" layout resource
        SetContentView (Resource.Layout.Main);

        WebView view = FindViewById<WebView>(Resource.Id.web);
        view.Settings.JavaScriptEnabled = true;
        view.SetWebChromeClient (new MyWebChromeClient ());
        view.LoadData (html, "text/html", null);
        view.AddJavascriptInterface(new Foo(this), "Foo");
    }
}

Activity1.html 是我们将要显示的HTML内容.唯一有趣的是,我们提供了一个/button/@ onClick 属性,该属性调用JavaScript片段 Foo.run().注意方法名称(运行"),它以小写的"r"开头;我们将在稍后返回.

Activity1.html is the HTML content we're going to show. The only interesting thing is that we provide a /button/@onClick attribute which invokes the JavaScript fragment Foo.run(). Note the method name ("run") and that it starts with a lowercase 'r'; we will return to this later.

还有三件事需要注意:

  1. 我们使用 view.Settings.JavaScriptEnabled = true 启用JavaScript.没有这个,我们就不能使用JavaScript.
  2. 我们使用 MyWebChromeClient 类(稍后定义)的实例调用 view.SetWebChromeClient().这有点货神般的编程":如果我们不提供它,那么事情就不起作用了.我不知道为什么如果改为执行看似等效的 view.SetWebChromeClient(new WebChromeClient()),则会在运行时出现错误:

  1. We enable JavaScript with view.Settings.JavaScriptEnabled=true. Without this, we can't use JavaScript.
  2. We call view.SetWebChromeClient() with an instance of a MyWebChromeClient class (defined later). This is a bit of "cargo-cult programming": if we don't provide it, things don't work; I don't know why. If we instead do the seemingly equivalent view.SetWebChromeClient(new WebChromeClient()), we get an error at runtime:

E/Web Console( 4865): Uncaught ReferenceError: Foo is not defined at data:text/html;null,%3Chtml%3E%3Cbody%3E%3Cp%3EThis%20is%20a%20paragraph.%3C/p%3E%3Cbutton%20type=%22button%22%20onClick=%22Foo.run()%22%3EClick%20Me!%3C/button%3E%3C/body%3E%3C/html%3E:1

这对我也没有意义.

现在,我们需要 MyWebChromeClient 类:

class MyWebChromeClient : WebChromeClient {
}

请注意,它实际上并没有执行任何操作,因此,仅使用 WebChromeClient 实例会导致事情失败,这将更加有趣.:-/

Note that it doesn't actually do anything, so it's all the more interesting that just using a WebChromeClient instance causes things to fail. :-/

最后,我们进入有趣的"位,即 Foo 类,该类在上面与"Foo" JavaScript变量相关联:

Finally, we get to the "interesting" bit, the Foo class which was associated above with the "Foo" JavaScript variable:

class Foo : Java.Lang.Object, Java.Lang.IRunnable {

    public Foo (Context context)
    {
        this.context = context;
    }

    Context context;

    public void Run ()
    {
        Console.WriteLine ("Foo.Run invoked!");
        Toast.MakeText (context, "This is a Toast from C#!", ToastLength.Short)
        .Show();
    }
}

当调用 Run()方法时,它仅显示一条短消息.

It just shows a short message when the Run() method is invoked.

在Android版Mono的构建过程中,为以下应用程序创建了 Android可调用包装器每个 Java.Lang.Object 子类,它声明所有重写的方法和所有已实现的Java接口.这包括上面的 Foo 类,生成了Android Callable Wrapper:

During the Mono for Android build process, Android Callable Wrappers are created for every Java.Lang.Object subclass, which declares all overridden methods and all implemented Java interfaces. This includes the above Foo class, resulting in the Android Callable Wrapper:

package scratch.webkit;

public class Foo
    extends java.lang.Object
    implements java.lang.Runnable
{
    @Override
    public void run ()
    {
        n_run ();
    }

    private native void n_run ();

    // details omitted for clarity
}

当调用 view.AddJavascriptInterface(new Foo(this),"Foo")时,这没有将JavaScript "Foo" 变量与C#类型相关联.这是将JavaScript "Foo" 变量与与C#类型的实例相关联的Android Callable Wrapper实例相关联.(啊,间接...)

When view.AddJavascriptInterface(new Foo(this), "Foo") was invoked, this wasn't associating the JavaScript "Foo" variable with the C# type. This was associating the JavaScript "Foo" variable with an Android Callable Wrapper instance associated with the instance of the C# type. (Ah, indirections...)

现在,我们开始进行前面提到的斜眼看".C# Foo 类实现了 Java.Lang.IRunnable 接口,该接口是 java.lang.Runnable 接口的C#绑定.因此,Android Callable Wrapper声明其实现了 java.lang.Runnable 接口,并声明了 Runnable.run 方法.Android和Android内的JavaScript不会看到"您的C#类型.他们反而看到了Android Callable Wrappers.因此,JavaScript代码没有调用 Foo.Run()(大写的"R"),而是调用了 Foo.run()(小写的"r"),因为Android/JavaScript可以访问的类型声明一个 run()方法,不是一个 Run()方法.

Now we get to the aforementioned "squinting." The C# Foo class implemented the Java.Lang.IRunnable interface, which is the C# binding for the java.lang.Runnable interface. The Android Callable Wrapper thus declares that it implements the java.lang.Runnable interface, and declares the Runnable.run method. Android, and thus JavaScript-within-Android, does not "see" your C# types. They instead see the Android Callable Wrappers. Consequently, the JavaScript code isn't calling Foo.Run() (capital 'R'), it's calling Foo.run() (lowercase 'r'), because the type that Android/JavaScript has access to declares a run() method, not a Run() method.

当JavaScript调用 Foo.run()时,将调用Android Callable Wrapper scratch.webview.Foo.run()方法,这通过JNI会导致执行 Foo.Run() C#方法,这实际上是您首先要做的所有事情.

When JavaScript invokes Foo.run(), then the Android Callable Wrapper scratch.webview.Foo.run() method is invoked which, through the joy that is JNI, results in the execution of the Foo.Run() C# method, which is really all you wanted to do in the first place.

如果您不喜欢使用名为 run()的JavaScript方法,或者想要参数或其他任何东西,您的世界将变得更加复杂(至少直到Android版Mono才开始)4.2和 [导出] 支持).您将需要执行以下两项操作之一:

If you don't like having the JavaScript method named run(), or you want parameters, or any number of other things, your world gets much more complicated (at least until Mono for Android 4.2 and [Export] support). You would need to do one of two things:

  1. 找到一个提供所需名称和签名的现有绑定接口或虚拟类方法.然后覆盖接口的方法/实现,事情看起来与上面的示例非常相似.
  2. 滚动您自己的Java类.向 monodroid邮件列表询问以获取更多详细信息.这个答案就这样了.
  1. Find an existing bound interface or virtual class method that provides the name and signature that you want. Then override the method/implement the interface, and things look fairly similar to the example above.
  2. Roll your own Java class. Ask on the monodroid mailing list for more details. This answer is getting long as it is.

这篇关于Monodroid Javascript回调的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-16 07:04