Javascript calls from Android using PhoneGap
Solution 1
Firstly, you are using a Plugin
subclass. Plugin
has been deprecated and has been replaced with CordovaPlugin
. If you're using an old version of PhoneGap, I would recommend that you upgrade.
Secondly, your exec call is wrong. The docs for plugin development clearly state that you have to pass 5 parameters, while you're passing 3 nulls. How do you expect that to be handled?
cordova.exec(function(winParam) {}, function(error) {}, "service",
"action", ["firstArgument", "secondArgument", 42,
false]);
Here, the service
, action
and the array of parameters determine what will happen in your Java code. The first two determine what will happen in JavaScript under certain conditions. So, while you can use null for the first two, you have to specify the last three.
I have a working example plugin that works with PhoneGap 2.3.0. See the code below:
public class ExampleJSCommunicator extends CordovaPlugin {
public boolean execute (final String action, final JSONArray args, CallbackContext callbackContext) throws JSONException {
PluginResult.Status status = PluginResult.Status.OK;
String result = "";
cordova.getActivity ().runOnUiThread (new Runnable () {
@Override
public void run() {
try {
String displayText = "";
if (action.equals ("buttonClicked")) {
displayText = args.getString(0) + " was clicked";
}
else if (action.equals ("animationRunning")) {
displayText = args.getBoolean(0) ? "Animation started running" : "Animation stopped running";
}
TextView label = (TextView) cordova.getActivity().findViewById (R.id.textView);
label.setText (displayText + " and the Activity knows it!");
} catch (JSONException e) {
e.printStackTrace();
}
}
});
return true;
}
}
With the code above, you have your Java-side plugin capable of handling two custom "actions" - buttonClicked
and animationRunning
. These actions serve my purposes, but you could name them otherwise.
Now, you still need to register your plugin, so that Cordova will know about it. This is done in the xml/config.xml
file. Under plugins
, you have to add the following:
<plugin name="ExampleJSCommunicator" value="com.example.phonegap.ExampleJSCommunicator"/>
Then you can pass data (or "actions") from JavaScript as follows. Note that the parameters (which.id
and animationRunning
are passed in an array):
cordova.exec (null, null, "ExampleJSCommunicator", "buttonClicked", [which.id]); // my first action
cordova.exec (null, null, "ExampleJSCommunicator", "animationRunning", [animationRunning]); // my second action
These two exec
calls will trigger the execute method in the ExampleJSCommunicator
class and will get handled in the respective if blocks. It doesn't matter where you call exec, as long as you declare your JavaScript code after you include the cordova.js file. My JavaScript is contained within a separate main.js file and it works just fine:
<script type="text/javascript" charset="utf-8" src="cordova.js"></script>
<script type="text/javascript" charset="utf-8" src="main.js"></script>
Solution 2
So far you're not actually using any of the tools that Apache Cordova gives you for making plugins and are just trying to bolt on a class with the standard Android SDK. If you are looking to add functionality, I recommend writing a plugin, because of the following reasons:
- Apache Cordova has alternate ways of communication between the WebView and Java so that it still works even when addJavascriptInterface fails
- You don't need to override things
I highly recommend you read this and move your custom Java code to a plugin: http://docs.phonegap.com/en/2.5.0/guide_plugin-development_android_index.md.html
However, the main bug in you example is by using a WebView in your class instead of a CordovaWebView. A WebView doesn't have a working sendJavascript method and the only simple way of sending the Javascript back is by using loadUrl.
stealthjong
Software Engineer/Architect Java (experienced) Android (experienced) C# (some experience) PHP (some experience) HTML/CSS (some experience) JavaScript/jQuery (some experience) clojure (some experience) Furthermore Studying Information Technology Knowledge of Patterns and Frameworks Read books on proper/clean code Multiple (private) coding/architectural projects
Updated on June 27, 2022Comments
-
stealthjong about 2 years
I have and application built with
PhoneGap
, and I'm trying to communicate withJavascript
from native code.in my
DroidGap
extending class:@Override public void onCreate(Bundle savedInstanceState) { Logger.log("oncreate"); super.onCreate(savedInstanceState); super.init(); super.appView.getSettings().setJavaScriptEnabled(true); super.appView.getSettings().setSupportZoom(true); super.appView.getSettings().setBuiltInZoomControls(true); super.appView.getSettings().setDisplayZoomControls(false); jsinterface = new CommunicationInterface(this, appView); super.appView.addJavascriptInterface(jsinterface, "communicationinterface"); }
the javascriptinterface:
public class CommunicationInterface { private WebView mAppView; private DroidGap mGap; public CommunicationInterface(DroidGap gap, WebView view) { mAppView = view; mGap = gap; } public String getTestString() { return "teststring"; } public void parse(Object o) { Logger.log(o); } }
The
Javacript
is located in an external file (I create an HTML file which has this line in theheader
:<script type="text/javascript" src="scripts.js"></script>
)Scripts.js
:function sendToInterface() { alert("alert"); var map = new Object(); (...) window.communicationinterface.parse(map); //communication js -> android seems to work. }
I read in other posts that it's possible to communicate between PhoneGap and Android, but thusfar I've not had any success. I did manage to create an alert, but that was with
loadUrl("javascript:alert('Alert');")
, but I've also read that you shouldn't do because that's whatsendJavascript()
is for (and it causes leaks, reloads page, etc). I've tried to shoot a couple of Strings through thesendJavascript()
method, but to no avail:sendJavascript("javascript:alert('Alert');")
sendJavascript("javascript:sendToInterface();")
sendJavascript("sendToInterface();")
sendJavascript("window.sendToInterface();")
How to communicate from native -> PhoneGap (or what's wrong with what I already have)? Thusfar other posts and questions haven't helped me with this particular problem.
Read:
- http://www.jumpbyte.com/2012/phonegap-native-to-javascript/
- Phonegap: "processMessage failed" unable to send javascript function (Cordova 2.5.0)
- Communication between Android Java and Phonegap Javascript? (although this is the other direction, but I'm trying to do both)
- http://www.intelligrape.com/blog/2013/01/31/using-sendjavscript-to-inject-javascript-into-android-phonegap-application/
- Phonegap - Android - Calling javascript from native causes leakedwindow
- how to call javascript function from android using phonegap plugin
EDIT
I wrote a working project:
Java part
import org.apache.cordova.DroidGap; import org.json.JSONException; import org.json.JSONObject; import android.os.Bundle; import android.util.Log; public class App extends DroidGap { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); super.loadUrl("file:///sdcard/ds/index.html"); System.out.println("loading from sdcard"); Thread t = new Thread() { public void run() { try { for (int i = 0; i < 3; i++) { sleep(2000); sendValue("value " + i, "another vlaue " + i); } } catch (Exception e) { e.printStackTrace(); } }; }; t.start(); } public void sendValue(String value1, String value2) { System.out.println("sendvalue in app"); JSONObject data = new JSONObject(); try { data.put("value1", value1); data.put("value2", value2); } catch (JSONException e) { Log.e("CommTest", e.getMessage()); } String js = String.format("window.plugins.appcomm.updateValues('%s');", data.toString()); this.sendJavascript(js); } } import org.apache.cordova.api.Plugin; import org.apache.cordova.api.PluginResult; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.util.Log; public class AppComm extends Plugin{ private static AppComm instance; public AppComm () { instance = this; } public static AppComm getInstance() { return instance; } @Override public PluginResult execute(String action, JSONArray args, String callbackId) { System.out.println("in execute from appcomm"); return null; } public void sendValue(String value1, String value2) { System.out.println("sendvalue in appComm"); JSONObject data = new JSONObject(); try { data.put("value1", value1); data.put("value2", value2); } catch (JSONException e) { Log.e("CommTest", e.getMessage()); } String js = String.format( "window.plugins.commtest.updateValues('%s');", data.toString()); this.sendJavascript(js); } }
res/xml/plugins.xml
<plugins> <plugin name="App" value="org.apache.cordova.App"/> <plugin name="Geolocation" value="org.apache.cordova.GeoBroker"/> <plugin name="Device" value="org.apache.cordova.Device"/> <plugin name="Accelerometer" value="org.apache.cordova.AccelListener"/> <plugin name="Compass" value="org.apache.cordova.CompassListener"/> <plugin name="Media" value="org.apache.cordova.AudioHandler"/> <plugin name="Camera" value="org.apache.cordova.CameraLauncher"/> <plugin name="Contacts" value="org.apache.cordova.ContactManager"/> <plugin name="File" value="org.apache.cordova.FileUtils"/> <plugin name="NetworkStatus" value="org.apache.cordova.NetworkManager"/> <plugin name="Notification" value="org.apache.cordova.Notification"/> <plugin name="Storage" value="org.apache.cordova.Storage"/> <plugin name="Temperature" value="org.apache.cordova.TempListener"/> <plugin name="FileTransfer" value="org.apache.cordova.FileTransfer"/> <plugin name="Capture" value="org.apache.cordova.Capture"/> <plugin name="Battery" value="org.apache.cordova.BatteryListener"/> <plugin name="SplashScreen" value="org.apache.cordova.SplashScreen"/> <plugin name="AppComm" value="com.example.plugin.AppComm"/> </plugins>
cordova.xml
<?xml version="1.0" encoding="utf-8"?> <cordova> <access origin=".*"/> <!-- allow local pages --> <log level="DEBUG"/> <preference name="classicRender" value="true" /> </cordova>
Index.html header
<head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0" /> <title> </title> <link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.css" /> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"> </script> <script src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"> </script> <script type="text/javascript" charset="utf-8" src="cordova.js"></script> <script type="text/javascript" charset="utf-8"> var AppComm=function(){}; AppComm.prototype.updateValues=function(a){ var map = new Object(); map["X1"] = "hallo"; map["X2"] = "hi"; cordova.exec(null, null, null); }; cordova.addConstructor(function(){cordova.addPlugin("appcomm",new AppComm)}); </script> </head>
One of the problems was that javascript was in a separate file (I think that was one of the problems). If it isn't to much to ask, how can I properly call java back, and with what values? How to implement the execute method and how to call it (I'm really bad at JQuery)?