Javascript回调函数传递给Android

Posted

技术标签:

【中文标题】Javascript回调函数传递给Android【英文标题】:Javascript Callback function pass to Android 【发布时间】:2011-10-24 13:24:19 【问题描述】:

我有一个用 Java 实现的 javascript 接口,由我在 webview 中加载的 javascript 代码调用。

JS Inside webview:

android.myFunction(function(data)
    console.log(data);
);

Java:

public class JavaScriptInterface 

    Context context;
    WebView webView;

    JavaScriptInterface(Context c, WebView w) 
        context = c;
        webView = w;
    

    public void myFunction(String callback) 
        //when I log callback, it is "undefined"
         String someData = "Yay for data";
         String js =
             "javascript:(function()  "
                 + "var callback = " + callback + ";"
                 + "callback('" + someData + "');"
             + ")()";
        webView.loadUrl(js);
    

webview 加载的字符串最终是:

javascript:(function() var callback = undefined; undefined();)()

我有几个想法:

一个。在 JS 中将回调构建为字符串。

b.在将回调传递给 Android.myFunction() 之前调用回调的 toString();

我的问题是,最好的方法是什么?我希望能够将对象传递给 Android,它神奇地工作。显然,情况并非如此。 ;) 下一个最好的方法是什么?

【问题讨论】:

将函数创建为字符串听起来像是纯粹的、蒸馏过的邪恶。 我完全同意。不幸的是,我很确定这是我在这种情况下的唯一选择。此外,我不确定是否有更好的方法来解决这个问题。我想要回调的灵活性,而不是仅仅写出一个函数 Android 端来处理回调。 @missingno 通过字符串操作创建函数有什么问题?我假设 Jonathan 会事先验证字符串,所以这看起来很合理。 【参考方案1】:

我遇到了类似的问题:我想在网络应用中使用原生 Android 确认对话框。这意味着我必须使用确认对话框的结果从 Android 回调到 Javascript 部分。

我解决了这个问题:

function foo() 
    // user confirmation needed
    var dataString = <encode data into string>;
    MyClient.showConfirmationDialog('myCallBackFunction', dataString, 'A title', 'A message');

上面的代码调用了 Android javascript 接口(见下文)。 javascript 提供了回调方法myCallbackFunction(),其名称作为参数传递给Android(连同数据字符串、标题和消息)。回调函数如下:

function myCallbackFunction(dataString, result) 
    var data = <decode data from dataString>;
    if (result) 
        // user has confirmed
     else 
        // user has denied
    

在Android端,我先在Activity的onCreate()方法中激活Javascript接口:

public void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    WebView webView = new WebView(this);
    setContentView(webView);
    WebSettings settings = webView.getSettings();
    settings.setJavaScriptEnabled(true);
    webView.addJavascriptInterface(new MyJavascriptInterface(webView), "MyClient");

MyJavascriptInterface 的实现然后创建相应的 Android 对话框并将结果传递回 javascript:

    WebView webView;

    public MyJavascriptInterface(WebView w) 
         this.webView = w;
    

    @JavascriptInterface
    public void showConfirmationDialog(final String callbackFunction, final String data, String title,
            String message) 

        Dialog.OnClickListener positiveListener = new Dialog.OnClickListener() 

            @Override
            public void onClick(DialogInterface dialog, int which) 
                webView.loadUrl("javascript:" + callbackFunction + "('" + data + "', true)");
            
        ;
        Dialog.OnClickListener negativeListener = new Dialog.OnClickListener() 

            @Override
            public void onClick(DialogInterface dialog, int which) 
                webView.loadUrl("javascript:" + callbackFunction + "('" + data + "', false)");
            
        ;

        AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
        builder.setTitle(title).setMessage(message).setPositiveButton("Ok", positiveListener)
                .setNegativeButton("Cancel", negativeListener).setCancelable(false);
        builder.create().show();
    

将回调函数的名称传递给 Android 允许使用多次调用确认对话框,每个确认对话框都配备了自己的函数来执行实际操作。数据字符串将携带执行操作所需的所有数据(甚至可以包含 Json 编码的对象)。

【讨论】:

这个答案值得更多提升(在我看来)。我几乎为这个“回调”功能挖掘了 PhoneGap 的源代码。但由于这个答案,我不必。 哇,用 javascript 字符串调用 loadUrl 真的是从 Java 调用(返回)到 Javascript 的唯一方法吗? MyJavascriptInterface 中的 webview 来自哪里??【参考方案2】:

您将无法按照指定的方式传递函数。您将一个函数传递给 Android.myData,但 Android.myData 需要一个字符串。相反,您可能想要

var myCallback = console.log;
Android.myFunction("myCallback");

您仍然有一个问题,即您没有将任何数据传递给回调。虽然这与您的问题没有直接关系,但它将成为一个问题,因为您将有相同的字符串转换问题(可通过 JSON 解决......但如果 Android 为您处理这部分会很好)。

最后,您可以将 javascript: 字符串缩短为

String js = "javascript:" + callback + "();";

但是,当然,先测试;)

【讨论】:

这是我终于意识到的。它应该更明显。将要解析为 javascript 的字符串从 Android 注入到 Webview 中,显然是远远超出范围......吻那个闭包再见。至于第二个,我编辑了问题以纠正这个错误。这就是我在没有先测试的情况下发布我的代码的缩写版本的结果。【参考方案3】:

WebView 允许您在窗口上下文中直接执行一些 JavaScript。 所以你不需要通过资源 URL 传递 JavaScript。

这是一种将数据传回 html 页面的经过批准的方式

/**
 * This is an approved way to pass data back to the html page
 */
 webView.evaluateJavascript("alert('pass here some ...')", new ValueCallback<String>() 
       @Override
       public void onReceiveValue(String s) 

        
    );

来自官方文档的详细信息

在当前显示页面的上下文中异步评估 JavaScript。如果非空,|resultCallback|将使用从该执行返回的任何结果调用。此方法必须在 UI 线程上调用,回调将在 UI 线程上进行。

兼容性说明。面向 N 或更高版本的应用程序,来自空 WebView 的 JavaScript 状态不再在诸如 loadUrl(String) 之类的导航中保持不变。例如,调用 loadUrl(String) 之前定义的全局变量和函数将不会存在于加载的页面中。应用程序应该使用 addJavascriptInterface(Object, String) 来跨导航保存 JavaScript 对象。

Link to the official WebView documentation

【讨论】:

以上是关于Javascript回调函数传递给Android的主要内容,如果未能解决你的问题,请参考以下文章

Javascript:将自定义参数传递给回调函数

JavaScript:如何将多个参数传递给对象成员内的回调函数?

如何在 Javascript .filter() 方法中将额外参数传递给回调函数?

将参数传递给回调函数COCOS2D Android

SWIG (Java):如何将带有回调函数的结构从 Android 应用程序传递给 C++?

什么是回调函数