Android Webview using flyers - JNI error

I am developing an Android application containing Webview. The HTML for web browsing contains mostly JavaScript code; he retrieves a map of the building from Geoserver. I use Flyer to display different layers. Each base layer is a floor.

I add 2 overlays to the base levels from Android; one is a heatmap, and the other is a set of markers representing the location of some sensors. Thanks to the JavaScript interface in Android, JavaScript code can retrieve datasets created with Android.

It works when I show ONE of the overlays. As soon as I want to display both overlays, or after I display the overlay, hide it and display the other, the application will crash. I have different errors according to the first addition.

Something really strange: this only happens with my tablet (which is very cheap) and not with AVD.

Here is my JavaScript code:

<script type="text/javascript" charset="utf-8"> var map, baseLayers, heatmapLayer, sensorsLayer; var ground_floor = new L.tileLayer.wms('http://192.168.1.16/geoserver/wms', { layers: 'ground_floor', format: 'image/png' }); var first_floor = new L.tileLayer.wms('http://192.168.1.16/geoserver/wms', { layers: 'first_floor', format: 'image/png' }); var second_floor = new L.tileLayer.wms('http://192.168.1.16/geoserver/wms', { layers: 'second_floor', format: 'image/png' }); var current_floor = ground_floor; baseLayers = { "Ground floor": ground_floor, "First floor": first_floor, "Second floor": second_floor }; map = new L.Map('map', { center: new L.LatLng(-45, 170), zoom: 30, layers: [ground_floor], crs: L.CRS.EPSG900913 }).setView([-45.8668664, 170.5185323], 31); var control = L.control.layers(baseLayers).addTo(map); map.on('baselayerchange', onBaseLayerChanged); // Called when the base layer (meaning the floor) is changed function onBaseLayerChanged(event) { // Update the current floor if (event.layer == ground_floor) current_floor = ground_floor; else if (event.layer == first_floor) current_floor = first_floor; else if (event.layer == second_floor) current_floor = second_floor; else Android.debug("Wrong base layer"); Android.debug("yo1"); // Update the heatmap and sensors' location // if they are displayed if (map.hasLayer(heatmapLayer)) { removeHeatmap(); addHeatmap(); } Android.debug("yo2"); if (map.hasLayer(sensorsLayer)) { Android.debug("yo3"); removeSensors(); addSensors(); Android.debug("yo4"); } } function addHeatmap() { heatmapLayer = L.TileLayer.heatMap({ // radius could be absolute or relative // absolute: radius in meters, relative: radius in pixels radius: { value: 5, absolute: true }, opacity: 0.8, gradient: { 0.45: "rgb(0,0,255)", 0.55: "rgb(0,255,255)", 0.65: "rgb(0,255,0)", 0.95: "yellow", 1.0: "rgb(255,0,0)" } }); var dataSet; if (current_floor == ground_floor) dataSet = JSON.parse(Android.getDataSetGroundFloor()); else if (current_floor == first_floor) dataSet = JSON.parse(Android.getDataSetFirstFloor()); else if (current_floor == second_floor) dataSet = JSON.parse(Android.getDataSetSecondFloor()); else Android.debug("Error getDataSet : wrong floor"); heatmapLayer.setData(dataSet.data); control.addOverlay(heatmapLayer, "Heatmap"); heatmapLayer.addTo(map); } function removeHeatmap() { control.removeLayer(heatmapLayer); map.removeLayer(heatmapLayer); } function addSensors() { var sLat, sLon; var markers = new Array(); if (current_floor == ground_floor) { sLat = JSON.parse(Android.getLatitudesGroundFloor()); sLon = JSON.parse(Android.getLongitudesGroundFloor()); } else if (current_floor == first_floor) { sLat = JSON.parse(Android.getLatitudesFirstFloor()); sLon = JSON.parse(Android.getLongitudesFirstFloor()); } else if (current_floor == second_floor) { sLat = JSON.parse(Android.getLatitudesSecondFloor()); sLon = JSON.parse(Android.getLongitudesSecondFloor()); } else Android.debug("Error getCoordinates : wrong floor"); for (i=0; i<sLat.length && i<sLon.length; i++) { var marker = L.marker([sLat[i],sLon[i]]); markers.push(marker); } sensorsLayer = L.layerGroup(markers); control.addOverlay(sensorsLayer, "Sensors"); sensorsLayer.addTo(map); } function removeSensors() { control.removeLayer(sensorsLayer); map.removeLayer(sensorsLayer); } </script> 

If I add a heatmap first and then the sensors, I have a NullPointerException on the "sensorLatFloor" with the following code:

 public JSONArray getLatitudesGroundFloor() { if (!isSensorsQueryDone()) new SelectSensorsLocationATask(SensorsFragment.this).execute(); // Wait for the end of the query while (!querySuccessful) {} JSONArray latJSON = null; latJSON = new JSONArray(sensorsLatGroundFloor); return latJSON; } 

This is for the first floor, but it does the same for each floor. "sensorsLatGroundFloor" is an ArrayList populated by AsyncTask SelectSensorsLocationATask after a query in the local database. The code works because it works when I want to show only sensors.

When I first show the sensors and then the heatmap, the application crashes and I have the following error in LogCat:

 JNI ERROR (app bug): accessed staled weak global reference 0xffffffff (index 65535 in a table of size 8) VM aborting Fatal signal 11 (SIGSEV) at 0xdeadd00d 

This is very strange because I don’t manipulate JNI code at all ...

In addition, I have another mistake that should be very stupid, but I do not understand why it does not work. Take a look at this part of my JavaScript code:

 // Called when the base layer (meaning the floor) is changed function onBaseLayerChanged(event) { // Update the current floor if (event.layer == ground_floor) current_floor = ground_floor; else if (event.layer == first_floor) current_floor = first_floor; else if (event.layer == second_floor) current_floor = second_floor; else Android.debug("Wrong base layer"); Android.debug("yo1"); // Update the heatmap and sensors' location // if they are displayed if (map.hasLayer(heatmapLayer)) { removeHeatmap(); addHeatmap(); } Android.debug("yo2"); if (map.hasLayer(sensorsLayer)) { Android.debug("yo3"); removeSensors(); addSensors(); Android.debug("yo4"); } } 

This function should update the overlays (s) displayed when switching the base layer (which means gender). It works with thermal insulation, but not with sensors ... If the heating card is not on the card, the function is finished, it does not even look at my second one, if. You can see my "Android.debug", it displays a message in LogCat, which I put as a parameter. Here LogCat displays only "yo1".

EDIT: I understood something about this last error. The problem is that the leaflet function hasLayer. If the map has the specified layer, it returns true. Therefore, in my opinion, if this is not so, it should return false ... It makes sense. But instead, it causes a code error, so the code after the function is ignored. Either I make a mistake that I don’t see when I call it, or Liflet made a useless function ... Therefore, I must have made a stupid mistake, but could not find it!

Hope I was clear enough for you to understand my problems ... Let me know if you need more Android code, although I don't think that would be helpful.

Thank you in advance.

* CORRECTION OF THE PROBLEM *

Errors were caused by a problem of synchronization of tasks in the Android code. You can see that I am waiting for the end of the query with the variable "querySuccessful". In fact, I only manipulated this variable from AsyncTask: I set it to false at the beginning and true at the end. Now I set it to true before starting AsyncTask and the application is working, I have no more errors.

I still have problems with the hasLayer function; It works when I switch the base layer once, sometimes twice, but then it stops working. But apparently, this may be a bug from Leaflet, which will be fixed in the next release.

+4
source share

Source: https://habr.com/ru/post/1483494/


All Articles