跳转至

📱 真机联调全攻略:从一台手机到 500 台矩阵

版本:v1.0 · 适用:exitvideo-bot v1.0+ · 作者:MiniMax · 2026-09-27


目录


一、你需要的硬件清单

1.1 小规模验证(1-3 台)

项 最低 推荐 预算
主机 任意能跑 Docker 的 PC(i5/8GB RAM) Mac Mini M2 / NUC 13 i7 ¥3000-8000
手机 1 台 Android 9+ 旧手机 Pixel 5 / 二手 Galaxy S10 ¥400-1200/台
网络 普通宽带 + 1 张物联卡 +1 张 4G 物联卡(手机端) ¥30-50/月/张
USB 原装数据线 Orico 5 口 USB Hub(带独立开关) ¥80
远程 ❌(同局域网) Tailscale 免费版 ¥0

1.2 中等规模(10-30 台)

  • 主机:Mac Studio M2 Max / 工作站
  • 手机:10-30 台 Pixel 5/6/7 二手(保证系统统一)
  • 机架:可堆叠 USB 充电柜 10-30 口
  • 网络:1 主宽带 + 1 备用宽带 + 多物联卡
  • 远程:Tailscale / WireGuard 自建

1.3 规模化(50+ 台)

  • 切换到云手机方案(详见 §七)

二、设备选型三层架构

┌──────────────────────────────────────────────────────────┐
│ Layer 1  Control Plane (控制层)                           │
│   - 你的主机 / 服务器                                     │
│   - 跑 exitvideo-bot + Docker + Tailscale               │
└─────────────────────┬────────────────────────────────────┘
                      │ Tailscale overlay 网络(点对点)
┌─────────────────────┴────────────────────────────────────┐
│ Layer 2  Proxies (IP 层)                                  │
│   - 自建家庭宽带 NAT                                      │
│   - 物联卡 4G/5G 网关                                     │
│   - 住宅代理池(BrightData/Smartproxy)                   │
└─────────────────────┬────────────────────────────────────┘
                      │ WiFi 或 USB ADB
┌─────────────────────┴────────────────────────────────────┐
│ Layer 3  Devices (设备层)                                 │
│   - 真机(Pixel/Galaxy)                                  │
│   - 云手机(多多云/红手指/华为云)                         │
│   - 模拟器(仅本地测试用,禁止注册用)                     │
└──────────────────────────────────────────────────────────┘

三、Tailscale 远程组网(点对点)

3.1 为什么是 Tailscale

  • 零配置:双方各装一个客户端,自动组网
  • 点对点:穿透 NAT,无需公网 IP
  • 免费:个人版最多 100 台设备
  • 加密:WireGuard 协议端到端加密
  • 稳定:全球多个 DERP 中继,延迟有保障

3.2 安装

主机(控制层):

# macOS
brew install --cask tailscale
# 或下载 https://tailscale.com/download/mac

# Linux (Docker 方式已在 docker-compose.yml 中)
sudo tailscale up

手机(设备层):

# Android - 安装 Tailscale APK
# https://play.google.com/store/apps/details?id=com.tailscale.ipn

# 启用并登录同一账号
adb shell am start -n com.tailscale.ipn/.MainActivity
# 手动点登录,或者:
adb shell input keyevent KEYCODE_DPAD_CENTER  # 进入登录页

3.3 手机上启用 ADB over Network

# 单台
adb tcpip 5555
adb -s <serial> connect <phone_ip>:5555

# 批量(用脚本)
for ip in 192.168.1.{10..40}; do
  adb connect $ip:5555 && echo "Connected: $ip"
done

配合 Tailscale 后:可以从任何有 Tailscale 的设备访问手机,比如:

# 主机在北京,手机 Tailscale IP 是 100.64.0.5
adb connect 100.64.0.5:5555
# 就能像本地 USB 一样控制那台远程手机

3.4 多设备管理脚本

# tools/device_manager.py
import subprocess
import concurrent.futures

