ReDroid (Remote anDroid) 是自架「云手机」的方案,透过docker在电脑上跑Android系统容器,再利用Scrcpy的镜射萤幕功能连线到Android桌面。
ReDroid也是在电脑上用开源软体跑Android APP的解决方案。因为别说云手机了,很多Android手游模拟器都是闭源软体哪。相较之下,ReDroid除了ARM转译器以外都是开源的。更棒的是ReDroid支援GPU加速+ARM转x86的转译器,这样就可以玩大多数手机3D游戏了。
对Linux用户来说,这更是除了Waydroid以外,在Linux电脑高效率跑Android APP的方法。
本文将讨论如何在x86架构的Linux电脑,用ReDroid玩ARM架构的手机游戏。我们会在ReDroid映像档加入ARM转译器+Google服务框架,以达成最佳使用体验。
PS Windows和MacOS也可以跑Docker。
ReDroid在Github上有各大Linux发行版的安装说明。
由于我是Arch Linux,要先补齐Linux核心模组binderfs。
接着安装docker和docker-compose,用于执行容器
安装ADB和Scrcpy,连线到Android桌面:
# archlinux
sudo pacman -S android-tools
sudo pacman -S scrcpy
*想要Scrcpy和Sndcpy二者整合和按键映射的,可以试试看QtScrcpy。
大部分电脑是x86架构,只能执行x86版的Android APP,然而很多手机游戏只有ARM版。所以跑ReDroid前建议先下载ARM转译器,不然能执行的APP会少很多。
ReDroid支援用QEMU或libndk转译,本文采用后者。libndk是Google开发的ARM → x86转译器,含在Android Studio的模拟器里。注意libndk是专有软体,使用上处于灰色地带。
sudo pacman -S sleuthkit p7zip binwalk git
cd ~
git clone https://github.com/sickcodes/Droid-NDK-Extractor.git
cd Droid-NDK-Extractor
chmod +x android-extract-ndk.sh
./android-extract-ndk.sh x86_64
之后会在工作目录的/working/extracted/
下找到native-bridge.tar
,大约30MB。
切换到native-bridge.tar
所在的目录,修正权限后重新压缩档案:
cd ~/Droid-NDK-Extractor/working/extracted/
mkdir native-bridge
cd native-bridge
tar -xpf ../native-bridge.tar
chmod 0644 system/etc/init/ndk_translation_arm64.rc
chmod 0755 system/bin/arm
chmod 0755 system/bin/arm64
chmod 0755 system/lib/arm
chmod 0755 system/lib64/arm64
chmod 0644 system/etc/binfmt_misc/*
tar -cpf native-bridge.tar system
mv native-bridge.tar ..
cd ..
rm -r native-bridge
native-bridge.tar
准备完成。ReDroid目前提供Android 8 ~ Android 13的映像档。由于要使用libndk的关系,故以Android 11的映像档为基础下去建置。
新增一个空白目录android
,将刚刚制作的native-bridge.tar
放到此目录。
在android
目录新增一个名为dockerfile
的档案,填入以下内容:
FROM redroid/redroid:11.0.0-amd64
ADD native-bridge.tar /
android
目录开启终端机,开始建置含有libndk的ReDroid映像档:docker build . -t redroid-11-libndk
如果要用ReDroid玩手机游戏,建议电脑至少要有8GB以上RAM,因为有时ARM在转译成x86指令时会占用大量RAM。
--rm
是因为后续还要对系统做修改。sudo docker run -itd --privileged \
-v /home/user/data11-nb:/data \
-p 5555:5555 \
redroid-11-libndk \
androidboot.redroid_gpu_mode=host \
ro.product.cpu.abilist=x86_64,arm64-v8a,x86,armeabi-v7a,armeabi \
ro.product.cpu.abilist64=x86_64,arm64-v8a \
ro.product.cpu.abilist32=x86,armeabi-v7a,armeabi \
ro.dalvik.vm.isa.arm=x86 \
ro.dalvik.vm.isa.arm64=x86_64 \
ro.enable.native.bridge.exec=1 \
ro.dalvik.vm.native.bridge=libndk_translation.so \
ro.ndk_translation.version=0.2.2
如果要用docker-compose.yml
,内容如下:
version: "3"
services:
redroid:
image: redroid-11-libndk
stdin_open: true
tty: true
privileged: true
ports:
- "5555:5555"
volumes:
- /home/user/data11-nb:/data
command:
- androidboot.redroid_gpu_mode=host
- ro.product.cpu.abilist0=x86_64,arm64-v8a,x86,armeabi-v7a,armeabi
- ro.product.cpu.abilist64=x86_64,arm64-v8a
- ro.product.cpu.abilist32=x86,armeabi-v7a,armeabi
- ro.dalvik.vm.isa.arm=x86
- ro.dalvik.vm.isa.arm64=x86_64
- ro.enable.native.bridge.exec=1
- ro.dalvik.vm.native.bridge=libndk_translation.so
- ro.ndk_translation.version=0.2.2
adb connect localhost:5555
# 如果連不上,用以下指令看一下容器內部發生什麼問題
sudo docker logs <容器ID>
sudo docker exec <容器ID> logcat
scrcpy -s localhost:5555
./sndcpy localhost:5555
作者说Google服务框架是专有软体无法内建,那么就得自行安装了。第一个方法是重新编译映像档,第二个是手动安装OpenGApps。
不推荐第一个方法,耗时而且作者提供的GApps编译教学又有其他APP侦测不到的问题。
这里采用第二个方法:手动安装,过程参考自:Install GApps Manually - Google Groups
到OpenGapps下载x86_64架构的Android 11 GApps,选择最小化的pico版。
解压缩,会看到以下目录
open_gapps-x86_64-11.0-pico-20220503
├── Core
├── GApps
├── META-INF
├── Optional
在解压缩的目录新增system
目录。
接着,将Core
和GApps
目录里面的.lz
档案都解压缩,并将里面的APK目录按照对应的安装目录放到system
目录。例如GApps/googletts-x86_64/nodpi/app/
下的GoogleTTS
目录要放到/system/app
。
放好之后,system
下的目录结构应该会长这样:
system
├── app
│ ├── GoogleCalendarSyncAdapter
│ │ └── GoogleCalendarSyncAdapter.apk
│ ├── GoogleContactsSyncAdapter
│ │ └── GoogleContactsSyncAdapter.apk
│ ├── GoogleExtShared
│ │ └── GoogleExtShared.apk
│ └── GoogleTTS
│ └── GoogleTTS.apk
├── etc
│ ├── default-permissions
│ │ ├── default-permissions.xml
│ │ └── opengapps-permissions-q.xml
│ ├── permissions
│ │ ├── com.google.android.dialer.support.xml
│ │ ├── com.google.android.maps.xml
│ │ ├── com.google.android.media.effects.xml
│ │ ├── privapp-permissions-google.xml
│ │ └── split-permissions-google.xml
│ ├── preferred-apps
│ │ └── google.xml
│ └── sysconfig
│ ├── dialer_experience.xml
│ ├── google_build.xml
│ ├── google_exclusives_enable.xml
│ ├── google-hiddenapi-package-whitelist.xml
│ └── google.xml
├── framework
│ ├── com.google.android.dialer.support.jar
│ ├── com.google.android.maps.jar
│ └── com.google.android.media.effects.jar
├── priv-app
│ ├── AndroidAutoPrebuiltStub
│ │ └── AndroidAutoPrebuiltStub.apk
│ ├── AndroidMigratePrebuilt
│ │ └── AndroidMigratePrebuilt.apk
│ ├── CarrierSetup
│ │ └── CarrierSetup.apk
│ ├── ConfigUpdater
│ │ └── ConfigUpdater.apk
│ ├── GoogleBackupTransport
│ │ └── GoogleBackupTransport.apk
│ ├── GoogleExtServices
│ │ └── GoogleExtServices.apk
│ ├── GoogleFeedback
│ │ └── GoogleFeedback.apk
│ ├── GoogleOneTimeInitializer
│ │ └── GoogleOneTimeInitializer.apk
│ ├── GooglePackageInstaller
│ │ └── GooglePackageInstaller.apk
│ ├── GooglePartnerSetup
│ │ └── GooglePartnerSetup.apk
│ ├── GoogleRestore
│ │ └── GoogleRestore.apk
│ ├── GoogleServicesFramework
│ │ └── GoogleServicesFramework.apk
│ ├── Phonesky
│ │ └── Phonesky.apk
│ ├── PrebuiltGmsCore
│ │ └── PrebuiltGmsCore.apk
│ └── SetupWizard
│ └── SetupWizard.apk
└── product
└── overlay
└── PlayStoreOverlay.apk
adb connect localhost:5555
adb -s localhost:5555 root
adb -s localhost:5555 remount
adb -s localhost:5555 shell "rm -rf system/priv-app/PackageInstaller"
system
目录推送到ReDroid系统,并赋予权限:adb -s localhost:5555 push system /
adb -s localhost:5555 shell "pm grant com.google.android.gms android.permission.ACCESS_COARSE_LOCATION"
adb -s localhost:5555 shell "pm grant com.google.android.gms android.permission.ACCESS_FINE_LOCATION"
adb -s localhost:5555 shell "pm grant com.google.android.setupwizard android.permission.READ_PHONE_STATE"
adb -s localhost:5555 shell "pm grant com.google.android.setupwizard android.permission.READ_CONTACTS"
adb reboot
docker ps
docker restart
启动Scrcpy,开启系统设定→应用程式,点选右上角显示系统应用程式,将Google Play服务和Play商店的权限都开启。
执行以下指令取得Android装置ID,到Google网站注册装置,等个30分钟后重新启动Redroid容器,才能登入Google Play。
adb -s localhost:5555 root
adb -s localhost:5555 shell 'sqlite3 /data/data/com.google.android.gsf/databases/gservices.db \
"select * from main where name = \"android_id\";"'
目前即使有安装libndk,Android 11的Play商店还是不给下载ARM架构的APP,请配合APKPure之类的应用程式商店安装APP。
除了用容器内部的浏览器下载APK外,你还可以用ADB安装APK至ReDroid容器。比方说到ApkMirror下载Line的APK,接着用ADB安装:
adb -s localhost:5555 install "jp.naver.line.android.apk"
你也可以用ADB的pull
和push
指令传输档案。
关于萤幕解析度的调整,请参阅ReDroid和Scrcpy的使用说明。
如果要将ReDroid关机,将Scrcpy视窗关闭后,停止容器:
sudo docker ps
sudo docker stop
之后可以再用此指令启动ReDroid。ReDroid容器的/data
资料位于~/data11-nb
目录,可以用来备份多个系统的档案。
docker ps --filter "status=exited"
docker start
adb connect localhost:5555
scrcpy -s localhost:5555
目前来看,ReDroid的泛用性比Waydroid好,因为不只造福Linux使用者,连那些想摆脱专有模拟器、想自架云手机的用户都可以利用ReDroid达成目的。