单机器人Javascript回调

单机器人Javascript回调,javascript,xamarin.android,Javascript,Xamarin.android,我正在尝试使用monodroid和webkit来创建一个应用程序。我在让html页面调用javascript方法时遇到了一个问题,javascript方法可能是我应用程序中方法的接口。关于如何在java中实现这一点,at有一个教程,但同样的代码在C#上不起作用 这次交流联系了几个关于使用JNI来解决monodroid和javascript接口方法的问题的线程,但我一直无法让它正常工作 现在,我尝试使用一些代码说明,但没有成功: // Java class RunnableInvoker { Ru

我正在尝试使用monodroid和webkit来创建一个应用程序。我在让html页面调用javascript方法时遇到了一个问题,javascript方法可能是我应用程序中方法的接口。关于如何在java中实现这一点,at有一个教程,但同样的代码在C#上不起作用

这次交流联系了几个关于使用JNI来解决monodroid和javascript接口方法的问题的线程,但我一直无法让它正常工作

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

// 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");
//Java
类RunnableInvoker{
可运行r;
公共RunnableInvoker(RunnableR){
这个。r=r;
}
//必须与javascript名称匹配:
公共无效剂量测定法(){
r、 运行();
}
}
从C#,您将创建一个实现Java.Lang.iRunTable的类:
//C#
类SomeAction:Java.Lang.Object、Java.Lang.iRunTable{
行动a;
public void SomeAction(Action a){this.a=a;}
public void Run(){a();}
}
然后把事情联系起来:
//要调用的C#操作
var action=newsomeaction(()=>{/*…*/});
//创建JavaScript桥对象:
IntPtr RunnableInvoker_Class=JNIEnv.FindClass(“RunnableInvoker”);
IntPtr RunnableInvoker_ctor=JNIEnv.GetMethodID(RunnableInvoker_类,”,“(Ljava/lang/Runnable;)V”);
IntPtr instance=JNIEnv.NewObject(RunnableInvoker_类、RunnableInvoker_向量、新JValue(操作));
//将WebView连接到JS对象
web_view.AddJavascriptInterface(新的Java.Lang.Object(实例,JniHandleOwnership.TransferLocalRef),“Android”);
这段代码应该能够使应用程序中html页面上的某个人可以单击按钮,调用java,然后java将调用C。这不起作用


我想知道是否有人知道问题出在哪里,或者我可以使用monodroid让webkit中加载的html按钮调用c方法,或者让我的c代码调用javascript方法。您希望从JavaScript调用C#代码。如果你不介意这样眯着眼睛,那就很简单了

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


现在我们可以进入应用程序本身:

[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");
    }
}
这对我来说也毫无意义

  • 我们调用
    view.AddJavascriptInterface()
    将JavaScript名称
    “Foo”
    与类
    Foo
    的实例相关联
  • 现在我们需要
    MyWebChromeClient
    类:

    class MyWebChromeClient : WebChromeClient {
    }
    
    请注意,它实际上没有做任何事情,因此更有趣的是,仅使用
    WebChromeClient
    实例就会导致失败-/

    最后,我们得到了“有趣的”位,
    Foo
    类,它与上面的
    “Foo”
    JavaScript变量关联:

    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()
    方法时,它只显示一条短消息

    这是怎么回事 在Mono for Android构建过程中,为每个
    Java.Lang.Object
    子类创建了一个类,它声明了所有重写的方法和所有实现的Java接口。这包括上面的
    Foo
    类,产生了Android可调用包装器:

    package scratch.webkit;
    公开课Foo
    扩展java.lang.Object
    实现java.lang.Runnable
    {
    @凌驾
    公开作废运行()
    {
    n_run();
    }
    私有本机void n_run();
    //为了清楚起见,省略了细节
    }
    
    调用
    view.AddJavascriptInterface(new Foo(this),“Foo”)
    时,这并没有将JavaScript
    “Foo”
    变量与C类型相关联。这是将JavaScript
    “Foo”
    变量与与与C#type实例关联的Android可调用包装器实例相关联。(啊,间接的…)

    现在我们来看前面提到的“斜视”。C#
    Foo
    类实现了
    Java.Lang.irunable
    接口,它是
    Java.Lang.Runnable
    接口的C#绑定。因此,Android可调用包装器声明它实现了
    java.lang.Runnable
    接口,并声明了
    Runnable.run
    方法。Android以及Android中的JavaScript不会“看到”您的C#类型。相反,他们看到的是Android可调用包装器。因此,JavaScript代码没有调用
    Foo.Run()
    (大写'R'),而是调用
    Foo.Run()
    (小写'R'),因为Android/JavaScript有权访问的类型声明了
    Run()
    方法,而不是
    Run()
    方法

    当JavaScript调用
    Foo.run()
    时,会调用Android可调用的包装器
    scratch.webview.Foo.run()
    方法,通过JNI带来的乐趣,它会导致执行
    Foo.run()
    C#方法,这是您最初真正想要做的

    但是我不喜欢跑步! 如果您不喜欢名为
    run()
    的JavaScript方法,或者您想要参数或其他任何东西,您的世界会变得更加复杂(至少在Mono for Android 4.2和
    [Export]
    支持之前是这样)。您需要做以下两件事之一:

  • 查找提供所需名称和签名的现有绑定接口或虚拟类方法。然后重写方法/实现接口,事情看起来与上面的示例非常相似
  • 滚动您自己的Java类。询问更多细节。这个答案越来越长了

  • 我发现在AddJavascriptInterface之后调用LoadData是必要的。此外,通过此更改,不需要分配WebChromeClient。例如,下面的修改版本运行良好:

    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);
    
            SetContentView (Resource.Layout.Main);
    
            WebView view = FindViewById<WebView> (Resource.Id.web);
            view.Settings.JavaScriptEnabled = true;
    
            view.AddJavascriptInterface (new Foo (this), "Foo");
            view.LoadData (html, "text/html", null);
        }
    
        class Foo : Java.Lang.Object, Java.Lang.IRunnable
        {
            Context context;
    
            public Foo (Context context)
            {
                this.context = context;
            }
    
            public void Run ()
            {
                Toast.MakeText (context, "This is a Toast from C#!", ToastLength.Short).Show ();
            }
        }
    }
    
    公共类活动1:活动
    {
    常量字符串html=@”
    这是一段

    点击我! "; 创建时受保护的覆盖无效(捆绑包) { base.OnCreate(bundle); SetContentView(Resource.Layout.Main); WebView=findviewbyd(Resource.Id.web); 不及物动词
    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);
    
            SetContentView (Resource.Layout.Main);
    
            WebView view = FindViewById<WebView> (Resource.Id.web);
            view.Settings.JavaScriptEnabled = true;
    
            view.AddJavascriptInterface (new Foo (this), "Foo");
            view.LoadData (html, "text/html", null);
        }
    
        class Foo : Java.Lang.Object, Java.Lang.IRunnable
        {
            Context context;
    
            public Foo (Context context)
            {
                this.context = context;
            }
    
            public void Run ()
            {
                Toast.MakeText (context, "This is a Toast from C#!", ToastLength.Short).Show ();
            }
        }
    }
    
    // C#
    // !!!
    using Java.Interop; // add link to Mono.Android.Export
    
    public class Activity1 : Activity
    {
        const string html = @"
        <html>
        <body>
        <p>This is a paragraph.</p>
        <button type=""button"" onClick=""Foo.SomeMethod('bla-bla')"">Click Me!</button>
        </body>
        </html>";
    
        class Foo : Java.Lang.Object // do not need Java.Lang.IRunnable 
        {
            Context context;
    
            public Foo (Context context)
            {
                this.context = context;
            }
    
            [Export] // !!! do not work without Export
            [JavascriptInterface] // This is also needed in API 17+
            public string SomeMethod(string param)
            {
                Toast.MakeText (context, "This is a Toast from C#!" + param, ToastLength.Short).Show ();
            }
        }
    
        protected override void OnCreate (Bundle bundle)
        {
            base.OnCreate (bundle);
    
            SetContentView (Resource.Layout.Main);
    
            WebView view = FindViewById<WebView> (Resource.Id.web);
            view.Settings.JavaScriptEnabled = true;
    
            view.AddJavascriptInterface (new Foo (this), "Foo");
            view.LoadData (html, "text/html", null);
        }
    }
    
    private class AlertableWebChromeClient : Android.Webkit.WebChromeClient
    {
        private const string XAMARIN_DATA_ALERT_TAG = "XAMARIN_DATA\0";
    
        public override bool OnJsAlert(Android.Webkit.WebView view, string url, string message, Android.Webkit.JsResult result)
        {
            if (message.StartsWith(XAMARIN_DATA_ALERT_TAG))
            {
                //Parse 'message' for data - it can be XML, JSON or whatever
    
                result.Confirm();
                return true;
            }
            else
            {
                return base.OnJsAlert(view, url, message, result);
            }
        }
    }
    
    if (window.xamarin_alert_callback != null) {
        alert("XAMARIN_DATA\0" + JSON.stringify(data_object));
    }
    
    Android.Net.UrlQuerySanitizer.IValueSanitizer
    
    string Sanitize(string value);