English | 中文
Andock 是一个在 Android 上运行 Docker 容器的应用,基于 udocker 概念用 Kotlin 重新实现,使用 PRoot 作为执行引擎,无需 root 权限。
- 🐳 完整容器管理 - 创建、启动、停止、删除容器,支持环境变量和工作目录配置
- 📦 镜像管理 - 从 Docker Registry 拉取镜像,查看镜像详情,删除不需要的镜像
- 🔍 Docker Hub 搜索 - 集成 Docker Hub 搜索,支持无限滚动分页(Paging 3)
- 🌍 镜像加速 - 内置多个中国镜像源,支持自定义镜像源和 Bearer Token 认证
- 📱 二维码导入 - 使用 CameraX + ML Kit 扫描二维码快速导入镜像源配置
- 💻 交互终端 - 完整的容器终端访问,支持 exec 执行命令
- 🚀 无需 Root - 基于 PRoot v0.15 技术,完全运行在用户空间
- 🔧 Shizuku 集成 - 可选使用 Shizuku 禁用 Android 12+ 的 Phantom Process Killer
- 🌐 Docker API 服务器 - 内置 Docker Engine API v1.45 兼容的 HTTP 服务器(Unix Socket)
- 🎨 Material Design 3 - 遵循 Google 最新设计规范,深色/浅色主题自动切换
- 🌏 完整国际化 - 中英文界面自动切换
项目采用多模块架构,清晰分离业务逻辑和 UI 层:
andock/
├── daemon/ # 核心业务逻辑模块 (Android Library)
│ ├── app/ # 应用配置和初始化
│ │ ├── AppContext.kt # 应用上下文和目录配置
│ │ ├── AppInitializer.kt # App Startup 初始化
│ │ └── AppModule.kt # Hilt 依赖注入模块
│ │
│ ├── containers/ # 容器管理
│ │ ├── Container.kt # 容器实例(FlowRedux 状态机)
│ │ ├── ContainerManager.kt # 容器创建、追踪、删除
│ │ ├── ContainerState.kt # 8 种状态(Created, Starting, Running...)
│ │ └── ContainerStateMachine.kt # 状态机逻辑
│ │
│ ├── database/ # Room 数据库
│ │ ├── AppDatabase.kt # 数据库定义(版本 1)
│ │ ├── dao/ # 数据访问对象
│ │ │ ├── ImageDao.kt
│ │ │ ├── LayerDao.kt
│ │ │ ├── ContainerDao.kt
│ │ │ ├── RegistryDao.kt
│ │ │ ├── AuthTokenDao.kt
│ │ │ └── SearchRecordDao.kt
│ │ └── model/ # 数据库实体
│ │ ├── ImageEntity.kt
│ │ ├── LayerEntity.kt
│ │ ├── LayerReferenceEntity.kt
│ │ ├── ContainerEntity.kt
│ │ ├── RegistryEntity.kt
│ │ ├── AuthTokenEntity.kt
│ │ └── SearchRecordEntity.kt
│ │
│ ├── engine/ # PRoot 执行引擎
│ │ ├── PRootEngine.kt # PRoot 命令构建和执行
│ │ ├── PRootEnvironment.kt # 环境变量配置
│ │ └── PRootVersion.kt # 版本检测
│ │
│ ├── images/ # 镜像管理
│ │ ├── ImageManager.kt # 镜像仓库操作
│ │ ├── ImageClient.kt # Docker Registry API 客户端
│ │ ├── ImageReference.kt # 镜像名称解析器
│ │ ├── downloader/ # 镜像下载器
│ │ │ ├── ImageDownloader.kt # 下载状态机
│ │ │ └── ImageDownloadState.kt # 下载状态
│ │ └── model/ # Registry API 模型
│ │
│ ├── http/ # HTTP 服务器
│ │ ├── UnixHttp4kServer.kt # Unix Socket HTTP 服务器
│ │ ├── TcpHttp4kServer.kt # TCP HTTP 服务器(调试用)
│ │ └── HttpProcessor.kt # HTTP/1.1 协议处理器
│ │
│ ├── server/ # Docker API 服务器
│ │ ├── DockerApiServer.kt # 主服务器(组合所有路由)
│ │ └── routes/ # Docker API v1.45 路由模块
│ │ ├── ContainerRoutes.kt
│ │ ├── ImageRoutes.kt
│ │ ├── SystemRoutes.kt
│ │ ├── VolumeRoutes.kt
│ │ ├── ExecRoutes.kt
│ │ └── ... (更多路由)
│ │
│ ├── registries/ # 镜像源管理
│ │ ├── Registry.kt # 镜像源实例(FlowRedux 状态机)
│ │ ├── RegistryManager.kt # 镜像源追踪和健康检查
│ │ └── RegistryStateMachine.kt # 健康检查状态机
│ │
│ ├── search/ # Docker Hub 搜索
│ │ ├── SearchPagingSource.kt # Paging 3 数据源
│ │ ├── SearchRepository.kt # 搜索仓库
│ │ ├── SearchHistory.kt # DataStore 搜索历史
│ │ └── model/ # 搜索结果模型
│ │
│ ├── io/ # 文件 I/O 工具
│ │ ├── File.kt # 符号链接处理、SHA256 计算
│ │ ├── Zip.kt # Tar/GZ 解压(保留符号链接)
│ │ └── Tailer.kt # 日志尾随读取
│ │
│ ├── os/ # 操作系统集成
│ │ ├── ProcessLimitCompat.kt # Phantom Process Killer 管理
│ │ ├── RemoteProcess.kt # Shizuku 进程包装器
│ │ └── Process.kt # 进程执行工具
│ │
│ └── logging/ # 日志系统
│ ├── TimberLogger.kt # Timber + SLF4J 集成
│ └── TimberServiceProvider.kt # SLF4J 服务提供者
│
├── proot/ # PRoot 原生编译模块 (Android Library)
│ └── src/main/cpp/
│ ├── CMakeLists.txt # CMake 构建配置
│ └── scripts/ # 编译脚本
│ ├── build-talloc.sh # 编译 talloc 依赖库
│ ├── build-proot.sh # 编译 PRoot
│ └── filter-output.sh # 输出过滤器
│
└── app/ # UI 模块 (Android Application)
├── AndockApplication.kt # Application 类
├── ui/
│ ├── MainActivity.kt # 单 Activity + Compose
│ │
│ ├── screens/ # 页面组件(按功能分组)
│ │ ├── main/ # 主导航页面
│ │ │ └── MainScreen.kt # 底部导航栏
│ │ ├── home/ # 主页(仪表盘)
│ │ │ ├── HomeScreen.kt
│ │ │ └── HomeViewModel.kt
│ │ ├── containers/ # 容器管理
│ │ │ ├── ContainersScreen.kt # 容器列表
│ │ │ ├── ContainerDetailScreen.kt # 容器详情
│ │ │ ├── ContainerCreateScreen.kt # 创建容器
│ │ │ └── ContainersViewModel.kt
│ │ ├── images/ # 镜像管理
│ │ │ ├── ImagesScreen.kt
│ │ │ ├── ImageDetailScreen.kt
│ │ │ ├── ImagePullDialog.kt
│ │ │ └── ImagesViewModel.kt
│ │ ├── search/ # Docker Hub 搜索
│ │ │ ├── SearchScreen.kt
│ │ │ ├── SearchFilterPanel.kt
│ │ │ ├── SearchHistoryPanel.kt
│ │ │ └── SearchViewModel.kt
│ │ ├── registries/ # 镜像源管理
│ │ │ ├── RegistriesScreen.kt
│ │ │ ├── AddMirrorScreen.kt
│ │ │ └── RegistriesViewModel.kt
│ │ ├── qrcode/ # 二维码扫描
│ │ │ ├── QrcodeScannerScreen.kt
│ │ │ └── QrcodeCamera.kt
│ │ ├── terminal/ # 容器终端
│ │ │ ├── TerminalScreen.kt
│ │ │ └── TerminalViewModel.kt
│ │ ├── limits/ # 进程限制管理
│ │ │ └── ProcessLimitScreen.kt
│ │ └── settings/ # 设置页面
│ │ ├── SettingsScreen.kt
│ │ └── SettingsViewModel.kt
│ │
│ ├── components/ # 可复用 UI 组件
│ │ ├── DetailCard.kt
│ │ ├── StatusIndicator.kt
│ │ ├── LoadingDialog.kt
│ │ └── ...
│ │
│ └── theme/ # Material Design 3 主题
│ ├── Spacing.kt # 间距常量(8dp 网格)
│ └── IconSize.kt # 图标尺寸常量
│
└── build/ # 构建输出(自动生成)
└── intermediates/jniLibs/ # PRoot 二进制文件(由 proot 模块编译)
├── arm64-v8a/
│ ├── libproot.so # PRoot 可执行文件
│ ├── libproot_loader.so # 64 位加载器
│ └── libproot_loader32.so # 32 位加载器
├── armeabi-v7a/
├── x86_64/
└── x86/
- Kotlin 2.2.21 - 100% Kotlin 代码
- Jetpack Compose - 现代化声明式 UI (BOM 2025.12.00)
- Material Design 3 - Google 最新设计规范
- Coroutines & Flow 1.10.2 - 异步响应式编程
- Hilt 2.57.2 - 依赖注入框架
- AssistedInject - 动态实例创建(Container, ImageDownloader, Registry)
- Room 2.8.4 - 本地 SQLite 数据库
- DataStore - 轻量级键值存储(搜索历史)
- Paging 3.3.5 - 分页加载(Docker Hub 搜索)
- Ktor 3.3.3 - HTTP 客户端(OkHttp 引擎)
- kotlinx-serialization 1.9.0 - JSON 序列化
- http4k 6.23.1.0 - HTTP 服务器框架
- FlowRedux 2.0.0 - 状态机框架(容器、下载、镜像源)
- Apache Commons Compress 1.28.0 - Tar/GZ 解压
- XZ 1.11 - .tar.xz 格式支持
- Coil 3.3.0 - 图片加载库(Compose 集成)
- CameraX 1.5.2 - 相机 API
- ML Kit Barcode Scanning 17.3.0 - 二维码识别
- Shizuku 13.1.5 - 系统权限管理
- PRoot v0.15 - 用户空间 chroot(从源码自动编译,基于 LukeXeon/proot)
- talloc 2.4.2 - PRoot 内存管理依赖库
- Timber 5.0.1 - Android 日志库
- SLF4J 2.0.17 - 统一日志接口
- Navigation Compose 2.9.6 - 类型安全导航
- Android Studio Ladybug (2024.2.1) 或更高版本
- JDK 17+
- Android SDK API 36
- 设备要求: Android 8.0+ (API 26+)
# 1. 克隆项目
git clone <repository-url>
cd andock
# 2. 使用 Android Studio 打开项目并同步 Gradle
# 3. 编译 Debug APK
./gradlew assembleDebug
# 4. 安装到设备(需要连接 ADB)
export PATH="$PATH:$HOME/Library/Android/sdk/platform-tools"
adb install app/build/outputs/apk/debug/app-debug.apk# 设置环境变量
export PATH="$PATH:$HOME/Library/Android/sdk/platform-tools"
export JAVA_HOME=/opt/homebrew/opt/openjdk@17 # macOS
# 运行所有测试
./gradlew connectedAndroidTest
# 运行特定测试
./gradlew connectedDebugAndroidTest --tests com.github.andock.ImagePullAndRunTest步骤:
- 打开应用,点击 Registries 标签
- 选择内置镜像源或添加自定义源
内置镜像源:
- Docker Hub (官方) - registry-1.docker.io
- DaoCloud (中国) - docker.m.daocloud.io
- Xuanyuan (中国) - docker.xuanyuan.me
- Huawei Cloud (中国) - mirrors.huaweicloud.com
二维码导入格式:
{
"name": "My Custom Mirror",
"url": "https://mirror.example.com",
"bearerToken": "optional_token_here"
}方式一: Docker Hub 搜索
- 点击 Search 标签
- 输入关键词(如
alpine,nginx,ubuntu) - 点击搜索结果中的下载按钮
方式二: 直接拉取
- 点击 Images 标签 → 点击 + 按钮
- 输入完整镜像名称(如
alpine:latest) - 点击 Pull 开始下载
方式三: 二维码扫描
- 点击 Images 标签 → 点击扫码图标
- 扫描包含镜像配置的二维码
步骤:
- 在 Images 页面选择镜像 → 点击 Run 按钮
- 配置容器参数:
- 容器名称 (自动生成或自定义)
- 环境变量 (如
KEY=VALUE) - 工作目录 (可选)
- 命令 (覆盖默认 CMD,可选)
- 点击 Create 创建并启动容器
在 Containers 页面可以:
- 使用筛选器查看容器(全部/运行中/已退出)
- 启动或停止容器
- 查看容器详情(环境变量、端口、网络等)
- 打开终端执行命令
- 删除不需要的容器
容器状态:
- Created - 已创建但未启动
- Starting - 正在启动中
- Running - 运行中
- Stopping - 正在停止
- Exited - 已退出
- Dead - 进程意外终止
- Removing - 正在删除
- Removed - 已删除
步骤:
- 在 Containers 页面点击运行中容器的 Terminal 按钮
- 在终端界面输入命令并执行
- 支持快捷命令按钮(如
ls,pwd,ps)
在 Android 12+ 设备上,系统可能会杀死后台进程。使用 Shizuku 可以禁用此限制:
步骤:
- 安装 Shizuku
- 启动 Shizuku 服务
- 打开应用,进入 Settings → Background Process Limit
- 授权 Shizuku 权限
- 点击 Disable Limit 按钮
原理:
# Android 12L+ (API 32+)
settings put global settings_enable_monitor_phantom_procs false
# Android 12 (API 31)
device_config set_sync_disabled_for_tests persistent
device_config put activity_manager max_phantom_processes 2147483647设计决策: 数据库不存储运行状态
- 数据库 (ContainerEntity): 仅存储静态配置(名称、镜像、创建时间、配置)
- 运行时 (Container): 每个容器实例维护自己的状态机,追踪 8 种状态
- UI 层: 直接观察
Container.stateStateFlow,实时更新
状态转换:
Created → Starting → Running → Stopping → Exited → Removing → Removed
↓
Dead
优势:
- 避免数据库中状态过期(应用被杀死时"Running"状态失效)
- UI 自动实时更新:状态改变时自动重组
- 类型安全的状态转换:状态机确保转换合法性
- 精确的状态表达:8 种状态完整表达容器生命周期
示例代码:
@Composable
fun ContainerCard(container: Container) {
// 观察状态变化,状态改变时自动重组
val containerState by container.state.collectAsState()
// 直接使用状态进行 UI 逻辑判断
when (containerState) {
is ContainerState.Running -> ShowRunningUI()
is ContainerState.Created -> ShowCreatedUI()
is ContainerState.Exited -> ShowExitedUI()
else -> ShowDefaultUI()
}
// 显示状态名称
Text(text = containerState::class.simpleName ?: "Unknown")
}设计决策: 仅存储压缩层,按需解压
存储结构:
layersDir/
└── {sha256-digest}.tar.gz # 压缩层文件(2-5 MB)
containersDir/
└── {containerId}/
└── rootfs/ # 解压后的容器文件系统(7-15 MB)
优势:
- 70% 存储节省 (Alpine: 3MB 压缩 vs 10MB 解压)
- 更快的镜像拉取(无需解压)
- 更简单的层管理(无需去重)
权衡: 容器创建时需要解压,但每个容器只需解压一次
问题: Android 10+ 禁止从 app_data_file 目录执行二进制文件
解决方案:
proot模块在构建时自动下载并编译 PRoot v0.15 + talloc 2.4.2- 编译输出为
libproot.so,打包到 APK 的jniLibs/目录 - Android 自动提取到
nativeLibraryDir,SELinux 上下文为apk_data_file(可执行) - 直接从
applicationInfo.nativeLibraryDir执行
16KB 页面对齐: 为 Android 15+ 设备配置了 -Wl,-z,max-page-size=16384 链接选项
参考: Termux 实现
Docker 镜像(尤其是 Alpine Linux)大量使用符号链接。标准 Java Files API 不保留它们。
解决方案: 使用 Android Os API 处理符号链接
- Os.lstat() + OsConstants.S_ISLNK() // 检测符号链接
- Os.readlink() // 读取链接目标
- Os.symlink() // 创建符号链接
- Os.chmod() // 保留权限确保 Alpine Linux 等依赖符号链接的镜像能正常工作。
实现: UnixHttp4kServer 支持 FILESYSTEM 和 ABSTRACT 两种命名空间
命名空间对比:
| 特性 | FILESYSTEM | ABSTRACT |
|---|---|---|
| 文件创建 | ✅ 创建 socket 文件 | ❌ 无文件(内存) |
| 可见性 | ls, stat 可见 |
文件系统不可见 |
| 清理 | ✅ 关闭时自动清理 | |
| 权限 | 受文件权限约束 | N/A |
| 用途 | 调试、CLI 工具 | 应用内部 IPC |
Docker API 集成:
UnixHttp4kServer(
name = File(appConfig.filesDir, "docker.sock").absolutePath,
namespace = Namespace.FILESYSTEM,
httpHandler = routes.reduce { acc, handler -> acc.then(handler) }
)URL 分页实现:
- PagingSource Key: URL (String?)
- 初始请求:
/v2/search/repositories/?query={q}&page_size=25 - 后续请求: 跟随响应中的
nextURL
特性:
- 防抖搜索(400ms 延迟)
- 搜索历史(DataStore,最多 20 条)
- UI 侧筛选(官方镜像、最低星数)
- 实时拉取状态追踪
- 无 root 权限限制: 部分系统调用不可用(如
mount,chroot) - 网络隔离: 不支持 Docker 网络功能(bridge, overlay)
- 存储限制: 所有数据存储在应用内部存储中
- 架构限制: 仅支持设备原生架构的容器(不支持模拟)
- 性能: PRoot 引入约 10-15% 性能开销
- ✅ Alpine Linux (latest) - 使用 musl libc 和 BusyBox,完全兼容
- ✅ BusyBox (latest) - 最小化工具集,完全兼容
⚠️ Ubuntu (需要较大存储空间,部分功能受限)⚠️ Nginx (受网络限制影响)- 其他镜像测试中...
/data/data/com.github.andock/
├── files/
│ ├── containers/
│ │ └── {containerId}/
│ │ └── rootfs/ # 容器文件系统
│ └── layers/
│ └── {digest}.tar.gz # 压缩层文件
├── cache/
│ ├── log/
│ │ └── {containerId}/
│ │ ├── stdout # 容器标准输出
│ │ └── stderr # 容器标准错误
│ └── docker.sock # Docker API Unix Socket
└── databases/
└── andock.db # Room 数据库
- udocker - 原始概念和参考实现
- PRoot - 用户空间 chroot 实现
- Termux - Android 终端和 PRoot patches
- Shizuku - 系统服务访问框架
- LukeXeon/proot - PRoot for Android(基于 green-green-avk/proot 的 fork)
本项目采用 MIT 许可证 - 查看 LICENSE 文件了解详情。