资讯动态

ArcGIS for Android 100.5示例代码全解析:从导入到避坑

发布时间:2026/10/15 19:50:40 来源:尧图企业网站定制
简介Android GIS开发中地图初始化、图形绘制与离线数据加载是高频需求而不同版本SDK的API差异常让开发者陷入迁移困境。ArcGIS for Android 100.5作为架构分水岭以MapView、GraphicsOverlay等组件重新定义了地图应用构建方式其官方示例代码涵盖坐标转换、符号渲染、图层查询等完整功能链路。通过剖析工程结构、核心API调用及生命周期管理可以快速掌握从Gradle配置到真机调试的实践路径并规避坐标偏移、黑屏、识别为空等常见问题。文章结合离线geodatabase与要素加载场景帮助开发者将示例工程转化为可持续迭代的项目骨架尤其适合从旧版10.x迁移或初次接触100.x架构的Android GIS工程师。1. 为什么 100.5 这个版本值得你从头到尾跑一遍示例做 Android 端 GIS 开发的人多半都经历过这样的场景项目工期紧地图刚初始化完又要加图形绘制、属性查询、离线切片手忙脚乱地去翻官方文档、翻论坛、翻别人的博客结果发现版本对不上API 名字对不上连包名都变了。ArcGIS for Android 100.5 正好处在一个分水岭上——它把旧的 10.x API 彻底甩开全面转向 100.x 的新架构异步任务、地理数据库、离线地图、符号渲染这些能力都变得比之前清晰得多。这个版本的完整示例代码是很多人入门和迁移时真正踩过的路我就是靠着它把项目从 10.2 迁过来的也在里面踩过不少坑。这篇文章要讲的不只是把示例代码下载下来跑通而是把它的工程结构、核心 API、坐标与符号、离线与查询、以及最容易翻车的地方一个个拆开给你看。不管你是第一次接触 ArcGIS for Android还是已经写了一阵子但总被某些报错卡住这篇文章都能给你一条可复现的路径。尤其是那些在 100.5 里改了名字、换了写法的 API网上资料零零散散这里一次讲透。2. 示例代码的工程构成与两种运行方式2.1 用 Gradle 把工程拉起来之前先搞清楚它里面有什么ArcGIS for Android 100.5 的完整示例代码官方发布形式是一个包含多个模块的 Android Studio 工程。常见做法是代码库里既有arcgis-android-sdk-sample这样的主示例模块也有独立的java和kotlin两种语言版本示例模块。这个设计很实在因为当时很多团队还在 Java 和 Kotlin 之间摇摆官方直接给两套省得你自己翻译。我第一次打开这个工程的时候印象最深的是它的模块划分每个示例都是一个独立的 Activity 或 Fragment按照功能分类放在不同的包下面比如display地图显示、analysis分析、geometry几何、features要素、layers图层、maps地图操作等等。这种组织方式的好处是你想看哪个功能直接进对应包代码量小、依赖清晰不像有些开源项目把几百个类堆在一起翻半天找不到入口。在跑起来之前你需要确认两件事。第一Android Studio 版本不能太低100.5 的示例工程用的 Gradle Plugin 版本和 AGP 版本有一定要求太老的 IDE 会直接同步失败第二SDK 的本地 Maven 仓库路径要配置对因为 ArcGIS for Android 的 SDK 不是直接从 Google 的 Maven 中央仓库拉取的而是通过 ArcGIS 自己的 Maven 仓库或者本地仓库文件来引用的。这一步如果配错后面所有依赖都会标红。配置本地仓库的常见写法如下在项目根目录的build.gradle里添加allprojects { repositories { google() mavenCentral() maven { url https://esri.bintray.com/arcgis } } }esri.bintray.com/arcgis是 100.5 时代官方指定的 Maven 仓库地址SDK 的.aar包就是从这里面拉的。后来 Bintray 服务关停后官方迁移到了新地址但在 100.5 这个版本上旧地址依然有效。如果你用新版本 SDK 的工程模板去套 100.5这里就要改。我一般会先单独编译一个空项目来验证仓库可达性再做示例工程导入这样能把网络问题和工程问题分开排查。2.2 用 Android Studio 导入示例代码需要注意的四个细节把示例代码跑起来步骤不多但细节不少。我直接在 Android Studio 里用Open选择示例工程根目录等 Gradle 同步完成。这里有几个容易翻车的地方我按踩坑顺序列给你。第一个是 JDK 版本。100.5 的示例工程官方推荐 JDK 8但你本机如果装的是 JDK 11 或更高Gradle 同步有时候会报Unsupported class file major version之类的错误。这不是示例代码的问题而是 Gradle 版本和 JDK 版本不匹配导致的。解决方法是把 Android Studio 里的 Gradle JDK 版本切到 8 或 11具体看 Gradle wrapper 版本我一般直接切到项目要求的版本。第二个是 Android SDK 版本。示例工程里compileSdkVersion一般在 27 或 28 左右你的本机如果没有安装对应版本的 SDKAndroid Studio 会提示你下载。这个下载过程在国内网络环境下可能很慢我用的是代理加手动下载 SDK 平台包的方式避免卡在自动下载上。第三个是签名问题。示例代码里很多功能比如在线地图、地理编码、路径分析都需要 API Key 才能跑。100.5 的示例代码里有一个res/values/strings.xml或者assets目录下的配置文件会预留一个api_key字段你需要去开发者后台申请 Key 填进去。如果不填地图底图会加载失败但矢量图形绘制这种本地功能不受影响。第四个是模拟器还是真机。我建议直接用真机调试因为 100.5 的地图渲染对 OpenGL ES 版本有要求部分模拟器的显卡驱动不支持会出现黑屏或者地图瓦片加载不出来。真机上虽然也会遇到问题但至少环境更接近生产。工程跑起来之后你会在 Android Studio 的 Run 配置里看到很多个示例入口选一个你关心的功能跑就行。比如我想看地图标注怎么加就直接进GraphicsOverlay相关的示例点运行地图加载出来标注显示在屏幕上整个链路就通了。这一步通了后面所有功能都能顺着这个链路去改。3. 把 100.5 的示例代码拆开核心 API 和功能模块的对应关系3.1 地图初始化的新写法MapView、ArcGISMap 和 Basemap100.5 的地图初始化和旧版 10.x 完全是两套思路。旧版是MapView直接绑定一个ArcGISTiledMapServiceLayer新版则引入了ArcGISMap这个顶层容器底图、图层、初始视图都往里面塞。代码结构上我一般这样写MapView mapView findViewById(R.id.mapView); ArcGISMap map new ArcGISMap(Basemap.Type.Topographic, 34.05, -118.25, 12); mapView.setMap(map);这里Basemap.Type.Topographic表示地形底图34.05和-118.25是初始中心点经纬度12是初始缩放级别。mapView 的生命周期管理很重要100.5 里必须重写 Activity 的onResume、onPause和onDestroy分别调用mapView.resume()、mapView.pause()和mapView.dispose()。如果不调用这几个方法地图在屏幕旋转或者页面切换时会出现内存泄漏和崩溃尤其是dispose()漏掉后反复进出页面内存会一路涨上去最后直接 OutOfMemory。示例代码里每个示例都写好了生命周期处理这也是我建议你直接基于示例改而不是从空项目开始写的原因之一。Basemap除了Topographic还有Streets、Imagery、Oceans等类型你也可以用Basemap.fromUrl()加载自定义底图服务。我在实际项目里用的是自己发布的内网切片服务写法就是Basemap.fromUrl(http://xxx/arcgis/rest/services/xxx/MapServer)这样不依赖外网底图在业务内网环境下也能正常跑。3.2 图形绘制与 GraphicsOverlay用 Graphic 表达点线面地图上画点、画线、画面在 100.5 里统一走GraphicsOverlayGraphicSymbol这套组合。以前 10.x 里GraphicsLayer的老写法已经废弃迁移过来的人最容易在这里报错因为找不到GraphicsLayer这个类了。先看一个画点的示例GraphicsOverlay graphicsOverlay new GraphicsOverlay(); mapView.getGraphicsOverlays().add(graphicsOverlay); Point point new Point(-118.25, 34.05, SpatialReferences.getWgs84()); SimpleMarkerSymbol symbol new SimpleMarkerSymbol(SimpleMarkerSymbol.Style.CIRCLE, 0xFFFF0000, 12); Graphic graphic new Graphic(point, symbol); graphicsOverlay.getGraphics().add(graphic);GraphicsOverlay相当于一个承载图形对象的图层Graphic是单个图形Symbol决定图形长什么样。SimpleMarkerSymbol.Style.CIRCLE表示圆形0xFFFF0000是红色 ARGB 颜色值12是像素大小。如果你要画线用Polyline配合SimpleLineSymbol画面用Polygon配合SimpleFillSymbol。这一套组合在 100.5 里非常稳定性能也比 10.x 好很多因为渲染引擎重写了大量图形叠加时帧率不会掉得那么快。我踩过的一个坑是坐标系。Point构造的时候如果不指定SpatialReference默认是 Web 墨卡托但你业务数据大多用的是 WGS84 经纬度画出来位置会偏到海里。示例代码里大部分 Point 都显式传了SpatialReferences.getWgs84()我后来自己写的时候也养成这个习惯。3.3 图层加载ArcGISMapImageLayer 与服务图层的区别100.5 里的图层类型比 10.x 清晰很多。ArcGISMapImageLayer对应动态地图服务ArcGISMapServerLayer对应要素服务ArcGISTiledLayer对应切片服务。一开始我很不理解为什么要把图层类型拆这么细后来明白了不同类型的服务在服务端渲染方式、缓存策略、查询能力上都不一样客户端拆分开才能在底层做针对性优化。动态地图服务的加载示例ArcGISMapImageLayer imageLayer new ArcGISMapImageLayer(http://xxx/arcgis/rest/services/xxx/MapServer); map.getOperationalLayers().add(imageLayer);注意这里用的是getOperationalLayers()也就是操作图层集合底图是单独管理的。操作图层叠加在底图之上可以控制透明度、可见范围、图例信息。在 100.5 里图层的可见性控制比旧版方便很多直接imageLayer.setVisible(false)就行不会影响其他图层。加载服务图层的时候最常遇到的问题就是服务地址不可达报错信息一般是Layer failed to load。这个报错太笼统了我通常会先用自己的网络工具确认地址能访问再去看服务是否是 HTTPS 混合内容问题。Android 9 之后默认禁止 HTTP 明文流量如果你的服务是http://开头必须在AndroidManifest.xml里配置android:usesCleartextTraffictrue或者用networkSecurityConfig单独放行。示例代码里的服务地址大多是https但你自己换成本地服务时这一步最容易漏。3.4 属性查询与识别Identify 操作在 100.5 里的正确姿势在一张地图上点击要素查看属性是 Web GIS 开发里最常见的需求。100.5 里对应的 API 是IdentifyLayerAsyncTask示例代码里也有完整实现。它的基本逻辑是把点击位置的屏幕坐标转成地图坐标然后在地图坐标周围一定容差范围内对指定图层做识别。Point mapPoint mapView.screenToLocation(screenPoint); IdentifyLayerAsyncTask task new IdentifyLayerAsyncTask(imageLayer, mapPoint, 10, false, new IdentifyLayerAsyncTask.IdentifyLayerAsyncTaskListener() { Override public void onCompleted(IdentifyLayerResult identifyLayerResult) { ListGeoElement elements identifyLayerResult.getElements(); for (GeoElement element : elements) { MapString, Object attributes element.getAttributes(); // 遍历属性字典 } } Override public void onError(Exception e) { // 处理识别失败 } }); task.execute();识别容差10表示屏幕像素的容差范围值越大越容易命中但也更容易误判。false表示只识别当前图层不递归识别子图层。getAttributes()返回的是一个 Map键是字段名值是属性值拿到之后可以直接填充到 UI 上。这个流程在 10.x 里是通过IdentifyTask异步完成的100.5 里改成了IdentifyLayerAsyncTask名字变了参数顺序也变了老代码迁移过来必须逐行改。我还遇到过一个情况就是识别结果一直为空但图层数据确实存在。后来排查发现是因为我没有等待图层完全加载完成就发起了识别请求图层还在加载中服务端自然识别不到。解决办法是在图层加载监听器的回调里再触发识别或者先判断imageLayer.getLoadStatus() LoadStatus.LOADED。4. 坐标转换与符号渲染示例代码里最容易看懂的参数设置4.1 WGS84 与 Web 墨卡托的转换以及你迟早要遇到的坐标系坑ArcGIS for Android 100.5 底图默认是 Web 墨卡托EPSG:3857但业务数据大多是 WGS84EPSG:4326。如果你直接拿 WGS84 的坐标点去构造一个 Web 墨卡托空间参考下的 Graphic显示位置会完全错乱。示例代码里的几何对象绝大多数创建时都指定了SpatialReferences.getWgs84()这样几何对象自身是 WGS84 坐标MapView 会自动做投影转换渲染。但有一种情况比较隐蔽你从 XML 或者服务端接口拿到的坐标字符串可能混用了坐标系有的字段是 WGS84有的已经是投影坐标。我通常的做法是先统一规定后端返回的数据必须带SpatialReference信息或者干脆全部使用 WGS84 经纬度。如果确实需要做转换用GeometryEngine.project()Point sourcePoint new Point(x, y, SpatialReferences.getWgs84()); Geometry projectedGeometry GeometryEngine.project(sourcePoint, SpatialReferences.getWebMercator());GeometryEngine.project()的第一个参数是源几何第二个是目标空间参考。这个方法支持点、线、面等所有几何类型内部做了坐标转换。我在做市政设施点位展示时后端返回的是地方坐标系我就是先用这个接口把坐标统一转成 WGS84再往地图上加图标才解决了位置偏移的问题。4.2 符号类型与颜色参数SimpleMarkerSymbol 和 PictureMarkerSymbol 怎么选100.5 的符号系统分为两层图层级符号Renderer和图形级符号Symbol。图形级符号又分很多具体类最常用的是SimpleMarkerSymbol、SimpleLineSymbol、SimpleFillSymbol、PictureMarkerSymbol和TextSymbol。SimpleMarkerSymbol是纯矢量绘制点的大小、颜色、样式都是参数控制的。PictureMarkerSymbol是用本地图片或者 URL 图片作为图标需要传图片的 Drawable 或者路径。实际项目里业务图标大多是图片比如警车、变电站、消防栓用PictureMarkerSymbol更贴合需求。我建议你优先用矢量符号做调试因为颜色和大小改起来快图片符号还要考虑尺寸适配一张图在 mdpi 和 xxhdpi 屏上显示大小差很多。如果要用图片符号代码里会写成PictureMarkerSymbol symbol new PictureMarkerSymbol(getDrawable(R.drawable.fire_hydrant)); symbol.setHeight(36); symbol.setWidth(36); Graphic graphic new Graphic(point, symbol);注意setHeight和setWidth的单位是像素如果不设置图片会按原始尺寸显示有时候一张 200x200 的图片会把整个地图挡住。我一般在加载图片符号后强制设置宽高避免低分辨率图片在地图上显示过大。4.3 文本标注与偏移TextSymbol 的 alignment 参数地图上显示文字标注TextSymbol是最直接的手段。示例代码里有一个专门展示文本标注的模块核心参数是text、color、size、horizontalAlignment和verticalAlignment。我看到很多新手在这里翻车文字位置总是偏的因为默认对齐方式不是你想的那样。TextSymbol textSymbol new TextSymbol(16, 消防栓, 0xFF333333, TextSymbol.HorizontalAlignment.CENTER, TextSymbol.VerticalAlignment.MIDDLE); Graphic textGraphic new Graphic(devicePoint, textSymbol);HorizontalAlignment.CENTER让文字水平居中于锚点VerticalAlignment.MIDDLE让文字垂直居中于锚点。如果你想要文字显示在点位上方或下方就调整这两个对齐参数。16是文字大小单位是像素。字号在不同屏幕密度下的实际显示大小会有差异我在平板上调试时会把字号调大一两档。一个常见的需求是让文字和图标同时显示比如消防救援点的图标上方再挂一个名称。这时候需要给图标和文字各建一个Graphic放在同一个GraphicsOverlay里位置错开。直接把文字偏移几个像素不现实因为不同屏幕密度下偏移量不一样更好的办法是使用Graphic的yOffset属性单位是 DIP。5. 避坑指南100.5 示例代码从导入到上线的 5 个常见问题排查5.1 地图黑屏或底图加载不出来先查 API Key 和网络现象示例代码运行后能看到 UI 控件但地图区域一片黑或者空白的格子。原因最常见的是没有配置 API Key或者配置了错误的 Key。其次如果用的是 HTTP 明文服务Android 9 默认禁止加载日志里会出现Cleartext HTTP traffic not permitted的异常。再者模拟器对 OpenGL 的支持不足也可能导致黑屏。解决第一步检查strings.xml里的api_key是否填写正确并且确认该 Key 对应的包名和签名指纹与当前调试应用一致。第二步如果是 HTTP 地址在AndroidManifest.xml的application标签上添加android:usesCleartextTraffictrue。第三步换到真机上运行排除模拟器渲染问题。5.2 Gradle 同步失败版本号冲突第一反应不该是改依赖现象导入工程后 Gradle 同步一直失败报错信息指向某个依赖包的版本不存在或者冲突。原因100.5 的示例工程在当时用的依赖版本是固定的而新版本的 Android Studio 会自动尝试用最新的 Gradle Plugin 去解析导致版本不兼容。还有一个可能是你的本机 SDK Manager 里没有对应版本的compileSdkVersion。解决先不要乱改依赖版本。看工程的gradle-wrapper.properties里指定的 Gradle 版本再看根目录build.gradle里的 AGP 版本以及app/build.gradle里的compileSdkVersion。三者之间必须匹配。我的做法是让 Android Studio 提示的 SDK 版本去下载AGP 版本保持示例工程原值除非报错明确指出必须升级。5.3 识别结果为空检查图层的加载状态现象地图上点击要素回调正常执行但getElements()返回的列表是空的。原因图层尚未加载完成就执行了识别服务端没有数据可识别。还有一种可能识别容差太小点不够精确目标要素的像素范围没被点击命中。解决先确认图层getLoadStatus()是否为LOADED如果不是注册LoadStatusChangedListener在加载完成后再绑定点击事件。另外把容差从 10 调到 20 或者 30 再试排除点击偏差。如果仍然为空回到服务端用 REST 接口测试识别是不是正常。5.4 图形位置偏移图形和底图不在同一个位置先怀疑坐标系现象用 WGS84 坐标画了一个点但点的位置显示在海里或者相对底图整体偏移很远。原因构造Point时没有指定空间参考默认按 Web 墨卡托处理。或者通过MapView.screenToLocation()得到的坐标是 WGS84但你直接用成了服务坐标系。解决统一坐标系。调试时先用graphic.getGeometry().getSpatialReference()打出来看是什么坐标系再决定是否需要GeometryEngine.project()。一般来说后端数据统一转成 WGS84 经纬度再进客户端客户端不转坐标交给 MapView 做渲染转换是最省事的。5.5 页面退出后崩溃MapView 生命周期没管好现象Activity 正常关闭但日志报MapView is already disposed或者过一会儿内存溢出崩溃。原因没有正确调用mapView.dispose()或者在一个已经 dispose 的 MapView 上再次调用 resume / pause。解决严格按照示例代码的生命周期写法Override protected void onResume() { super.onResume(); mapView.resume(); } Override protected void onPause() { super.onPause(); mapView.pause(); } Override protected void onDestroy() { super.onDestroy(); mapView.dispose(); }这里dispose()一定要在super.onDestroy()之后或者之前我的习惯是放在super.onDestroy()之前先把地图资源释放掉再销毁 Activity 其他资源。如果用了 Fragmen 场景MapView 的生命周期跟随 Fragment需要额外注意重复创建时的资源释放。这一条是示例代码里做得最好的部分照着抄就行了不要自创。6. 把示例代码改造成自己的项目骨架三个值得保留的工程化习惯示例代码看完了坑也踩得差不多了接下来最重要的事情是把它消化成自己项目的地基。我自己的经历是把示例工程里的若干个功能模块抽取出来组合成了一个小型地图应用骨架后来在这个骨架上迭代了好几个项目省掉了大量重复劳动。第一个值得保留的习惯是FeatureLayer配合FeatureTable的方式加载本地或服务端要素。100.5 里要素表可以从 ArcGIS Server 要素服务创建也可以从本地 Geodatabase 创建。我经常用本地 Geodatabase 来做离线场景在网络不通的时候数据依然能查能画。关键代码是Geodatabase geodatabase new Geodatabase(path/to/local.geodatabase); FeatureTable featureTable geodatabase.getGeodatabaseFeatureTableByName(fire_hydrant); FeatureLayer featureLayer new FeatureLayer(featureTable); map.getOperationalLayers().add(featureLayer);这段代码看起来简单但离线数据的更新策略才是真正的重点。我一般会在应用启动时检查服务端数据版本如果有更新就把新的 Geodatabase 下载下来覆盖本地的。这个逻辑写起来不难但它决定了离线功能能不能真正落地而不是做成一个摆设。第二个值得保留的习惯是SpatialReference穿透到底层数据模型。我在示例代码基础上封装的工具类里所有坐标数据的输入输出都强制带上空间参考不允许出现无坐标系的数据。这个强制要求在团队协作中尤其重要不然每个新人都可能把坐标系搞混地图效果就会非常混乱。从底层避免这类问题比事后排查高效得多。第三个习惯是把地图的 UI 状态和业务数据分离。示例代码里GraphicsOverlay上的图形只是渲染层真正的业务数据是放在自己的业务对象里的。点击一个图形我用graphic.getAttributes()拿到业务 ID再去数据源查详情。这种设计在项目大了以后非常有用否则地图越来越复杂图形和业务数据纠缠在一起后期改一个字段都要在地图层面上处理半天。还有一个很实用的技巧在开发调试阶段把示例工程自带的错误监听器保留开发的时候日志输出得越详细越好。ArcGIS for Android 的很多错误信息是异步回调返回的不挂监听器就看不到具体内容。等到上线前再把这些日志打成 release 不输出既不影响调式体验也避免了生产环境日志过多。如果你正在基于 100.5 做项目我强烈建议你花一两天时间把示例代码完整跑一遍不要只跑你想用的那一个功能。很多 API 之间的调用逻辑是相通的比如空间查询和属性查询的返回类型、图层加载状态的处理方式用过一个功能再看另一个理解成本会降低很多。我做迁移的那段时间就是先跑Display map这个最基础的示例再逐个看Feature layer query、Identify layer、Change symbol color跑通三四个之后整个 SDK 的套路就摸清楚了。希望这些踩过的坑和沉淀下来的习惯能帮到你让你在 ArcGIS for Android 上少走弯路。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