自 Android 7 起,应用会忽略用户提供的证书

自 Android 7 起,应用会忽略用户提供的证书,除非它们被配置为使用这些证书。由于大多数应用不会明确选择使用用户证书,我们需要将 mitmproxy CA 证书放入系统证书存储中,以避免必须为每个想要监控的应用打补丁。

注意:应用可以决定忽略系统证书存储并维护自己的 CA 证书。在这种情况下,你必须对该应用打补丁。

1. 先决条件

  • 已安装 Android Studio / Android SDK(已在 Linux 64 位版本 4.1.3 上测试)
  • 已创建 Android 虚拟设备(AVD)。设置文档可在此处获取
  • AVD 生产构建(标有“Google Play”的构建)会阻止你使用 adb root。如果你需要安装 Google Play,则需要使用 Magisk 方法
  • AVD 的代理设置已配置为使用 mitmproxy。文档在此处
  • Android SDK 中的 emulator 和 adb 可执行文件已添加到 $PATH 变量中

    • emulator 在 Linux 系统上通常位于 /home/<your_user_name>/Android/Sdk/emulator/emulator
    • adb 在 Linux 系统上通常位于 /home/<your_user_name>/Android/Sdk/platform-tools/adb
    • 我将这些行添加到了我的 .bashrc 中:

      export PATH=$PATH:$HOME/Android/Sdk/platform-tools
      export PATH=$PATH:$HOME/Android/Sdk/emulator
  • Mitmproxy CA 证书已创建

    • 在 Linux 系统上通常位于 ~/.mitmproxy/mitmproxy-ca-cert.cer
    • 如果该文件夹为空或不存在,请运行 mitmproxy 以生成证书

2. 重命名证书

Android 中的 CA 证书按其哈希值命名存储,扩展名为 0(例如:c8450d0d.0)。有必要计算出你的 CA 证书的哈希值,并将其复制到一个以该哈希值作为文件名的文件中。否则 Android 将忽略该证书。默认情况下,mitmproxy CA 证书位于此文件中:~/.mitmproxy/mitmproxy-ca-cert.cer

说明

  1. 进入你的证书文件夹:

    cd ~/.mitmproxy/
  2. 生成哈希并复制证书:

    hashed_name=`openssl x509 -inform PEM -subject_hash_old -in mitmproxy-ca-cert.cer | head -1` && cp mitmproxy-ca-cert.cer $hashed_name.0

3. 将证书插入系统证书存储

现在我们必须将我们的 CA 证书放入 Android 文件系统中位于 /system/etc/security/cacerts/ 的系统证书存储内。默认情况下,/system 分区以只读方式挂载。以下步骤描述了如何获得对 /system 分区的写入权限,以及如何复制上一步中创建的证书。

使用 Magisk 时的说明

如果你想使用生产构建(标有“Google Play”;就是那些安装了 Google Play 的构建),你可以使用 Magisk 在你的 AVD 中获取 root 权限。Magisk 允许在你的 Android 设备或模拟器上获得 root 权限。

有关在 AVD 上安装 Magisk 的说明,请参见此处。

注意:说明中说要启动你的 AVD。此时不要向 mitmproxy 提供 -http-proxy 指令。

完成后,你的模拟器将允许 root。你可以通过运行终端模拟器并输入 su 来检查这一点。Magisk 应该会询问你是否要向该程序授予 root 权限。授予后,输入 whoami 将显示 root。

然而,在你安装 Magisk 后,你不能再使用 -writable-system 启动模拟器。这会导致启动循环。(使用 -show-kernel 启动你的 AVD 以查看错误。)但是你可以通过将 mitmproxy 证书放入 Magisk 模块并安装该模块来安装它。Magisk 将在启动期间负责将你的证书复制到 /system/etc/security/cacerts/。

从 mitmweb 下载 Magisk 模块

如果你运行 mitmweb,你可以直接下载 Magisk 模块,而不必手工制作它:

  1. 停止你的 AVD,然后使用以下命令再次启动它:

    -http-proxy 127.0.0.1:8080

    (或你运行 mitmweb 代理所使用的任何 IP 和端口组合)

  2. 然后,在 AVD 内,启动浏览器并导航到:

    http://mitm.it/cert/magisk
  3. 你将被提示下载 mitmproxy-magisk-module.zip,这就是你需要的 Magisk 模块。将该文件存储在某处(例如“Downloads”中)。
  4. 然后打开 Magisk,点击 Modules 并安装你的模块。
  5. 重启你的 AVD。

创建包含你的证书的 Magisk 模块

