开源 Android 应用的 F-Droid 上架实践

标签: Android开源F-Droid
实践 2026 年 4 月 7 日 约 9 分钟
开源 Android 应用的 F-Droid 上架实践

将开源 Android 应用接入 F-Droid,可以为用户提供不依赖商业应用市场的公开分发渠道。与 Google Play 等直接分发预编译 APK 的应用商店不同,F-Droid 采用从源码构建、社区公开审核的运作方式。本文以开源项目 WakeUpScreen 的实际提交过程为例,梳理收录要求、Fastlane 目录组织、GitLab Merge Request 提交流程以及版本自动检测配置,并针对本地验证与审核中的常见问题整理一套实用的解决方案。

一、F-Droid 的分发机制与收录要求

在 Google Play 之外,F-Droid 适合作为开源 Android 应用的另一条分发渠道。它与传统应用商店的核心差异主要体现在三点:

  • 严格的开源要求(FOSS):应用本身以及引用的所有第三方库都必须是自由与开源软件(Free and Open Source Software),不能包含任何闭源组件。Google Play Services、Firebase、商业统计或闭源推送 SDK 均不符合收录标准;若存在非自由组件,会被标记对应的 Anti-Feature 标签甚至直接拒绝收录。
  • 服务端独立构建与签名:F-Droid 不直接分发开发者预编译上传的 APK,而是由官方构建服务器从指定的 Git 提交拉取源码进行独立编译,并默认使用 F-Droid 官方的密钥签名发布。这种方式能确保用户下载到的安装包与公开源码完全一致,透明可追溯。
  • 基于 GitLab Merge Request 的公开审核:整个提交流程通过 GitLab MR 进行,没有开发者账号费用,审核重点集中在许可证合规性、无网络依赖的编译能力以及配置文件的完整性上。

适合提交到 F-Droid 的应用通常具备这些特征:代码完全开源、不依赖 Google 服务、注重隐私。以 WakeUpScreen 为例:项目采用 GPL-3.0 协议,不申请网络权限(android.permission.INTERNET),也没有集成任何统计或广告 SDK,完全符合 F-Droid 的收录标准。

二、提交前的准备工作

在向官方仓库发起提交前,需要在本地项目中完成三项准备:排查依赖与许可证、按 Fastlane 结构补充应用信息与素材、准备好正式发布的签名证书指纹。

2.1 依赖检查与开源许可证确认

首先需要排查项目依赖中是否存在闭源组件。可以在项目根目录下全局搜索常见的 Google 服务与商业 SDK:

grep -rE "play-services|firebase|com\.google\.android\.gms" .

如果存在匹配项,需要先将其移除或寻找开源替代方案,否则在审核时会被打上 Anti-Feature 标签,甚至导致收录被拒。

开源许可证方面,必须采用主流的开源协议(如 Apache-2.0、MIT、BSD、GPL 等),完整列表可查阅 fdroiddata 仓库的许可证规范。WakeUpScreen 采用的是 GPL-3.0 协议,符合要求。

此外,项目引用的所有第三方库也必须是开源的。例如 WakeUpScreen 使用的 MMKV、AndroidUtilCode、Glide、AndroidX 及 Jetpack Compose 等都符合要求。这里最需要警惕的是各类统计、推送与崩溃上报 SDK,如果是闭源的就必须剔除。

2.2 按照 Fastlane 结构整理元数据

F-Droid 客户端在展示应用时,需要应用名称、短描述、详细介绍、图标以及截图。F-Droid 支持自动从仓库中读取这些内容,前提是目录结构符合 Fastlane 规范,放置在 fastlane/metadata/android/ 路径下:

fastlane/metadata/android/
├── en-US/
│   ├── title.txt
│   ├── short_description.txt
│   ├── full_description.txt
│   ├── changelogs/
│   │   └── 30300.txt
│   └── images/
│       ├── icon.png
│       └── phoneScreenshots/
│           ├── 1.png
│           └── 2.png
├── zh-CN/
│   └── ...
└── it/
    └── ...

