拓冰建站拓冰建站
首页 / 资讯中心 / 正文

GeoLibre Android 原生应用构建与发布指南:基于 Tauri v2 Mobile 的 APK 打包、签名与 Google Play 上架全流程

GeoLibre Android 原生应用构建与发布指南基于 Tauri v2 Mobile 的 APK 打包、签名与 Google Play 上架全流程【免费下载链接】GeoLibreA lightweight, cloud-native GIS platform for visualizing, exploring, and analyzing geospatial data. It runs in the web browser, on the desktop, on mobile, and inside Jupyter notebooks.项目地址: https://gitcode.com/GitHub_Trending/ge/GeoLibre导读本文是 GeoLibre 在 Android 平台上的完整开发与发布手册覆盖从工具链搭建、APK/AAB 构建、16 KB 页面对齐、签名验签、CI 自动化、真机/模拟器安装测试到 Google Play Console 上架与geo:深链接入的全部环节。读完本文你将掌握为什么 GeoLibre 无需独立的 Android 代码库即可成为原生应用、如何在本地复现其官方签名构建流程、如何规避 16 KB 内存页对齐与包名覆写等坑以及如何为分叉版本或你自己的 GIS 应用跑通 Play 商店发布管线。架构基础同一份 React 代码库Tauri v2 Mobile 原生打包GeoLibre 的 Android 版本并非一套独立应用而是与桌面端共享同一份 React 代码库通过Tauri v2 mobile打包为原生 Android 应用。WebView 界面被打包进 APK 内部因此应用外壳支持离线运行地图瓦片与较重的引擎则按需拉取与桌面构建行为一致。从仓库源码可以看到这一架构的直接落地apps/geolibre-desktop/src-tauri/tauri.android.conf.json 将 Android 构建的productName设为GeoLibre桌面构建名为 GeoLibre Desktopidentifier覆写为org.geolibre.app并通过清空bundle.resources把 Python 后端从 Android 包里剔除gen/android目录是npx tauri android init生成的git-ignored按需再生成Android 端无需手写 Kotlin/Gradle 业务代码Android 包名的覆写放在tauri.android.conf.json而非全局tauri.conf.json是有意为之identifier是全平台共享的同时决定 macOS bundle ID、Linux AppStream ID 与 WebView 数据目录。若全局改动会遗弃既有桌面用户的设置并破坏仍以org.geolibre.desktop为键的 Linux/COPR/Homebrew 打包详见 docs/android.md。安装途径Google Play 与侧载 APKGeoLibre 在 Google Play 上以org.geolibre.app发布Play 构建已签名并自动更新。若偏好侧载每个 release 都会附带按 ABI 拆分的签名 APK可参考 docs/downloads.md#android-installation 获取下载说明。本文其余部分面向自行构建应用的开发者。Android 与桌面的能力差异哪些功能在移动端被隐藏Android 构建自带完整的地图工作区、添加数据Add Data、Whitebox 地理处理工具箱1000 工具通过 WebAssembly 在浏览器内运行、矢量工具Turf.js / 基于 Pyodide 的浏览器内 GeoPandas、SQL 工作区DuckDB-WASM 与浏览器内 PGlite/PostGIS 引擎、Python 控制台Pyodide、地理编码、统计、AI 助手、故事地图与插件。依赖本地桌面进程的工具在移动端被隐藏因为 Android 上没有 Python sidecar 或本地辅助二进制处理 → GeoLibre Toolbox →栅格Raster、格式转换Conversion、AI 分割AI Segmentation都需要 Python sidecar添加数据 →PostgreSQL由本地 Martin 瓦片服务器提供。这些工具通过基于 user-agent 的isMobile()检查进行门控因此 Add Data 菜单与图层面板Layer panel的 add-data 分组中从不出现这些入口其余功能全部在客户端运行。源码视角isMobile()与isDesktopRuntime()门控逻辑集中在 apps/geolibre-desktop/src/lib/is-mobile.tsisMobile()用/Android|iPhone|iPad|iPod/i正则匹配 UA并额外通过isIpadDesktopUserAgent(userAgent, maxTouchPoints)处理 iPadOS 13 伪装桌面 Macintosh UA 的情况多触点能力区分真实 MacisAndroid()用/Android/i做更窄的检查用于 Android 行为不同于 iOS 的平台 API如原生文档选择器的 MIME 过滤isDesktopRuntime()定义为isTauri() !isMobile(...)——其中isTauri()仅检查__TAURI_INTERNALS__ in window见 apps/geolibre-desktop/src/lib/is-tauri.ts。为什么不能只用isTauri()门控因为 Tauri 在打包的 Android/iOS 应用中同样为 true。若用isTauri()单独门控平板/手机用户会一路走到 sidecar 连接并报出原始的 could not connect to the sidecar at 127.0.0.1:8765 错误——这正是 GeoLibre#2091 在 iPadOS 上报的问题。isDesktopRuntime()才是桌面 Tauri 外壳的正确判定。PostgreSQL 有一个isMobile()检查覆盖不到的入口Browser 面板为发现目的在所有平台保留Databases数据库分区其 仍会打开 PostgreSQL 对话框。该对话框用isDesktopRuntime()isTauri() !isMobile()门控而非单独isTauri()因此在 Android 上显示requires GeoLibre Desktop提示并禁用 Connect而不是去调用一个不可能存在的 sidecarGeoLibre#2091。具体实现见 apps/geolibre-desktop/src/components/layout/add-data/sources/PostgresSource.tsx组件用useMemo(() isDesktopRuntime(), [])求值一次UA 在会话内稳定非桌面运行时渲染desktopOnlyNotice提示且 Connect 按钮disabled{source.isSubmitting || !desktopRuntime}handleConnectEditable与handleConnectPostgres里也都有if (!desktopRuntime) throw new Error(...)的防御性二次检查。工具链搭建一次性需要 Android SDK NDK、一个 JDK17 或 21——更新的 JDK 可能破坏 Android Gradle Plugin以及 Rust 的 Android 目标。最干净、无需 sudo 的布局是把所有内容放在用户可写的~/Android/Sdk下。# 1. JDK 17/21或复用 Android Studio 自带的 JBR位于 /opt/android-studio/jbr export JAVA_HOME/path/to/jdk-21 # 2. Android SDK 组件sdkmanager 随 Android Studio cmdline-tools 提供 export ANDROID_HOME$HOME/Android/Sdk yes | sdkmanager --sdk_root$ANDROID_HOME --licenses sdkmanager --sdk_root$ANDROID_HOME \ platform-tools platforms;android-36 \ build-tools;36.0.0 ndk;27.3.13750724 export NDK_HOME$ANDROID_HOME/ndk/27.3.13750724 # Tauri 需要 NDK_HOME # 3. Rust 四个 Android 目标没有 rustup 先安装 rustup target add aarch64-linux-android armv7-linux-androideabi \ i686-linux-android x86_64-linux-android要点NDK r27 (LTS)是 Tauri v2 支持的主线把这四个export写进 shell profile每个会话都有API 36Android 16而不是 34Tauri v2.11 Android 模板生成compileSdk 36/targetSdk 36Gradle 需要安装匹配的 platform它同时也是 Google Play 的最低门槛——从 2026-08-31 起新应用与更新必须 target API 36 才会被接受。仓库 CI 在 .github/workflows/android.yml 中与此完全一致ANDROID_PLATFORM: platforms;android-36、ANDROID_BUILD_TOOLS: build-tools;36.0.0、ANDROID_NDK_VERSION: 27.3.13750724并显式注释Keep these in sync with docs/android.md and the local setup。16 KB 内存页对齐Google Play 会拒绝 target Android 15 且原生库未按16 KB 内存页对齐的应用且此类库在 16 KB 设备上会加载失败。NDK r28 默认对齐r27 不会因此需显式传参。在 Android 构建前导出CI 在 workflow 级别设置见 .github/workflows/android.ymlexport RUSTFLAGS-C link-arg-Wl,-z,max-page-size16384 -C link-arg-Wl,-z,common-page-size16384必须是RUSTFLAGS环境变量。把同样的 flag 写进.cargo/config.toml的target.triple.rustflags是不生效的Tauri CLI 在调用 cargo 构建 Android 时会自己设置RUSTFLAGS而环境变量RUSTFLAGS会整体覆盖配置文件。config 文件形式会被静默忽略——它照常解析、照常构建最后交付的是 4 KB 对齐的库直接被 Play 拒绝。Tauri 会向继承的值追加所以导出环境变量有效。在构建好的 APK 上验证结果——检查 Play 实际收到的字节unzip -o -q app-arm64-release-unsigned.apk lib/*/*.so -d /tmp/apkcheck $NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-objdump -p \ /tmp/apkcheck/lib/*/*.so | awk $1 LOAD { print $NF } | sort -u # 每个值必须是 2**14 或更大出现 2**12 说明 flag 未生效用llvm-objdump -p而非readelf -lreadelf 会把每个 LOAD 跨两行换行很容易解析到错误的列、把地址当成对齐值。CI 对打包的每个.so运行同一检查一旦回归即失败。从 .github/workflows/android.yml 可看到这条检查链的完整形态CI 用unzip解出 APK 的lib/*/*.so与 AAB 的base/lib/*/*.so逐个.so用llvm-objdump -p提取 LOAD 段对齐指数要求 14即 2**1416384若某个归档没有任何原生库unzip 退出码 11也计为问题避免空检查通过。同时签名步骤用zipalign -P 16而非小写-p后者只保证 4 KB且 16 KB 要求有两条独立轴线ELF 段对齐 .so 在 APK zip 内的字节偏移前者靠 RUSTFLAGS后者靠 zipalign并在签名后再用zipalign -c -P 16复核最终产物。构建cd apps/geolibre-desktop npx tauri android init # 生成 src-tauri/gen/android一次性 npx tauri android build --apk --split-per-abi # release APK每 ABI 一个约 40 MB 每个 npx tauri android build --aab # 面向 Google Play 的通用 AABgen/android是生成的git-ignored按需重新生成构建release而非--debug剥离符号、体积优化的 Cargo profile 使每个 APK 约 40 MBdebug 构建约 200 MB未剥离的.so带 debuginfo--split-per-abi每个架构产出一个 APK而不是单个约 150 MB 的通用 APK。真机安装arm64版输出路径src-tauri/gen/android/app/build/outputs/apk/abi/release/app-abi-release-unsigned.apk 其中abi是 Tauri 的短名——arm64、arm、x86、x86_64——而不是 APK 内部lib/下出现的 Android ABI 目录名arm64-v8a、armeabi-v7a。侧载 / GitHub release 路径按 ABI 拆分的APKGoogle Play 路径通用AABPlay 会基于它生成按设备拆分的产物。不要给 AAB 加--split-per-abi——Play 要的是那一个 bundle。应用在 Android 上命名为GeoLibre桌面构建叫 GeoLibre Desktop包名org.geolibre.app两者都在src-tauri/tauri.android.conf.json中设置该文件同时把 Python 后端从 Android 包中剔除。签名release APK 默认未签名。要安装需先签名测试用 debug 密钥即可分发必须用真实密钥BT$ANDROID_HOME/build-tools/36.0.0 KS$HOME/.android/debug.keystore # Android 工具链自动创建也可自建 # -P 16不是 -p-p 只保证 4 KB而 Play 要求 .so 在 zip 内落在 16 KB 边界上。与 CI 使用的 flag 相同。 $BT/zipalign -P 16 -f 4 app-arm64-release-unsigned.apk aligned.apk $BT/apksigner sign --ks $KS --ks-pass pass:android \ --ks-key-alias androiddebugkey --key-pass pass:android \ --out geolibre-arm64.apk aligned.apk $BT/apksigner verify geolibre-arm64.apk示例签名的是 arm64 APK。按需替换 ABI——app-x86_64-release-unsigned.apk→geolibre-x86_64.apkarmeabi-v7a/x86同理。下文模拟器章节安装的geolibre-x86_64.apk就是同一流程把arm64换成x86_64。用于正式上传/发布的密钥keytool -genkeypair -v -keystore upload.jks -alias upload -keyalg RSA \ -keysize 2048 -validity 10000持续集成CI.github/workflows/android.yml 在每个已发布的 GitHub release 上也可通过 Run workflow 按钮按需触发构建已签名、按 ABI 拆分的 release APK并上传为geolibre-android-release-apks工件。在已发布的 release 上APK还会作为可下载资产附加到该 release——但仅当它们带有真实 release 签名时debug 签名的 APK 只保留为 CI 工件运行日志会输出警告说明原因。当以下仓库 secrets 已设置时用你的 release keystore 签名否则回退到一次性 debug 密钥以保证工件仍可安装用于测试ANDROID_KEYSTORE_BASE64—base64 -w0 upload.jksANDROID_KEYSTORE_PASSWORDANDROID_KEY_ALIASANDROID_KEY_PASSWORDCI 还会构建通用AAB并作为独立的geolibre-android-play-aab工件上传——但仅在具备真实 release keystore 的运行时Play 拒绝 debug 签名的 bundle。没有 keystore 时 AAB 构建被整体跳过而不是构建后丢弃。在已发布的 release 上geolibre-android.aab会与 APK 一起附加到 release使某个 tag 提交的确切 bundle 在 CI 工件过期后仍可回溯。侧载请取.apk。.aab是 Google Play 上传格式无法安装到设备。CI 工作流还有一些值得借鉴的工程细节包名三重校验生成 Gradle 工程后先 grepapplicationIdAPK 构建后用aapt2 dump packagename校验合并后的 manifestAAB 用bundletool dump manifest --xpath/manifest/package校验AAB 的 manifest 是 protobuf 编码aapt2 读不了。原因注释写得很清楚Play 在首次上传时就把包名烧死不可更改或复用所以宁可 fail-fast 也不假设。16 KB 对齐检查覆盖 APK 与 AAB 内所有.so并明确警告不要用find gen/android -path */release/*Gradle 输出目录是arm64Release等大写 R会匹配为空导致检查永不运行。签名 secrets 处理密钥/密码通过pass:file:传递避免出现在进程参数与日志keystore 解码到$RUNNER_TEMP后通过trap ... EXIT清理防止set -e中途失败留下明文密码文件。并发组隔离release 运行与手动运行分组隔离且 release 运行不可取消它是该 tag release 资产的唯一生产者手动运行之间则允许互相抢占。安装 / 测试真机开启开发者选项连点版本号 7 次与USB 调试侧载已签名 APKadb install -r geolibre-arm64.apk或把 APK 复制到手机点击安装允许安装未知应用。需要热重载的实时开发连接设备后运行npm run tauri android dev。模拟器sdkmanager --sdk_root$ANDROID_HOME \ emulator system-images;android-36;google_apis_playstore;x86_64 avdmanager create avd -n geolibre \ -k system-images;android-36;google_apis_playstore;x86_64 -d pixel_7 emulator -avd geolibre # x86_64 APK 匹配 x86_64 系统镜像——模拟器能翻译 arm64 构建但慢得多 # 且不会真正运行到 x86_64 库。 adb install -r geolibre-x86_64.apk若之后用不同签名密钥重建先卸载旧副本adb uninstall org.geolibre.app——Android 会拒绝签名变更的更新。这在侧载 APK 与 Play 构建之间切换时同样适用Play App Signing 用 Google 的密钥重新签名两者不可升级兼容。发布到 Google PlayGeoLibre 已作为org.geolibre.app上线本节记录上架流程供参考及发布分叉者使用。只有第 3 步在每次发布时重复用 upload key 构建 AAB 并递增versionCode。构建侧由上文 CI 工作流覆盖其余是 Play Console 的一次性上架流程。开发者账号$25一次性。尽量注册为组织而非个人账号2023-11-13 之后创建的个人账号必须在申请生产权限前运行封闭测试12 名主动测试者持续 14 天。组织账号豁免。Play App Signing。上传upload.jks作为upload密钥Google 持有实际的应用签名密钥并为每个 bundle 重新签名。仓库的ANDROID_KEYSTORE_*secrets 就是这个 upload key——务必备份 keystore丢失需要走 Play 支持重置。上传 AAB来自geolibre-android-play-aabCI 工件或在该工件过期后使用 release 的geolibre-android.aab资产。versionCode由tauri.conf.json中的版本派生每次上传必须递增。商店素材512×512 图标、一张1024×500 功能图以及至少两张手机截图。再加 7 英寸与 10 英寸平板截图——Play 会降低没有平板截图的应用的排名而 GIS 工作区确实适合平板。隐私政策 URL——指向已发布的 privacy policy。数据安全表单。如实声明每个网络目的地地理编码、AI 助手、底图/瓦片抓取以及 Earth Engine 的 Google OAuth。注明哪些是传输而非收集——GeoLibre 不运营保留用户数据的后端但表单按用途询问。内容分级问卷与目标受众。每次更新都要牢记下文已知限制Android 上仍有若干 Add Data 路径处于惰性状态用户点了一下没反应就是一星差评所以要在移动端像 sidecar 工具那样用isMobile()把它们门控起来。已知限制 / 后续工作本地文件源MBTiles、本地栅格、项目文件假设真实文件系统路径Android 作用域存储返回的是 content URI这些流程需要适配才能原生工作。下载离线区域Download Offline Area工具依赖 service worker而 Tauri 构建桌面与 Android不使用它——这是 PWA 功能。原生离线底图缓存打包/下载的 MBTiles/PMTiles是未来的增强方向。Earth Engine OAuth使用桌面回环/多窗口流程移动端 deep-link 重定向是后续工作。从其他应用打开位置geo:深链GeoLibre 处理 AndroidACTION_VIEWintent 的geo:scheme无论是冷启动应用还是应用已处于打开状态。支持的格式包括geo:40.7128,-74.006geo:40.7128,-74.006?z12geo:0,0?q40.7128,-74.006(New%20York)z12坐标采用纬度、经度顺序。数字形式的q坐标会覆盖 URI 中的占位坐标纯地址查询不支持。直接 URI 中可选的第三个坐标是海拔米会经过校验但在定位地图时被忽略。缩放默认 14必须在 0 到 24 之间。无效位置会被忽略。收到的位置只会移动地图不会替换当前项目的图层。Tauri 的 deep-link 插件从tauri.android.conf.json生成 Android intent filter因此 CI 重新生成gen/android时它同样生效。要在已连接设备上同时验证冷启动与运行中的应用运行两次下面命令两次之间平移一下地图adb shell am start -a android.intent.action.VIEW -d geo:0,0?q40.7128,-74.006z12 org.geolibre.app深链的源码实现apps/geolibre-desktop/src-tauri/tauri.android.conf.json 中plugins.deep-link.mobile声明了scheme: [geo]appLink: false这是 Android intent filter 的唯一来源前端接入点在 apps/geolibre-desktop/src/lib/native-coordinate-open.tsinitializeNativeCoordinateOpen()先isTauri()短路再动态 importtauri-apps/plugin-deep-link用onOpenUrl订阅应用生命周期内的 URL同时用getCurrent()读取冷启动 URIaccept()把 URL 列表经coordinateTargetFromGeoUri解析为坐标目标——startup阶段存入initialTarget之后通过useAppStore.getState().setMapView(location)移动实时地图。finishNativeCoordinateStartup()在启动播种地图后调用把后续 intent 切换到移动实时地图模式URI 解析在 apps/geolibre-desktop/src/lib/coordinate-url.ts其if (!/^geo:/i.test(uri)) return null;与文档的无效位置被忽略对应。Web 链接可用https://geolibre.app/?lat40.7128lon-74.006zoom12或紧凑形式https://geolibre.app/?12/40.7128/-74.006打开同一位置。仅坐标的启动优先于已保存的启动设置并跳过引导流程。与坐标参数组合时显式的项目或数据链接保留其既有相机/加载行为。参考资料官方 Android 文档docs/android.md安装下载见 docs/downloads.md#android-installation隐私政策见 docs/privacy.mdAndroid 平台配置apps/geolibre-desktop/src-tauri/tauri.android.conf.json平台判定实现apps/geolibre-desktop/src/lib/is-mobile.ts、apps/geolibre-desktop/src/lib/is-tauri.tsPostgreSQL 桌面运行门控apps/geolibre-desktop/src/components/layout/add-data/sources/PostgresSource.tsx深链接入与解析apps/geolibre-desktop/src/lib/native-coordinate-open.ts、apps/geolibre-desktop/src/lib/coordinate-url.tsAndroid CI 工作流.github/workflows/android.yml【免费下载链接】GeoLibreA lightweight, cloud-native GIS platform for visualizing, exploring, and analyzing geospatial data. It runs in the web browser, on the desktop, on mobile, and inside Jupyter notebooks.项目地址: https://gitcode.com/GitHub_Trending/ge/GeoLibre创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门