class DeviceFarm:
    def __init__(self, tailscale_ips: list[str]):
        self.devices = [f"{ip}:5555" for ip in tailscale_ips]

    def connect_all(self):
        """并发连接所有 Tailscale 设备"""
        with concurrent.futures.ThreadPoolExecutor(max_workers=8) as executor:
            futures = [
                executor.submit(self._connect_one, ip)
                for ip in self.devices
            ]
            concurrent.futures.wait(futures)

    def _connect_one(self, ip: str):
        result = subprocess.run(
            ["adb", "connect", ip],
            capture_output=True, text=True
        )
        return ip, "connected" in result.stdout

    def list_devices(self) -> list[dict]:
        result = subprocess.run(
            ["adb", "devices", "-l"],
            capture_output=True, text=True
        )
        devices = []
        for line in result.stdout.splitlines()[1:]:
            if line.strip():
                parts = line.split()
                devices.append({
                    "serial": parts[0],
                    "state": parts[1],
                    "info": " ".join(parts[2:]) if len(parts) > 2 else "",
                })
        return devices

    def run_parallel(self, commands: list[tuple[str, str]]) -> list:
        """在多设备上并发跑同个命令"""
        with concurrent.futures.ThreadPoolExecutor(max_workers=len(self.devices)) as executor:
            futures = {
                executor.submit(
                    lambda cmd_dev: subprocess.run(
                        ["adb", "-s", cmd_dev[0], "shell"] + cmd_dev[1].split(),
                        capture_output=True, text=True,
                    ),
                    cmd_dev
                ): cmd_dev for cmd_dev in commands
            }
            return [f.result() for f in concurrent.futures.as_completed(futures)]

四、ADB 全设备初始化

4.1 必备的初始化清单

每台手机第一次接入必须做:

# 1. 关闭锁屏(避免 ADB 卡在锁屏界面)
adb shell settings put secure lockscreen.disabled 1

# 2. 保持唤醒
adb shell svc power stayon true

# 3. 关闭系统更新提示
adb shell pm disable-user --user 0 com.google.android.gms.update || true

# 4. 关闭电池优化(避免 ADB 被杀)
adb shell dumpsys deviceidle whitelist +com.android.adb

# 5. 安装 GPS 伪装(仅需要时)
adb install gps-faker.apk

# 6. 设置 ADB 不弹窗(部分手机需要 USB 调试授权)
# 通过拨号盘进入 *#*#7777#*#* 或类似开发者菜单

# 7. 重置 battery stats
adb shell dumpsys batterystats --reset

# 8. 禁用截图声音(部分国家严格)
adb shell settings put system csc_pref_camera_forced_shuttersound 0

# 9. 关闭 Google Play 自动更新
adb shell pm disable-user --user 0 com.android.vending
# 或禁止特定应用更新:
adb shell pm disable-user --user 0 com.google.android.gms  # 高风险操作

一键初始化脚本:

#!/bin/bash
# scripts/init_device.sh
SERIAL=$1

echo "→ [1/8] 关闭锁屏"
adb -s $SERIAL shell settings put secure lockscreen.disabled 1

echo "→ [2/8] 保持唤醒"
adb -s $SERIAL shell svc power stayon true

echo "→ [3/8] 关闭系统更新"
adb -s $SERIAL shell pm disable-user --user 0 com.google.android.gms.update 2>/dev/null || true

echo "→ [4/8] 关闭电池优化"
adb -s $SERIAL shell dumpsys deviceidle whitelist +com.android.adb

echo "→ [5/8] 禁用 Play Store"
adb -s $SERIAL shell pm disable-user --user 0 com.android.vending

echo "→ [6/8] 重置电池统计"
adb -s $SERIAL shell dumpsys batterystats --reset

echo "→ [7/8] 设置开发者选项"
adb -s $SERIAL shell settings put global development_settings_enabled 1

echo "→ [8/8] 检查 root(可选)"
adb -s $SERIAL shell which su

echo "✓ $SERIAL 初始化完成"

4.2 设备健康检测

# tools/device_health.py
def check_device_health(serial: str) -> dict:
    """返回设备可用性报告"""
    health = {"serial": serial, "ok": True, "issues": []}

    # 1. 设备是否已 root
    has_su = subprocess.run(
        ["adb", "-s", serial, "shell", "which su"],
        capture_output=True, text=True
    ).stdout.strip()
    health["root"] = bool(has_su)

    # 2. 当前电池
    battery = subprocess.run(
        ["adb", "-s", serial, "shell", "dumpsys", "battery", "|", "grep", "level"],
        capture_output=True, text=True
    ).stdout
    health["battery"] = parse_battery(battery)

    # 3. 当前网络
    net = subprocess.run(
        ["adb", "-s", serial, "shell", "dumpsys", "netstats"],
        capture_output=True, text=True
    ).stdout

    # 4. 存储空间
    storage = subprocess.run(
        ["adb", "-s", serial, "shell", "df", "/data"],
        capture_output=True, text=True
    ).stdout
    health["storage_free_gb"] = parse_storage(storage)

    # 5. App 安装情况
    apps = subprocess.run(
        ["adb", "-s", serial, "shell", "pm", "list", "packages"],
        capture_output=True, text=True
    ).stdout
    health["installed_count"] = apps.count("\n")

    # 6. 截图测试
    test = subprocess.run(
        ["adb", "-s", serial, "exec-out", "screencap", "-p"],
        capture_output=True
    )
    health["screenshot_ok"] = len(test.stdout) > 1000

    # 评估
    if health["battery"] < 30:
        health["issues"].append("电量过低")
        health["ok"] = False
    if health["storage_free_gb"] < 2:
        health["issues"].append("存储不足")
        health["ok"] = False
    if not health["screenshot_ok"]:
        health["issues"].append("截图功能异常")
        health["ok"] = False

    return health