准备这部分内容时需要注意几个细节:

  • 多语言独立维护:各语言目录(如 zh-CN、en-US)互相独立,都需要单独提供 title.txt、short_description.txt(建议 80 字以内)和 full_description.txt。
  • 更新日志以版本号命名:changelogs/ 目录下的更新日志文件名,必须使用整数类型的 versionCode(例如 30300.txt),而不是版本名称 versionName(如 3.0.3.txt)。
  • 图片规格:icon.png 推荐使用 512×512 分辨率,可以直接复用 Android 工程中的高分辨率图标;屏幕截图按序号递增放入 phoneScreenshots/ 目录下。

2.3 准备正式签名的证书指纹

这一步虽然不是强制作业,但在实际维护中非常重要。在 F-Droid 的配置文件中,建议填入开发者官方签名证书的 SHA-256 指纹(AllowedAPKSigningKeys),用于版本安全校验,防止应用被未授权的签名冒充。

如果项目中已经通过 GitHub Actions 等 CI 工具建立了自动发布流程(监听版本变化,自动编译签名 APK 并发布 GitHub Release),提取指纹会非常简单。只需下载已经签名的 Release APK,执行 Android SDK 的校验命令:

$ANDROID_HOME/build-tools/<version>/apksigner verify --print-certs release.apk

找到输出中的 Signer #1 certificate SHA-256 digest: 对应的值,去掉中间的冒号与空格,并全部转为小写字母即可备用。

三、元数据配置与提交流程

F-Droid 收录的所有应用元数据均统一托管在 GitLab 上的 fdroid/fdroiddata 仓库中,遵循标准的 Fork & Merge Request 协作流程。

3.1 检出仓库与初始化配置文件

  1. 注册 GitLab 账号,并将 fdroid/fdroiddata 仓库 Fork 到个人命名空间。
  2. 将 Fork 后的仓库克隆到本地,并基于 master 创建新分支(建议加上 --depth=1 浅克隆以节省下载体积)。
  3. 从官方模板复制一份新的配置文件。
git clone --depth=1 git@gitlab.com:<你的GitLab用户名>/fdroiddata.git
cd fdroiddata

# 分支名和文件名都必须使用应用的 applicationId
git checkout -b com.symeonchen.wakeupscreen
cp templates/build-gradle.yml metadata/com.symeonchen.wakeupscreen.yml

3.2 编写应用元数据(YAML 配置)

这个 YAML 配置文件决定了 F-Droid 服务器如何拉取代码、校验版本以及执行编译任务。以 WakeUpScreen 为例,一份标准的配置文件如下:

Categories:
  - System
License: GPL-3.0-only
AuthorName: riko
AuthorEmail: example@example.com
WebSite: https://riko2chen.github.io/WakeUpScreen/
SourceCode: https://github.com/riko2chen/WakeUpScreen
IssueTracker: https://github.com/riko2chen/WakeUpScreen/issues
Changelog: https://github.com/riko2chen/WakeUpScreen/blob/HEAD/docs/CHANGELOG.md

AutoName: WakeUpScreen

RepoType: git
Repo: https://github.com/riko2chen/WakeUpScreen.git

Builds:
  - versionName: 3.0.3
    versionCode: 30300
    commit: 456b21655618b0baffc27328e01199aa2c2f9a28
    subdir: app
    gradle:
      - yes

AllowedAPKSigningKeys: <SHA-256 证书指纹,十六进制小写且无冒号与空格>

AutoUpdateMode: Version
UpdateCheckMode: Tags
CurrentVersion: 3.0.3
CurrentVersionCode: 30300

关键配置项解析:

  • Categories:必须使用 F-Droid 预设的分类名称(如 System、Connectivity、Internet、Multimedia)。
  • License:必须填写标准的 SPDX 许可证标识(如 GPL-3.0-only、Apache-2.0)。
  • Builds.commit:必须指定完整的 40 位 Git Commit Hash,不要写 Tag 名称(Tag 在 Git 中可以被移动或重打,Commit Hash 才能唯一锁定代码)。
  • AllowedAPKSigningKeys:填入前面提取的官方签名证书指纹。
  • Changelog:建议使用 /HEAD/ 指向默认分支,避免因主分支更名(如 master 迁移到 main)导致链接失效。

3.3 本地校验与静态检查

本地验证建议先执行轻量的静态规则检查。可以通过 Homebrew 安装官方工具链:

brew install fdroidserver