如果你不运行 mitmweb,你需要自己创建一个 Magisk 模块。有关 Magisk 模块的深入信息,请参见此处,但基本上可以归结为:

  1. 创建以下目录:

    mitmproxycert                    (这将是你的模块根目录)
    mitmproxycert/com/google/android
    mitmproxycert/system/etc/security/cacerts
  2. 将你在第 2 步中重命名后的证书放入 mitmproxycert/system/etc/security/cacerts,并对其执行:

    chmod 664
  3. 将 https://github.com/topjohnwu/Magisk/blob/master/scripts/module_installer.sh 的内容保存为本地文件 update-binary,并放入 mitmproxycert/com/google/android。
  4. 创建一个名为 updater-script 的文件,其中仅包含字符串:

    #MAGISK

    并将其放入 mitmproxycert/com/google/android。

  5. 创建一个名为 module.prop 的文件,并将其放入 mitmproxycert。该文件应包含类似以下内容:

    id=mitmproxycert
    name=MITM proxy certificate
    version=1
    versionCode=1
    author=mitmproxycert
    description=My shiny MITM proxy certificate to reveal all secrets and obtain world domination!
  6. 使用类似以下命令将模块打包:

    cd ./mitmproxycert ; zip -r ./../mitmproxycert.zip ./

    然后使用以下命令将其推送到你正在运行的 AVD:

    adb push ./../mitmproxycert.zip /storage/emulated/0/Download/.
  7. 然后转到你的 AVD,打开 Magisk,点击 Modules 并安装你的模块(你会在 Downloads 文件夹中找到它)。
  8. 重启你的 AVD。

API 级别 > 28 且使用 -writable-system 的说明

默认情况下,/system 分区以只读方式挂载。以下步骤描述了如何获得对 /system 分区的写入权限,以及如何复制第 2 章中创建的证书。

从 API 级别 29(Android 10)开始,似乎无法将“/”分区挂载为读写。Google 使用 OverlayFS 为此问题提供了解决方法。不幸的是,在撰写本文时(2021 年 4 月 11 日),此解决方法中的说明会导致你的模拟器陷入启动循环。Stackoverflow 上某个聪明人找到了一种仍然可以让 /system 目录可写的方法。

请记住:如果你想使用你的证书,你必须始终使用 -writable-system 选项启动模拟器。否则 Android 将加载一个“干净”的系统镜像。

已在运行 API 级别 29 和 30 的模拟器上测试。

说明

  1. 列出你的 AVD:

    emulator -list-avds

    (如果这返回空列表,请在 Android Studio AVD Manager 中创建一个新的 AVD)

  2. 启动所需的 AVD:

    emulator -avd <avd_name_here> -writable-system

    (添加 -show-kernel 标志以查看内核日志)

  3. 以 root 身份重启 adb:

    adb root
  4. 禁用安全启动验证:

    adb shell avbctl disable-verification
  5. 重启设备:

    adb reboot
  6. 以 root 身份重启 adb:

    adb root
  7. 执行分区重新挂载为读写:

    adb remount

    (如果 adb 告诉你需要重启,请再次重启 adb reboot 并再次运行 adb remount。)

  8. 推送你在第 2 步中重命名后的证书:

    adb push <path_to_certificate> /system/etc/security/cacerts
  9. 设置证书权限:

    adb shell chmod 644 /system/etc/security/cacerts/<name_of_pushed_certificate>
  10. 重启设备:

    adb reboot

API 级别 <= 28 且使用 -writable-system 的说明

已在运行 API 级别 26、27 和 28 的模拟器上测试。

请记住:如果你想使用你的证书,你必须始终使用 -writable-system 选项启动模拟器。否则 Android 将加载一个“干净”的系统镜像。

说明

  1. 列出你的 AVD:

    emulator -list-avds

    (如果这返回空列表,请在 Android Studio AVD Manager 中创建一个新的 AVD)

  2. 启动所需的 AVD:

    emulator -avd <avd_name_here> -writable-system

    (添加 -show-kernel 标志以查看内核日志)

  3. 以 root 身份重启 adb:

    adb root
  4. 执行分区重新挂载为读写:

    adb remount

    (如果 adb 告诉你需要重启,请再次重启 adb reboot 并再次运行 adb remount。)

  5. 推送你在第 2 步中重命名后的证书:

    adb push <path_to_certificate> /system/etc/security/cacerts
  6. 设置证书权限:

    adb shell chmod 644 /system/etc/security/cacerts/<name_of_pushed_certificate>
  7. 重启设备:

    adb reboot

