ArcGIS for Android 100.5示例代码详解:从环境配置到离线GIS开发 简介ArcGIS for Android 100.5 完整示例代码是一套面向Android开发者的GIS开发学习资源涵盖地图渲染、图层管理、地理编码、几何操作、定位服务、空间查询、地理处理、离线地图及UI组件等核心功能模块。资源共2000个文件以png界面资源、xml布局配置、java/kt示例源码、gradle工程文件及md说明文档为主体压缩包约69.71MB目录结构清晰便于按功能索引学习。示例覆盖TiledMapServiceLayer、DynamicMapServiceLayer、FeatureLayer等多种图层切换以及缓冲区分析、最近邻搜索、反地理编码、轨迹显示等典型场景开发者可对照官方API边读边改快速落地地图应用需求。目前已有1219人浏览学习适合初中级开发者系统掌握ArcGIS Runtime SDK for Android 100.5开发要点也可作为团队二次开发的基础模板。1. 这套示例代码到底能干什么做 Android 端 GIS 开发的人应该都经历过这种阶段地图 SDK 的 API 文档翻了一堆真到自己动手写的时候还是不知道从哪下手。ArcGIS for Android 100.5 这套完整示例代码就是为了解决这个问题而存在的。它不是那种只有一两个 demo 的入门教程工程而是 Esri 官方把 Runtime SDK 核心功能全部拆成独立小示例统一放在一个工程里覆盖了从地图显示、图层加载、数据编辑到空间分析、路径导航、符号渲染等几乎全部常用场景。我最初接触这套示例代码是在 2018 年底当时刚接手一个巡护管理类 App 的改造项目需要在地图上叠加本地 shapefile、做缓冲区分析、还要求支持离线切片。那时候 100.x 的 API 改动很大网上资料又少全靠官方这套示例代码才把项目撑下来。所以这次写这篇文章也想把当时折腾的收获整理出来。你不管是刚开始接触 ArcGIS Android 开发还是已经在用旧版本想往 100.5 迁移这套代码都值得花时间过一遍。这套代码的实用价值不只是“能跑通”更关键的是它每个示例都保持了最小化、独立性你在实际项目里遇到某个需求直接打开对应示例拷贝核心逻辑改一改就能用省掉大量去 Stack Overflow 碰运气的步骤。2. 示例库整体架构与模块划分2.1 官方示例库的代码组织方式先把整套工程的目录结构搞清楚后面用起来才顺手。ArcGIS for Android 100.5 示例代码在 GitHub 上的仓库名是 arcgis-runtime-samples-android工程采用 Gradle 构建主模块就是app。所有示例按照功能类别划分包名每个示例都是独立的Activity配套一个同名的布局文件和说明文档。这种设计的好处非常明显模块解耦彻底删掉哪个示例都不影响其他的而且每一个示例都可以单独运行、单独调试。模块分类上100.5 版本把示例分成了大约 12 个大类包括地图Map、图层Layers、数据Data、要素编辑Edit and Manage Data、分析Analysis、路径规划Routing、场景Scenes等。每个大类下面又有多个细分示例比如“图层”这块就包含了在线切片图层、离线切片图层、矢量切片、栅格图层、要素图层的加载也有通用图层、WMS、WMTS 等协议图层的加载示例差不多把日常开发能遇到的图层类型都覆盖了。这套组织方式最大的价值在于你不需要先看懂全部代码才能干活。我曾经帮同事定位一个聚合符号不显示的问题直接在示例列表里找到“Clustering”这个示例打开对应的 Activity前后不到十分钟就定位到是我们初始化时漏了设置聚类渲染器。这种查找效率比自己翻 API 文档高很多。2.2 示例类别的功能覆盖面具体到功能分类100.5 这套示例有几个特别值得关注的模块。第一个是“Analysis”包里面包含可视域分析、缓冲区分析、测量距离和面积、最近设施点分析等这些在 Web 端做起来比较容易但移动端不少人以为做不了其实 Runtime SDK 都提供了原生实现的 API。比如缓冲区分析你只需要构造一个GeometryEngine的调用传入目标几何和缓冲半径就能返回缓冲后的几何对象根本不需要自己去写几何运算逻辑。第二个是“Edit and Manage Data”包这个在做外业数据采集类 App 时特别有用。里面有要素增删改、属性编辑、几何编辑、图层同步、离线编辑同步等完整流程。我当时做巡护系统外业人员需要离线采集点位、回到有网环境再同步参考的就是这部分的“离线编辑同步”示例。第三个是“Routing”包包含路径规划、方向导航、服务区分析等。需要注意的是这部分功能在 100.5 中需要单独配置路径分析服务光靠自带底图是跑不出结果来的。模块包名核心功能典型使用场景Map地图显示、基础交互、底图切换所有应用的基础能力Layers各类图层的加载与渲染业务数据的空间展示Analysis空间分析、几何测量选址、巡护、规划类应用Edit and Manage Data要素编辑、离线数据同步外业采集、内业处理Routing路径规划、导航、服务区分析出行、物流调度应用Scenes三维场景展示与分析城市级三维可视化3. 环境准备Android Studio 与 Gradle 配置细节3.1 配置一套能跑成功的开发环境这套示例代码虽然官方建议直接用 Android Studio 打开即可运行但如果你完全按默认设置来大概率会卡在 Gradle 同步这步。原因很简单100.5 这个版本发布时对应的 Gradle 插件版本和依赖仓库地址跟现在的最新环境已经有差别了。我建议你按下面这套环境组合来搭建实测兼容性最稳。JDK 版本1.8不要用高版本有些老版本 Gradle 对高版本 JDK 支持不太好容易报错Android SDKAPI 28 左右建议同时保留 28 以下的支持库兼容版本Gradle 插件版本3.3.2 左右Gradle wrapper4.7 左右构建工具版本28.0.3这几个版本的组合验证过多台机器同步基本没遇到过问题。如果你本地的 SDK 版本比我列的高一般也不影响因为 Gradle 会自动做向下兼容处理。但如果你直接把 Android Studio 的默认配置拿过来极大概率会遇到Failed to resolve: arcgis-android或者Could not find com.android.tools.build:gradle:x.x.x这类错误。3.2 build.gradle 中的依赖声明与关键参数打开根目录下的build.gradle你会看到示例代码通过 Maven 远程仓库引入 ArcGIS Runtime SDK。这里有个细节arcgis-android的依赖声明中包含了版本号 100.5.0这个版本号对应的是 Maven 上的一个具体发布版本改错一个数字就拉不下来。实际项目中我一般这样组织依赖在工程根目录的build.gradle里配置仓库allprojects { repositories { google() jcenter() maven { url https://esri.bintray.com/arcgis } } }然后在 app 模块的build.gradle里添加 SDK 依赖implementation com.esri.arcgisruntime:arcgis-android:100.5.0注意jcenter()目前已经停止服务了新项目里不需要再加但 100.5 时代它还在维护所以老工程里经常能看到。另外要提醒一点这是 SDK 的依赖不需要额外引入 Google Play Services 的定位依赖Runtime SDK 内部已经封装了定位组件的调用。第一次同步工程时由于要下载 SDK 的 AAR 包和相关传递依赖耗时可能比较长建议保证网络稳定。如果下载失败多试几次或者检查是否因为网络原因导致 Maven 仓库连接不稳定。3.3 许可初始化与基础地图加载ArcGIS Runtime SDK 100.5 已经全面转向基于开发者许可的授权模式。即使是试用你也需要先申请一个开发者 API Key开发者许可然后在代码中初始化。初始化一般放在 Application 的onCreate里或在使用地图的第一个页面前完成。public class App extends Application { Override public void onCreate() { super.onCreate(); // 设置许可替换成你自己的 API Key ArcGISRuntimeEnvironment.setLicense(your license key); // 关闭自带的开箱即用的调试日志输出 ArcGISRuntimeEnvironment.setApiKey(your api key); } }运行官方示例时如果初始化许可没写很多功能会直接抛LicenseException。我在测试过程中遇到过一个比较隐蔽的问题许可比 100.5 发布更晚的版本有时候行为表现不太一致代码能跑但某些分析功能会静默失败或返回空结果。所以如果你确认自己的逻辑没问题优先检查许可是否匹配。地图加载方面最简单的就是创建一张带底图的地图ArcGISMap map new ArcGISMap(Basemap.Type.TOPOGRAPHIC, 39.909, 116.397, 12); mapView.setMap(map);这里纬度和经度顺序是(latitude, longitude)很多人写的时候容易顺手写成(x, y)结果地图糊了一片白。这个细节我在刚开始接触的时候也吃过亏。4. 实际操作用示例代码快速实现图层展示与要素点击4.1 从示例列表到功能实现的完整流程如果你只是想快速验证一个想法不用从零写 Activity可以直接在官方示例的列表页找到对应功能点击进入后系统会加载实际的交互页面。比如你要实现一个“点击要素高亮显示”的需求就可以找到“Feature Layer Definition Expression”或“Feature Layer Query”类似功能的示例。以“点击要素图层的要素并弹出属性信息”为例核心逻辑是监听MapView的单击事件然后通过IdentifyLayerAsync方法识别被点击位置的要素。你可以直接看示例源码中怎么处理的核心代码大概长这样mapView.setOnTouchListener((view, motionEvent) - { if (motionEvent.getAction() MotionEvent.ACTION_UP) { final android.graphics.Point screenPoint new android.graphics.Point( Math.round(motionEvent.getX()), Math.round(motionEvent.getY()) ); identifyFeatureLayer(screenPoint); } return false; }); private void identifyFeatureLayer(android.graphics.Point screenPoint) { ListenableFutureListIdentifyLayerResult future mapView.identifyLayersAsync(screenPoint, 10, false); future.addDoneListener(() - { try { ListIdentifyLayerResult results future.get(); for (IdentifyLayerResult result : results) { ListIdentifyLayerResult.GeoElement elements result.getElements(); if (elements.size() 0) { GeoElement element elements.get(0); // 在这里读取 attributes 并显示 MapString, Object attributes element.getAttributes(); showAttributesDialog(attributes); } } } catch (Exception e) { Log.e(TAG, Identify failed: e.getMessage()); } }); }这里面有几个关键点需要重点关注。identifyLayersAsync里的第三个参数popupsOnly如果你不关心弹窗只在有配置时才响应一般传false容差参数我通常设置成 10 像素左右如果设备密度较高可以适当上调到 15-20否则容易出现点击不灵敏的情况。拿到结果后elements可能包含多个图层中的要素别忘了按图层区分避免弹错属性。4.2 不同数据源的图层加载方式对比示例代码里加载图层的方式非常全不同数据源的加载代码也有差异。这里我整理了 100.5 里最常见的几种并做了对比。图层类型数据源初始化方式常见坑点ArcGIS 在线切片图层在线服务 URLnew ArcGISTiledLayer(serviceUrl)服务没开启切片缓存时加载不出图要素图层Geodatabase 或 ServiceFeatureTableServiceFeatureTableFeatureLayer字段名大小写敏感查询结果集过大建议分页离线切片包.tpk 文件ArcGISTiledLayer初始化后设置路径路径不能放在应用私有目录外否则读取失败栅格图层本地影像文件new RasterLayer(new Raster(mRasterPath))影像文件过大时建议先做金字塔处理矢量切片矢量切片包new VectorTileLayer(path)样式文件需另外加载很多新手容易漏掉在这几个类型里最容易踩坑的是离线切片包的路径问题。100.5 里对文件访问的限制比旧版本严格得多如果文件路径不在应用能直接访问的私有目录范围内运行时会抛FileNotFoundException这类异常。正确的做法是把.tpk或.geodatabase文件放在Android/data/包名/files/下面或者用File对象动态创建到内部存储避免使用外置存储绝对路径。4.3 示例代码中的地图交互与符号应用地图交互和符号设置是示例代码中内容最多的一部分也是对实际项目帮助最大的。100.5 中的符号体系做了很大调整老的Symbol体系保留了下来同时又增加了很多新的渲染器方式。比如SimpleFillSymbol、SimpleLineSymbol、SimpleMarkerSymbol这一类是最基础的适合做简单的符号渲染。实际操作中我建议你优先参考“Renderers”包里的示例尤其是分类渲染和唯一值渲染。这两类在工作中使用频率极高。举个实际场景巡护 App 中要按巡护状态把点位分成“未处理、处理中、已完成”三类用唯一值渲染器就能一次配好三类符号不用每次画要素前手动判断再设置符号。ListUniqueValue uniqueValues new ArrayList(); uniqueValues.add(new UniqueValue(未处理, 未处理, new SimpleMarkerSymbol(SimpleMarkerSymbol.Style.CIRCLE, 0xFFFF0000, 12))); uniqueValues.add(new UniqueValue(处理中, 处理中, new SimpleMarkerSymbol(SimpleMarkerSymbol.Style.CIRCLE, 0xFFFFA500, 12))); uniqueValues.add(new UniqueValue(已完成, 已完成, new SimpleMarkerSymbol(SimpleMarkerSymbol.Style.CIRCLE, 0xFF00FF00, 12))); UniqueValueRenderer renderer new UniqueValueRenderer(); renderer.setFieldNames(Arrays.asList(status)); renderer.setUniqueValues(uniqueValues); featureLayer.setRenderer(renderer);这段代码跑起来之后图层的符号会根据要素属性里的status字段自动匹配。要注意的是setFieldNames需要传入一个 List不是单个字符串。另外渲染器设置的时机也很关键——如果要素图层还没完全加载完成就设置渲染器有时候会不生效所以最好在图层加载状态监听回调里设置或者直接在创建图层的代码里先设置再添加到地图。5. 离线数据与本地地理数据库的使用5.1 Geodatabase 的创建与读取离线能力是移动 GIS 比较核心的场景ArcGIS 100.5 在这块的支持相对成熟。在示例代码里与离线数据相关的主要在“Edit and Manage Data”和“Layers”两个包下面。你可以先用 ArcGIS Pro 或 Server 发布一个要素服务然后用 Runtime SDK 的GeodatabaseSyncTask下载数据生成本地 geodatabase。GeodatabaseSyncTask syncTask new GeodatabaseSyncTask(serviceUrl); ListenableFutureGeodatabase future syncTask.createGeodatabaseAsync(geodatabasePath); future.addDoneListener(() - { try { Geodatabase geodatabase future.get(); // 拿到 geodatabase 后遍历它的 FeatureTable for (FeatureTable table : geodatabase.getFeatureTables()) { FeatureLayer layer new FeatureLayer(table); map.getOperationalLayers().add(layer); } } catch (Exception e) { Log.e(TAG, create geodatabase failed, e); } });这段逻辑本身不复杂但实际跑起来容易遇到版本同步方面的错误。建议同步开始时检查本地 geodatabase 路径所在目录是否存在目录不存在直接创建否则createGeodatabaseAsync会报文件系统错误。另外当 geodatabase 用于离线编辑时GeodatabaseSyncTask需要指定同步方向不能随手填否则离线提前编辑的数据可能被覆盖。5.2 本地 Shapefile 和栅格文件的加载示例除了 geodatabase项目中也经常要直接读 Shapefile 和栅格图层文件尤其是测绘、国土相关的项目拿到的外业数据基本都是 Shapefile 格式。100.5 的 Runtime SDK 对 Shapefile 的支持做得挺不错加载方式也很简单。ShapefileFeatureTable shapefileTable new ShapefileFeatureTable(shapefilePath); FeatureLayer shapefileLayer new FeatureLayer(shapefileTable); map.getOperationalLayers().add(shapefileLayer);这里的shapefilePath必须是本地存储的绝对路径不要使用file:///android_asset/开头的路径。如果想打包到 Assets 里第一次启动需要先把文件复制到应用私有目录再走上面这套逻辑。栅格文件也是类似的思路官方示例用的是Raster和RasterLayer两个类。我觉得这里最需要注意的一点是栅格文件如果特别大比如几百 MB 的影像直接加载很容易导致内存溢出或卡顿。可以先借助桌面端的影像处理工具对文件做金字塔统计降低打开时的计算压力。在移动端实测下来金字塔化之后的影像加载速度能提升 2-3 倍体验差距非常明显。6. 常见错误与排查经验6.1 白屏、图层不显示这类问题遇到的概率最高。图层不显示首先看地图底图有没有加载出来如果底图正常但业务图层不显示依次排查这几项要素服务的 URL 是否在 100.5 下仍然有效有些老服务默认输出的版本过低SDK 兼容性会有问题图层的可见范围限制如果当前地图缩放级别不在服务设定的范围内图层自然不显示坐标系是否匹配底图是 Web Mercator业务数据是 CGCS2000 投影坐标叠加后位置偏移极远看起来就像没加载这里最常见的就是坐标系问题。ArcGIS Runtime 100.5 支持动态投影但前提是你得正确声明业务图层的坐标系信息。如果 Shapefile 没有配套的.prj文件SDK 无法判断坐标系默认可能按 WGS84 处理就会导致叠加位置错误。解决方法是先用桌面端工具查看坐标系并补齐.prj文件。6.2 许可导致的不稳定异常许可相关的报错在不同版本 SDK 之间表现差异很大。100.5 中如果许可证未设置或类型不对有些功能不会直接崩溃而是走完流程后返回空结果。这个问题排查起来比较费时间因为你很难从代码层面直接判断是哪一步出了问题。我的建议是在应用启动早期就统一初始化许可和 API Key并且集中写在一个工具类里方便统一更换。申请许可时最好用正式开发许可试用的许可有时限而且部分高级功能可能受限实际项目测试容易误判成代码问题。6.3 构建层面的兼容问题如果同步阶段就已经报错比如Failed to resolve: arcgis-android优先检查仓库地址是否写对、网络是否能访问到https://esri.bintray.com/arcgis。仓库连接有问题时可以尝试更换网络后再试。另有一种情况是 Gradle 版本和 SDK 要求不一致全都换成我前面列的那一组版本基本可以解决。还有一个小概率问题如果你电脑上同时配置了多个版本的 Android SDK而local.properties里的sdk.dir指向了错误的目录也会导致构建失败。检查一下这个文件确保 SDK 路径指向正确目录。6.4 常见问题排查速查表现象可能原因处理办法同步报错找不到 SDK 包仓库地址失效或未配置换用可用的 Maven 仓库重新同步地图白屏许可未初始化或 API Key 失效检查ArcGISRuntimeEnvironment初始化代码图层偏移不显示坐标系不匹配确认数据和底图的坐标系并正确声明要素点击无反应容差设置太小调大identifyLayersAsync的容差参数离线数据加载崩溃路径不可访问把文件放到应用私有目录下再读取符号不生效渲染器设置时机太晚确保在图层添加到地图前设置渲染器7. 实际项目中的扩展建议如果你打算把这套示例代码当成项目的地基来用我还有几个建议。首先正式项目里不要直接修改官方示例的源码而是以它为参照单独建项目。官方示例的包名和资源命名是为示例场景服务的直接改很容易留下隐患。我一般是把示例引用到设计文档里作为功能对照清单然后逐个模块在正式工程里实现。其次示例代码里很多 UI 交互写得很基础主要是为了演示功能不适合直接用到产品中。比如符号选择、属性编辑这种交互生产环境一般会重新设计。这块建议保留核心业务逻辑重写界面展示。最后我建议你用这套代码建一个内部维护的“技术样例池”。每遇到一个需求就先到官方示例里找有没有对应能力找到了就复制到自己的样例池里验证跑通之后再写进正式工程。时间长了这个样例池就是团队的移动端 GIS 开发字典比任何文档都实用。我在实际项目里维护了这样一个样例池之后新同事上手项目的速度明显加快至少不用再对着 API 文档摸黑走路了。以后你从 100.5 往更高版本升的时候也能借助这套样例池快速对比 API 变化降低迁移成本。本文还有配套的精品资源点击获取