cd ~/path/to/fdroiddata
fdroid readmeta
fdroid rewritemeta com.symeonchen.wakeupscreen
fdroid lint com.symeonchen.wakeupscreen
fdroid checkupdates --allow-dirty com.symeonchen.wakeupscreen

各命令的作用:

  • rewritemeta:按照官方规范格式化 YAML 文件字段顺序与缩进。
  • lint:检查必填字段是否完整、分类是否合法以及 URL 是否有效。
  • checkupdates:模拟服务器拉取规则,测试版本检测是否能够正常识别新版本。

如果本地有适宜的环境,也可以通过官方 Docker 镜像运行 fdroid build 测试全量编译。但在 macOS(尤其 Apple Silicon 架构)上,这一步往往成本过高(详见第四节),通常把本地检查通过后的构建任务交给远程 CI 更加高效。

3.4 关联 RFP 并提交 Merge Request

在正式提交 MR 之前,建议先在 Request For Packaging (RFP) 仓库 搜索应用名称。很多开源项目在此之前可能已经有社区用户提过收录申请。

例如 WakeUpScreen 就找到了之前用户提交的 rfp#2023 “New APP: WakeUpScreen”。如果在 MR 描述中写上 Closes rfp#2023,合并后就会自动关闭该 Issue。

提交元数据并推送到个人 Fork 仓库:

git add metadata/com.symeonchen.wakeupscreen.yml
git commit -m "New App: com.symeonchen.wakeupscreen"
git push origin com.symeonchen.wakeupscreen

在 GitLab 页面创建 Merge Request 时,确认两点:

  1. Target Project 指向上游 fdroid/fdroiddata,Target Branch 为 master;
  2. 完整填写 MR 描述模板,关联 RFP 编号并按实际情况勾选选项。

提交后,GitLab CI 会自动运行语法校验与编译流程。CI 通过后,再等待社区审核员(Packager)进行人工 Review。

四、常见问题与避坑指南

以下问题均来自实际提交过程中的真实场景,具备较强的参考价值。

4.1 checkupdates 无法直接解析 gradle.properties 中的版本号

问题原因

在 Android 项目中,为了方便统一管理,版本号通常会放在 gradle.properties 中:

// build.gradle
versionCode project.AppVersionCode.toInteger()
versionName project.AppVersionName

然而,fdroid checkupdates 采用的是静态正则表达式匹配文件内容,并不会实际运行 Gradle。因此它无法动态计算跨文件的变量引用,会导致检测失败并报错:

ERROR: Couldn't find any version information

解决方案

不需要把版本号妥协改回写死在 build.gradle 中。正确的做法是在 YAML 中保留 UpdateCheckMode: Tags,同时添加 UpdateCheckData 明确指定版本号存储的文件及正则表达式:

AutoUpdateMode: Version
UpdateCheckMode: Tags
UpdateCheckData: gradle.properties|AppVersionCode=(\d+)|.|AppVersionName=([\d.]+)

这种写法让检测工具直接从 Git Tag 对应的 gradle.properties 中提取版本信息,既不依赖外部 HTTP 请求,又兼容了属性文件管理版本号的工程结构。

4.2 Apple Silicon (Mac) 本地容器构建成本过高

官方文档建议在提交前在本地执行 fdroid build 进行完整构建验证。但在 Apple Silicon 芯片的 Mac 上,这一步通常很困难:

  1. 官方构建镜像 registry.gitlab.com/fdroid/fdroidserver:buildserver 仅针对 linux/amd64(x86 架构)打包;
  2. 在 ARM 架构上运行 x86 容器需要借助 QEMU 模拟,编译庞大的 Android Gradle 工程非常耗费内存和 CPU,运行极慢且容易崩溃。

实用建议

对于使用 Apple Silicon 的开发者,建议合理分工:

  • 本地只做轻量检查:执行 rewritemeta、lint 和 checkupdates,确保配置文件无语法和规则错误;
  • 完整编译交给远程 CI:GitLab CI 运行在原生的 x86 机器上,编译稳定且速度更快;
  • 在 Merge Request 的问卷中说明本地机器因架构原因未在本地运行 fdroid build,CI 通过后审核员通常完全认可。

4.3 本地与 CI 环境中 rewritemeta 格式化差异

现象