测试你的证书是否从系统证书存储中加载

在你的 AVD 中,转到:

设置 → 安全 → 高级 → 加密与凭据 → 受信任的凭据

在列表中找到你的证书(默认名称为 mitmproxy)。

使用 WSL 尤其是 Ubuntu 系统时,使用 ls 显示 /mnt/ 下的目录如果有中文,可能会显示乱码(具体可能是 Unicode 编码)。

问题解决方法

可尝试在终端中执行以下命令,设置编码为 UTF-8

export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8

简介

人見知りの小説家・高代槙生は、
姉夫婦の葬式で両親を亡くした姪の田汲朝を、勢いで引き取ることになる。

思いがけずはじまった同居生活によって、それまで静かだった槙生の日常は一変。
他人と暮らすことに不慣れな性格のため、15歳の朝との生活に不安を感じていた。
一方、両親を亡くし居場所を見失った朝は、はじめて感じる孤独の中で、
母とはまるで違う“大人らしくない”槙生の生き方に触れていく。

人づきあいが苦手で孤独を好む槙生と、人懐っこく素直な性格の朝。
性格も価値観もまるで違う二人は、戸惑いながらも、ぎこちない共同生活を始めていく。

共に、孤独を生きていく二人の、手探りで始まる年の差同居譚。

视频链接

【【异国日记】全13集 超清中字(未删减版)】https://www.bilibili.com/video/BV1vxDWBnEcW

分享可能会失效

当前的 React Native 开发生态中 Expo 已成为官方主推和社区广泛采用的开发方案。相比传统的 React Native CLI,Expo 有更简洁的项目初始化流程、更强大的工具链支持以及跨平台预览能力,适合初学者快速上手和中小型项目高效开发。

React Native CLI 依然可用,但其配置复杂、原生依赖管理繁琐,容易在不同操作系统上遇到兼容性问题。Expo 通过托管工作流(Managed Workflow)简化了原生模块的集成与构建流程,是现代 React Native 开发的首选方式。

本文将带你从零开始,完整走一遍使用 Expo 创建 React Native 项目、本地运行调试、打包 Android APK 与 iOS IPA 的全流程,涵盖环境准备、开发调试、签名配置及发布准备等关键环节。


一、环境准备

在开始之前,确保你的开发机器已安装以下工具:

  • Node.js
  • 包管理器:npm、yarn 或 pnpm 都可以,推荐优先用 npm 或 pnpm,后者在性能和依赖扁平化方面表现更好。

尽量保持所有工具为最新稳定版本。旧的 Node.js 或 npm 可能导致 node_modules 安装失败、依赖冲突或构建报错,出现莫名其妙的问题。

验证安装是否成功

打开终端(Terminal),依次执行以下命令:

node -v
npm -v

若能正常输出版本号(例如 v20.11.0 和 10.2.0),说明环境已就绪。


二、创建 React Native 项目

Expo 推荐使用 create-expo-app 快速初始化项目,无需手动配置 Webpack、Babel 原生工程。

初始化项目

在你希望存放项目的目录下执行:

npx create-expo-app myApp

创建一个标准 Expo 项目结构,里面有基础页面、配置文件和依赖项。

进入项目并启动开发服务器

cd myApp
npm start

成功启动后,终端会显示 Metro 打包服务(Metro Bundler)的运行界面,并自动打开浏览器页签(Expo DevTools)。

此时你会看到:

  • 一个 ASCII 风格的二维码
  • 一行提示文字:Scan the QR code above with Expo Go (Android) or the Camera app (iOS)
  • 多个快捷操作按钮(如运行模拟器、Web 预览等)

三、运行方式

Expo 支持多种方式运行应用。

1. 手机扫码运行

Android 设备

  1. 在 Google Play 商店下载并安装 Expo Go 应用。
  2. 打开应用 → 点击 "Scan QR Code" 。
  3. 对准电脑终端或浏览器中显示的二维码进行扫描。
  4. 应用将自动加载并运行。

iOS 设备

  1. 直接使用 系统相机 App 扫描二维码。
  2. 扫描后会弹出一个链接通知,点击选择在 Expo Go 中打开。
  3. 应用加载运行。

优势:无需数据线、无需编译,热重载(HMR)支持良好,非常适合真机调试。

2. 模拟器运行

在 Metro 终端界面中按对应键即可快速启动:

  • 按 a:启动 Android 模拟器(需已安装 Android Studio)
  • 按 i:启动 iOS 模拟器(仅限 macOS 用户)
  • 按 w:在浏览器中打开 Web 预览(基于 React Native for Web)

