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