即便在本地执行过 fdroid rewritemeta 且检查通过,提交 MR 后 GitLab CI 的元数据检查任务仍可能报错,并打印出格式差异(diff):

These files need rewritemeta:
metadata/com.symeonchen.wakeupscreen.yml

These are the formatting issues:
- UpdateCheckData: ...
+ UpdateCheckData:
+   ...

原因与解决办法

出现这种现象,通常是因为本地通过 Homebrew 安装的 fdroidserver 版本与 GitLab CI 容器中的工具版本存在细微差异,对长字符串折行或 YAML 缩进的处理规则不完全一致。

遇到这种情况,不需要在本地反复尝试格式化命令。直接以 CI 日志输出的 diff 内容为准,在本地手动对齐修改后重新 push 即可。

4.4 可重现构建(Reproducible Builds)的权衡

在 MR 模板中有一项关于「Enable Reproducible Builds(可重现构建)」的选项。这项配置直接关系到用户后续的升级体验:

模式签名机制跨渠道覆盖升级维护成本
默认模式(不启用)F-Droid 官方私钥统一签名不支持(与 Google Play / GitHub 官方签名不同,用户需卸载重装)较低,代码在官方容器内能正常编译即可
可重现构建(启用)沿用开发者官方签名证书支持(签名一致,用户可以在不同渠道间自由覆盖升级)较高,需要确保本地构建与官方构建结果逐字节一致

实现可重现构建需要消除编译期时间戳、文件打包顺序、代码混淆输出差异等一系列影响二进制一致性的因素。

选型建议:首次提交建议先选择默认模式,快速跑通收录流程;如果应用包含重要本地数据(如密码库、笔记或数据库类工具)且用户有强烈的跨渠道平滑迁移需求,后续再作为专项适配可重现构建。

4.5 社区审核(Packager Review)的核心意见

在 CI 全部通过后,社区审核员在人工 Review 中通常会提出两条关键意见:

  1. Builds.commit 必须填写 40 位 Commit Hash: 禁止使用 Tag 代替。因为在 Git 规范中 Tag 属于可移动的软引用,而 Commit Hash 具备不可篡改性,能确保将来任何时候重新构建都能锁定同一份代码。

    git rev-parse v3.0.3
    # 或直接查询远程仓库
    git ls-remote https://github.com/riko2chen/WakeUpScreen.git refs/tags/v3.0.3
  2. 版本检测优先基于 Git Tag 机制: 避免使用 UpdateCheckMode: HTTP 依赖第三方代码托管平台的 Raw API 接口,优先使用 UpdateCheckMode: Tags 搭配 UpdateCheckData 完成版本解析。这样既不依赖外部 HTTP 接口的稳定性,也符合社区的通用惯例。

经过审核员指导后的最终配置如下:

Builds:
  - versionName: 3.0.3
    versionCode: 30300
    commit: 456b21655618b0baffc27328e01199aa2c2f9a28
    subdir: app
    gradle:
      - yes

AllowedAPKSigningKeys: 7044dcb7b20edcefdb22923e649c84187e3aa818fc3a58dfcb2eccc92cddef1c

AutoUpdateMode: Version
UpdateCheckMode: Tags
UpdateCheckData: gradle.properties|AppVersionCode=(\d+)|.|AppVersionName=([\d.]+)

五、总结

将开源 Android 应用接入 F-Droid,核心流程可以概括为以下六步:

  1. 排查依赖:确保应用及第三方库完全符合开源许可,剥离闭源组件。
  2. 准备素材:按照 Fastlane 目录结构补齐多语言描述、图标与截图。
  3. 记录签名:规范发布流程,提取正式签名 APK 的 SHA-256 证书指纹。
  4. 编写配置:Fork 官方仓库,编写描述应用构建规则与版本检测的 YAML 文件。
  5. 本地检查:运行 rewritemeta、lint 与 checkupdates 验证配置正确性。
  6. 提交与审核:发起 Merge Request,关联历史 RFP Issue,根据 CI 和审核员反馈修改合入。

与商业应用商店相比,F-Droid 的全透明机制虽然在首次接入时需要花时间理解其规则与配置格式,但能让整个构建与发布链路保持开放、透明、可追溯。对于坚持无追踪、尊重用户隐私的开源项目来说,F-Droid 是一条非常值得维护的分发渠道。