提示:Web 预览虽不能完全还原原生行为,但可用于快速验证 UI 布局和部分交互逻辑。


四、Android 打包

完成开发并准备发布测试包时,需要将项目构建成原生安装包(APK)。以下是基于 Expo 裸工作流(Bare Workflow)的手动打包流程。

配置 Android 构建环境

  1. 安装 Android Studio(推荐最新稳定版),安装过程中确保包含:

    • Android SDK
    • Android SDK Platform-Tools
    • Android SDK Build-Tools
    • Android Emulator(可选)
  2. 配置系统环境变量:

    • 新建系统变量 ANDROID_HOME,值为 SDK 路径(常见路径):
C:\Users\<你的用户名>\AppData\Local\Android\Sdk
- 将以下路径添加到 `PATH`:
%ANDROID_HOME%\platform-tools
%ANDROID_HOME%\emulator  (可选,用于命令行启动模拟器)

验证:终端执行 adb devices,若有设备列表返回则说明配置成功。

进入项目并运行原生构建命令

# 在项目根目录执行
npx expo run:android

首次运行可能出现红字警告提示未检测到设备或模拟器,可暂时忽略。此命令会尝试连接设备或启动模拟器。

随后进入原生工程目录:

cd android

创建 local.properties 文件(指定 SDK 路径)

在 android/ 目录下创建 local.properties 文件,内容如下(Windows 示例):

sdk.dir=C:\\Users\\<你的用户名>\\AppData\\Local\\Android\\Sdk

注意:如果是 Windows,路径中的反斜杠需用双反斜杠转义,且路径必须真实存在。


生成不同类型的 APK 包

1. 生成 Debug 包(用于测试)

./gradlew assembleDebug
  • 输出路径:android/app/build/outputs/apk/debug/app-debug.apk
  • 未签名,仅用于本地测试,不可上架。

2. 生成 Release 包(用于发布)

./gradlew assembleRelease
  • 输出路径:android/app/build/outputs/apk/release/app-release.apk
  • 可发布,但必须完成签名配置,否则无法安装。

五、Release 签名配置

为了发布到应用市场,APK 必须经过数字签名。以下是完整的签名流程配置。

1. 生成 Keystore 文件

在项目合适位置(如 android/app/)生成密钥库:

keytool -genkeypair -v -storetype JKS -keyalg RSA -keysize 2048 -validity 10000 \
-keystore keystor.jks -alias keystor

将生成的 keystor.jks 文件复制到 android/app/ 目录下。

2. 配置全局变量(android/gradle.properties)

在该文件中添加以下内容(若不存在可手动创建):

MYAPP_UPLOAD_STORE_FILE=keystor.jks
MYAPP_UPLOAD_KEY_ALIAS=keystor
MYAPP_UPLOAD_STORE_PASSWORD=你的仓库密码
MYAPP_UPLOAD_KEY_PASSWORD=你的密钥密码

安全建议:不要将密码硬编码在代码中,可考虑使用环境变量或 CI/CD 秘钥管理。

3. 修改构建脚本(android/app/build.gradle)

找到 android {... } 块内的 signingConfigs,修改 release 配置:

signingConfigs {
    release {
        if (project.hasProperty('MYAPP_UPLOAD_STORE_FILE')) {
            storeFile file(MYAPP_UPLOAD_STORE_FILE)
            storePassword MYAPP_UPLOAD_STORE_PASSWORD
            keyAlias MYAPP_UPLOAD_KEY_ALIAS
            keyPassword MYAPP_UPLOAD_KEY_PASSWORD
        }
    }
}

同时确保 buildTypes.release 使用了该签名配置:

buildTypes {
    release {
        signingConfig signingConfigs.release
        minifyEnabled false
        shrinkResources false
    }
}

完成签名后重新打包

./gradlew clean
./gradlew assembleRelease

此时生成的 app-release.apk 即为已签名的正式发布包,可用于内测或上传应用商店。


六、iOS 打包

重要提醒:在 Windows 系统上无法编译 iOS 安装包。iOS 构建必须在 macOS 系统 上完成,并需配备 Xcode 和真实设备测试。

打包前提条件

  1. 一台运行较新 macOS 的 Mac 电脑
  2. 已安装 Xcode(App Store 下载)
  3. 已安装 Command Line Tools
  4. 网络通畅(建议开启代理以加速依赖下载)
  5. 已有可运行的 Expo 项目

1. 验证 Xcode 命令行工具