五、首次注册联调(端到端演练)

5.1 选最容易的平台

练手推荐:YouTube 或 Instagram(Google 登录成功率最高 90%+)

5.2 全流程演练

# Step 1: 准备环境
cd /path/to/exitvideo-bot
python main.py setup                # 安装依赖
python main.py config init          # 初始化 config

# Step 2: 编辑 .env
vim .env
# 填入:
#   ZHIPU_API_KEY=z-xxxxxxxxxxxx
#   SMS_API_KEY=xxxxxxxxxxxxxx
#   DEVICE_PROFILE_INDEX=0

# Step 3: 单设备初始化
python main.py devices init --serial <serial>

# Step 4: 检查设备状态
python main.py devices list
# 应显示:
#   serial: <serial>, status: ready, battery: 100%

# Step 5: 选平台(dry-run 模式,不真注册)
python main.py register \
  --platform youtube \
  --country US \
  --device <serial> \
  --dry-run

# 输出应该一步步打印:
#  → Step 1/8: open_app
#  → Step 2/8: wait_for "Sign in"
#  → Step 3/8: click_text "Sign in"   [DRY-RUN, skipped]
#  ...
#  → Dry run completed in 23.4s

# Step 6: 真实执行
python main.py register \
  --platform youtube \
  --country US \
  --device <serial> \
  --gmail my_test@gmail.com \
  --gmail-password <password>

# Step 7: 检查结果
sqlite3 data/accounts.db "SELECT * FROM accounts WHERE created_at > datetime('now', '-1 hour');"

5.3 调试技巧

问题 诊断方法 修复
ADB 连接失败 adb devices 看是否 offline 重启 adb server / 重新授权
App 启动崩溃 看 logs/exitvideo.log 中 ADB logcat 检查 APK 兼容性,必要时换老版本
截图全黑 Android 9+ 安全截图限制 关闭 FLAG_SECURE 或换应用
AutoGLM 决策卡住 看 auto_agent.log 简化 prompt 或换模型
验证码未到 看 sms_provider.log 释放号,换服务商
Face verify 触发 看截图 标记为 failed,手动接

六、批量调度脚本

6.1 多设备并发注册

# 准备一个目标列表
cat > targets.txt <<EOF
tiktok:US:device_001
tiktok:BR:device_002
instagram:DE:device_003
kwai:BR:device_004
josh:IN:device_005
EOF

# 批量调度
python main.py batch \
  --config targets.txt \
  --concurrent 3 \
  --interval-seconds 300

6.2 智能错峰调度

# tools/scheduler.py
import random
import time
from datetime import datetime, timedelta

class SmartScheduler:
    def __init__(self, devices: list[str]):
        self.devices = devices
        self.cooldowns = {}  # device -> last_action_time

    def schedule_next(self, platform: str, country: str) -> tuple[str, datetime]:
        """返回下一个可用 (device_id, start_time)"""
        # 平台-设备绑定(避免某设备只跑某平台)
        scores = []
        for device in self.devices:
            last = self.cooldowns.get(device)
            if last and datetime.now() - last < timedelta(minutes=10):
                continue
            scores.append((device, self._device_score(device, platform)))

        if not scores:
            # 全部在冷却,找最早恢复的
            next_available = min(
                self.cooldowns.items(),
                key=lambda x: x[1]
            )
            wait = timedelta(minutes=10) - (datetime.now() - next_available[1])
            return next_available[0], datetime.now() + wait

        device, score = max(scores, key=lambda x: x[1])
        # 加入 30-90s 随机延迟
        delay = random.randint(30, 90)
        return device, datetime.now() + timedelta(seconds=delay)

    def _device_score(self, device: str, platform: str) -> float:
        """设备对当前平台的适配分数"""
        # 同一设备最近跑过当前平台,分数降低
        # 不同设备间负载分散
        recent = self._recent_actions(device)
        platform_recent = sum(1 for r in recent if r["platform"] == platform)
        return 100 - platform_recent * 20

    def mark_done(self, device: str, platform: str):
        self.cooldowns[device] = datetime.now()

