ArcGIS SDK 100.2.1离线路线创建报错及旧版本示例适配咨询
解决ArcGIS SDK 100.2.1离线路线创建的"Cannot call this method in this context"错误
首先得说,100.x系列的ArcGIS Runtime SDK和10.2.x的API差异非常大,旧示例的写法完全不适用于新SDK,这是你遇到问题的核心原因。下面我会一步步帮你实现离线路线创建,并解决这个线程上下文的报错。
一、错误原因分析
这个报错几乎都是线程上下文不正确导致的:ArcGIS Runtime SDK 100.x中,所有耗时的地理操作(比如加载本地网络数据集、计算路由)必须在后台线程执行;而涉及UI更新或者组件初始化的部分,必须在主线程完成。如果搞混了线程,就会抛出这个异常。
二、离线路线创建的正确步骤(针对100.2.1)
假设你已经有包含网络数据集的离线地理数据库(.geodatabase),下面是完整的实现流程:
1. 准备离线资源
确保你的离线geodatabase包含:
- 可用的网络数据集(用于路由计算)
- 可选的定位点数据(起点、终点)
2. 初始化离线RouteTask
不要在主线程中直接初始化RouteTask或者加载网络数据集,必须用后台线程(比如AsyncTask,100.2.1时期Android还没普及Coroutine,用AsyncTask更稳妥)。
示例代码:
// 在Activity中启动后台加载任务 new RouteLoadTask().execute(); // 后台加载RouteTask的任务类 private class RouteLoadTask extends AsyncTask<Void, Void, RouteTask> { @Override protected RouteTask doInBackground(Void... voids) { try { // 本地geodatabase路径(示例为内部存储路径,可根据你的存储位置调整) String gdbPath = getFilesDir() + "/your_offline_data.gdb"; Geodatabase geodatabase = new Geodatabase(gdbPath); // 替换成你自己的网络数据集名称 String networkDatasetName = "CityStreets_ND"; NetworkDataset networkDataset = geodatabase.getNetworkDataset(networkDatasetName); // 创建离线RouteTask实例 return RouteTask.createFromNetworkDataset(networkDataset); } catch (ArcGISRuntimeException e) { e.printStackTrace(); return null; } } @Override protected void onPostExecute(RouteTask routeTask) { super.onPostExecute(routeTask); if (routeTask != null) { // 主线程中处理初始化完成逻辑,比如获取默认路由参数 getDefaultRouteParams(routeTask); } else { Toast.makeText(RoutingActivity.this, "离线路由资源加载失败", Toast.LENGTH_SHORT).show(); } } }
3. 获取路由参数并计算路线
获取默认参数、计算路由的操作都需要在后台线程执行,只有结果处理可以放在主线程:
private void getDefaultRouteParams(RouteTask routeTask) { new AsyncTask<RouteTask, Void, RouteParameters>() { @Override protected RouteParameters doInBackground(RouteTask... routeTasks) { try { // 获取默认路由参数,必须在后台线程 return routeTasks[0].createDefaultParameters(); } catch (ArcGISRuntimeException e) { e.printStackTrace(); return null; } } @Override protected void onPostExecute(RouteParameters params) { super.onPostExecute(params); if (params != null) { // 设置起点和终点(替换成你的实际点位) MapPoint start = new MapPoint(-118.2437, 34.0522, SpatialReferences.getWgs84()); MapPoint end = new MapPoint(-118.2500, 34.0580, SpatialReferences.getWgs84()); // 添加停靠点到参数 params.setStops(new Stop[]{new Stop(start), new Stop(end)}); // 启动路由计算任务 new RouteCalculateTask().execute(routeTask, params); } } }.execute(routeTask); } // 计算路由的后台任务 private class RouteCalculateTask extends AsyncTask<Object, Void, RouteResult> { @Override protected RouteResult doInBackground(Object... objects) { RouteTask task = (RouteTask) objects[0]; RouteParameters params = (RouteParameters) objects[1]; try { // 执行路由计算,必须在后台线程 return task.solveRoute(params); } catch (ArcGISRuntimeException e) { e.printStackTrace(); return null; } } @Override protected void onPostExecute(RouteResult result) { super.onPostExecute(result); if (result != null && result.getRoutes().size() > 0) { // 主线程中处理路由结果,比如在地图上绘制路线 Route route = result.getRoutes().get(0); Polyline routeLine = route.getRouteGeometry(); // 创建路线图形并添加到地图(假设你已创建GraphicsOverlay) Graphic routeGraphic = new Graphic(routeLine, new SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.RED, 3)); graphicsOverlay.getGraphics().add(routeGraphic); } else { Toast.makeText(RoutingActivity.this, "路由计算失败", Toast.LENGTH_SHORT).show(); } } }
4. 关键注意事项
- 线程严格隔离:所有涉及
Geodatabase、RouteTask的初始化和耗时操作,必须放在doInBackground中;UI更新(比如添加图形、提示)必须放在onPostExecute(主线程)。 - 资源释放:使用完
Geodatabase后记得调用close()方法,避免内存泄漏。 - 权限配置:如果geodatabase在外部存储,确保App申请了
READ_EXTERNAL_STORAGE权限;用内部存储(如示例中的getFilesDir())则无需额外权限。
三、其他排查方向
如果按上述步骤仍报错,检查以下几点:
- 确认离线geodatabase中的网络数据集有效,可通过ArcGIS Pro打开验证。
- 100.2.1版本的SDK集成是否正确,gradle依赖是否完整。
- 不要在
Application上下文或非UI线程中初始化MapView相关组件,MapView必须在Activity/Fragment的UI线程中创建。
内容的提问来源于stack exchange,提问作者user3176574




