I am in the process of writing a Phonegap app for Android that uses one plugin that I developing at the same time. The app uses Rhino to run perform a set of data analysis operations in JavaScript. One of the reasons for doing this is to enable me to silently update the data analysis rules as and when required without forcing an entire app version update.
The JavaScript code used by Rhino is downloaded by the app and stored to internal storage. This happens in two ways
- At App startup: When the app starts up it checks for the existence of the JS code file. If it is absent it fetches it from the server.
- In response to a Push message: When required I send down a push message to the app which reacts by trying to fetch the updated JS code from the server.
Both routes use the same bit of underlying Java code to perform the JS code update. The first one works without any issues. However the second one throws up exceptions.
The relevant code
private boolean doInit(CallbackContext cbc)
{
Storage.setFilePath(this.context.getFilesDir());
Storage.loadJSCode();
...
}
The above is called when Phonegap triggers its onDeviceReady
event - i.e.when the app starts up.
Storage
is my generic class for handling file I/O operations in the app. The actual loadJSCode
is shown below
public static String loadJSCode()
{
try
{
String rslt = "";
HttpURLConnection conn = null;
addr = "https://path/to/rhinocode.php";
try
{
URL url = new URL(addr);
conn = (HttpURLConnection)url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("User-Agent", Statics.USER_AGENT);
InputStream ips = conn.getInputStream();
//THE EXCEPTION IS THROWN HERE
int responseCode = conn.getResponseCode();
if (200 != responseCode)
{
Feedback.logError("GET error: " + responseCode + " on " + addr);
return null;
}
BufferedReader bufr = new BufferedReader(new InputStreamReader(ips));
String line;
while ((line = bufr.readLine()) != null) rslt += line;
bufr.close();
} finally{if (null != conn) conn.disconnect();}
return rslt;
} catch(Exception e)
{
Feedback.logError("get fault " + Utils.stackTrace(e));
return null;
}
}
}
A couple of notes of explanation
- Feedback is a class I use to handle all operations that involve displaying stuff to the user.
- Utils is a class I use to encapsulate a miscellany of utility routines. In this instance the stackTrace routine provides me with a full stack trace that led up to the problem as a string.
The Push message handler that triggers the offending call to loadJSCode
is shown below
public class Receive extends BroadcastReceiver
{
@Override
public void onReceive(Context context,Intent intent)
{
Bundle extras = intent.getExtras();
if (extras != null)
{
if (extras.getString("message") != null &&
extras.getString("message").length() != 0)
{
try
{
JSONObject jo = new JSONObject(extras.getString("message"));
int code = jo.optInt("kind",0);
if (1 == code) storage.loadJSCode();
} catch(Exception e){Feedback.setError(e.getMessage());}
}
}
}
}
The full stack trace leading up to this issue is shown below
android.os.NetworkOnMainThread Exception
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictM ode.java:1 156)
at com.android.org.conscrypt.OpenSSLSocketImpl.close(OpenSSLSock etImpl.javal 009)
at com.android.okhttp.Connection.c1ose(Connection.java:175)
at com.android.okhttp.internal.Util.c1oseQuietly(Util.java:110)
at com.android.okhttp.internal.http.HttpEngine.release(HttpEngine.java: 447)
at com.android.okhttp.internal.http.HttpURLConnectionImpl.disconnect
(HttpURLConnectionImpl.java:104)
at
com.android.okhttp.internal.http.HttpsURLConnectionlmpl.
disconnect(HttpsURLConnectionImpl java:124)
at example.com.plugin.Storage.IoadJSCode( Storage.java:28)
at example.com.plugin.Receive.onReceive (Receive.java:50)
at android.app.ActivityThread.handle (Storage.java:28)
at com.example.plugin.Receive.onReceive (Receive.java:50)
at android.app.ActivityThread.handle Receiver(ActivityThread.java:2585)
at android.app.ActivityThread.access $1800(ActivityThread.java:151)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1409)
at android.os.Handler.dispatchMessage(Handler.java:1 10)
at android.os.Looper.loon(Looper.java)
$1800(ActivityThread.java 51)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java 409)
at android.os.HandlerdispatchMessage(Handler.java:110)
at android.os.Looper.loop(Looper java:193)
at android.app.ActivityThread main( ActivityThread.java:5304)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.Zygotelnit $MethodAndArgsCaller.run(Zygote
at android.os.Looper.loop(Looper.jav a:193)
at android.app.ActivityThread.main( ActivityThread.java:5304)
at java.lang.reflect.Method.invokeNative(Native Method) at
java.lang.reflect.Method.invoke(Method.java:515) at
com.android.internal.os.Zygotelnit
$MethodAndArgsCaller.run(Zygot elnit.java:824)
at com.android.internal.os.Zygotelnit .main(Zygotelnit.java:640)
at dalvik.system.NativeStart.main
By dint of some experiment I have established that the exception gets thrown when InputStream ips = conn.getInputStream();
when loadJSCode
above is executed.
In the interests of completeness I should mention that the Receive
BroadcastReceiver
sub class is fired by via the following embeded in the plugin.xml
file.
<receiver android:name="com.example.plugin.Receive"
android:exported="false">
<intent-filter>
<!-- Do not modify this -->
<action android:name="pushy.me" />
</intent-filter>
</receiver>
I am using Phonegap CLI 6.3.3 with Java 7.
I suspect that I am somehow running foul of threading rules here and am ending up doing something from a background thread or something along those lines. However, it is not at all clear to me what I might be dong wrong that might be causing this. I hope that someone here might be able to explain.