终端执行:

xcode-select -p

预期输出:

/Applications/Xcode.app/Contents/Developer

若无输出,请运行:

sudo xcode-select --install

2. 安装依赖并预构建项目

npm install
npx expo prebuild -p ios

prebuild 命令会根据 app.json 配置生成原生工程目录(ios/ 和 android/)。若已有原生目录,可加 --clean 强制重建。

若提示 CocoaPods 错误

请先安装 Homebrew(若未安装):

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

再安装 CocoaPods:

brew install cocoapods

然后进入 ios/ 目录执行:

cd ios
pod install --repo-update

3. 使用 Xcode 打开项目

打开生成的 Workspace 文件:

open MyApp.xcworkspace

务必打开 **.xcworkspace**,而非 .xcodeproj,否则可能导致依赖加载失败。

4. 登录 Apple ID 并配置签名

个人开发者账号(未支付 $99/年)

  1. 在 Xcode 中进入 Preferences > Accounts,点击 “+” 添加 Apple ID。
  2. 登录后选中该账号,在项目 General 设置中选择对应的 Team。
  3. 同意最新的开发者协议(可能需访问 Apple Developer 网站确认)。

image

image

image

5. 添加设备 UDID

image

image

完成后重启 Xcode,确保签名状态变为 “Ready to Run”。

image


6. 手动生成 IPA

确保无误之后编译 Archive。

image

编译无误完成后会弹出如下界面:

image

image

image

image

  1. 在 Xcode 的 Products 目录下找到 .app 文件。
  2. 新建一个文件夹命名为 Payload。
  3. 将 .app 文件复制进去。
  4. 右键压缩 Payload 文件夹 → 得到 Payload.zip。
  5. 将 .zip 改名为 .ipa,即得到可安装的 IPA 包。

可通过邮件、企业分发平台等方式发送给测试人员安装。


七、结语

通过 Expo 构建 React Native 项目,不仅能享受“开箱即用”的开发体验,还能灵活切换托管与裸工作流,在需要时深入原生配置进行定制化打包。

场景推荐方式
快速原型开发Expo 托管工作流 + Expo Go 扫码
内测发布配置签名后构建 Release APK/IPA
上架应用市场使用 Expo Application Services (EAS) 或 Xcode 归档上传
持续集成推荐使用 EAS Build + CI/CD 工具链

随着 Expo 对 EAS(Expo Application Services)的持续优化,未来开发者甚至可以完全脱离本地原生环境,通过云端构建一键生成 Android 和 iOS 安装包,进一步降低入门门槛。

@REM example:
@REM del "%userprofile%/AppData/Local/Microsoft/vscode-cpptools/ipch/" /s /q /f
@REM rd "%userprofile%/AppData/Local/Microsoft/vscode-cpptools/ipch/" /s /q
@REM md "%userprofile%/AppData/Local/Microsoft/vscode-cpptools/ipch/"

@echo off

call:EmptyOneDir "%userprofile%\AppData\Local\Microsoft\vscode-cpptools\ipch"
call:EmptyOneDir "%userprofile%\AppData\Roaming\Code\CachedExtensionVSIXs"
call:EmptyOneDir "%userprofile%\AppData\Roaming\Code\Cache"
call:EmptyOneDir "%userprofile%\AppData\Roaming\Code\CachedData"
call:EmptyOneDir "%userprofile%\AppData\Roaming\Code\CachedExtensions"
call:EmptyOneDir "%userprofile%\AppData\Roaming\Code\CachedExtensionVSIXs"
call:EmptyOneDir "%userprofile%\AppData\Roaming\Code\Code Cache"
call:EmptyOneDir "%userprofile%\AppData\Roaming\Code\Crashpad"
call:EmptyOneDir "%userprofile%\AppData\Roaming\Code\logs"
call:EmptyOneDir "%userprofile%\AppData\Roaming\Code\Service Worker\CacheStorage"
call:EmptyOneDir "%userprofile%\AppData\Roaming\Code\Service Worker\ScriptCache"
call:EmptyOneDir "%userprofile%\AppData\Roaming\Code\User\workspaceStorage"
call:EmptyOneDir "%userprofile%\AppData\Roaming\Code\User\History"

goto end

:EmptyOneDir  rem same as Let empty [path] /q
    echo empty %1
    echo del %1 /s /q /f
    del %1 /s /q /f
    echo rd %1 /s /q
    rd %1 /s /q
    echo md %1
    md %1
:end
来源:https://blog.csdn.net/Tisfy/article/details/126082324