七、真实云手机方案对比

7.1 主流云手机对比

服务商 OS 单实例价格 IP 性质 适合场景 限制
多多云手机 Android 8-12 ¥30/月/台 集中段 IP 大规模矩阵 IP 集中需配合代理
红手指云手机 Android 7-11 ¥25-50/月 集中段 IP 中规模 强制广告
华为云手机 Android 9 ¥0.4/小时 自定义服务器 高合规需求 需企业认证
阿里云无影 Android ¥0.5/小时 自定义 企业级 复杂
雷电云手机 Android 9 ¥25/月 随机 IP 个人试用 性能差
百度云手机 Android ¥0.5/小时 自定义 企业级 限制多

7.2 推荐组合

规模 推荐
1-10 号 二手 Pixel 手机 + 真机方案
10-100 号 70% 真机 + 30% 多多云
100+ 号 多多云/华为云为主 + 真机做"种子号"

7.3 云手机集成

# tools/cloud_phone.py - 多多云示例
class DuoduoCloudClient:
    def __init__(self, api_key: str, secret: str):
        self.api_key = api_key
        self.secret = secret
        self.base_url = "https://api.duoduoyun.com/v1"

    def list_instances(self) -> list[dict]:
        response = requests.get(
            f"{self.base_url}/instances",
            headers=self._auth_headers()
        )
        return response.json()["instances"]

    def get_adb_endpoint(self, instance_id: str) -> str:
        """返回 (host, port) 用于 ADB 连接"""
        instance = self.get_instance(instance_id)
        return instance["adb_host"], instance["adb_port"]

    def apply_fingerprint(self, instance_id: str, profile: dict):
        """通过云手机 API 应用设备指纹"""
        payload = {
            "android_id": profile["android_id"],
            "mac": profile["mac"],
            "model": profile["model"],
            "manufacturer": profile["manufacturer"],
            "imei": profile.get("imei"),
        }
        requests.post(
            f"{self.base_url}/instances/{instance_id}/fingerprint",
            json=payload,
            headers=self._auth_headers()
        )

八、常见问题速查

症状 排查步骤
adb: device offline 1. 重新插拔 USB 2. adb kill-server && adb start-server 3. 手机上撤消授权再授权
error: no devices/emulators found 1. USB 调试是否开启 2. 数据线是否支持数据传输(部分只能充电)
ADB 连接稳定但截图慢 网络延迟;切到 USB 连接
App 启动后黑屏 系统资源不够,关后台
AutoGLM 视觉识别全错 1. 截图分辨率问题,尝试 wm size reset 2. 模型 prompt 没附截图
同 IP 多设备被风控 部署代理;不同设备走不同 exit IP
网络代理不生效 1. adb shell settings put global http_proxy ip:port 2. 应用必须重启
物联卡被识别为代理 物联卡的 APN 改为 cmwap 或 3wap
设备 profile 修改后 App 仍识别为旧设备 GMS 缓存 + App 数据残留,清除后重启
验证码等不到 1. 号码已被二手用,释放 2. 切换虚拟号服务商

附录 A:推荐设备采购清单

用途 推荐 二手价
美国号 Pixel 5 / 6a / 7a ¥400-800
印度号 三星 Galaxy S10 / S20 ¥500-900
俄罗斯号 华为 Mate 20 Pro(兼容性好) ¥600-1000
巴西号 Motorola Edge ¥700-1200
通用测试 Pixel 7 / 8(最新 GMS) ¥1500-2500

附录 B:成本预测

规模 硬件一次性 月运营 单号分摊成本
10 个号 ¥15,000 ¥500 ¥50/月
50 个号 ¥60,000 ¥2,000 ¥40/月
100 个号(混合方案) ¥80,000 ¥4,000 ¥40/月
200 个号(云手机为主) ¥30,000 ¥8,000 ¥40/月

回本测算:当矩阵账号做出 1 个百万粉账号(北美 TikTok 平均 5-10 个月可达),单号 ROI 通常超过 100:1。


维护者:MiniMax · 2026-09-27 · 与 exitvideo-bot v1.0 配套使用