我正在开发一个包含 Webview 的 Android 应用程序。webview对应的HTML主要包含JavaScript代码;它从地理服务器检索建筑物的地图。我使用 Leaflet 来显示不同的图层。每个基础层是一个地板。
我正在向 Android 的基础层添加 2 个叠加层;一个是热图,另一个是一组表示某些传感器位置的标记。由于 Android 中的 JavaScript 接口,JavaScript 代码可以获取从 Android 创建的数据集。
当我只显示一个叠加层时它可以工作。一旦我想显示两个覆盖,或者在我显示一个覆盖后,隐藏它并显示另一个,应用程序就会崩溃。根据首先添加的叠加层,我有不同的错误。
真的很奇怪:它只发生在我的平板电脑(非常便宜)上,而不是 AVD。
这是我的 JavaScript 代码:
<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>
如果我先添加热图,然后在传感器上添加以下代码的“sensorsLatFloor”上的 NullPointerException:
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;
}
这是针对一楼的,但对每一层都是一样的。“sensorsLatGroundFloor”是在本地数据库中查询后由 AsyncTask SelectSensorsLocationATask 填充的 ArrayList。该代码有效,因为它在我只想显示传感器时有效。
当我先显示传感器,然后显示热图时,应用程序崩溃并且 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
这很奇怪,因为我根本不操作 JNI 代码......
此外,我还有另一个错误,这一定很愚蠢,但我不明白为什么它不起作用。看看我的 JavaScript 代码的这一部分:
// 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");
}
}
当我切换基础层(即地板)时,此功能应该更新显示的叠加层。它适用于热图,但不适用于传感器......如果热图不在地图上,则该功能已完成,它甚至不看我的第二个 if。您可以看到我的“Android.debug”,它在 LogCat 中显示我作为参数输入的消息。在这里,LogCat 只显示“yo1”。
编辑:我想出了最后一个错误。问题似乎是传单功能“hasLayer”。如果地图指示了图层,则返回 true。所以在我看来,如果不是,它应该返回 false ......这是有道理的。但不是它,而是使代码错误,因此函数之后的代码被忽略。要么我犯了一个我调用它时看不到的错误,要么Leaflet做了一个无用的功能......所以我一定犯了一个愚蠢的错误,但我没有找到它!
我希望我已经足够清楚,可以让您理解我的问题...如果您需要更多 Android 代码,请告诉我,尽管我认为这不会有帮助。
提前谢谢你。
*问题已修复*
错误是由 Android 代码中的任务同步问题引起的。您可以看到我使用变量“querySuccessful”等待查询结束。实际上,我只是从 AsyncTask 操作这个变量:我在开始时将其设置为 false,最后将其设置为 true。现在我在启动 AsyncTask 之前将其设置为 true,并且应用程序正常工作,我没有更多错误。
Leaflet 函数 hasLayer 仍然存在一些问题;当我切换基础层一次(有时两次)时它可以工作,但随后它停止工作。但显然这可能是 Leaflet 的错误,将在下一个版本中修复。