Patch 处理
Patch 功能允许 VIP 用户修改和定制原生代码、资源文件,实现深度定制和快速修复。
什么是 Patch
Patch 是一种代码补丁机制,可以在不修改源码的情况下,对原生代码和资源进行修改。
使用场景
- 🐛 快速修复原生代码 bug
- 🎨 定制原生 UI 样式
- 🔧 修改原生配置文件
- 📝 替换原生资源文件
- 🚀 临时功能调整
- 🔄 版本兼容性处理
Patch 类型
1. 代码 Patch
修改 Java/Kotlin 或 Swift/Objective-C 代码。
支持的修改类型:
- 替换整个类
- 替换方法实现
- 添加新方法
- 修改方法调用
2. 资源 Patch
替换或新增资源文件。
支持的资源类型:
- 图片资源
- 布局文件
- 字符串资源
- 颜色值
- 样式定义
3. 配置 Patch
修改配置文件。
可修改的配置:
- AndroidManifest.xml
- Info.plist
- build.gradle
- Podfile
创建 Patch
项目结构
在项目根目录创建 nativePatches 目录:
your-project/
└── nativePatches/
├── android/
│ ├── code/
│ ├── res/
│ └── config/
└── ios/
├── code/
├── res/
└── config/Android Patch
修改代码
创建文件 nativePatches/android/code/MainActivity.patch:
// 原始文件: io/dcloud/PandoraEntry.java
package io.dcloud;
import android.os.Bundle;
public class PandoraEntry extends io.dcloud.common.DHUniAppEntryActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// PATCH: 添加自定义逻辑
initCustomFeature();
}
// PATCH: 新增方法
private void initCustomFeature() {
// 自定义初始化代码
android.util.Log.d("Patch", "Custom feature initialized");
}
}修改资源
替换启动图:
nativePatches/android/res/
└── drawable-xxhdpi/
└── splash.png // 替换默认启动图修改字符串资源 nativePatches/android/res/values/strings.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- PATCH: 修改应用名称 -->
<string name="app_name">我的定制应用</string>
</resources>修改配置
修改 AndroidManifest.xml nativePatches/android/config/AndroidManifest.patch.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest>
<application>
<!-- PATCH: 添加自定义 Activity -->
<activity
android:name=".CustomActivity"
android:theme="@style/CustomTheme" />
<!-- PATCH: 修改主题 -->
<activity android:name="io.dcloud.PandoraEntry"
android:theme="@style/MyCustomTheme" />
</application>
<!-- PATCH: 添加权限 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
</manifest>iOS Patch
修改代码
创建 nativePatches/ios/code/AppDelegate.patch.m:
// 原始文件: AppDelegate.m
#import "AppDelegate.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[super application:application didFinishLaunchingWithOptions:launchOptions];
// PATCH: 添加自定义逻辑
[self initCustomFeature];
return YES;
}
// PATCH: 新增方法
- (void)initCustomFeature {
NSLog(@"Custom feature initialized");
// 自定义初始化代码
}
@end修改资源
替换图标:
nativePatches/ios/res/
└── Assets.xcassets/
└── AppIcon.appiconset/
├── icon-60@2x.png
└── icon-60@3x.png修改配置
修改 Info.plist nativePatches/ios/config/Info.patch.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN">
<plist version="1.0">
<dict>
<!-- PATCH: 添加 URL Scheme -->
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
</array>
</dict>
</array>
<!-- PATCH: 修改相机权限说明 -->
<key>NSCameraUsageDescription</key>
<string>我们需要访问相机以拍摄照片</string>
</dict>
</plist>Patch 配置
全局配置
创建 nativePatches/config.json:
{
"version": "1.0.0",
"description": "项目自定义 Patch",
"android": {
"enabled": true,
"patches": [
{
"type": "code",
"target": "io/dcloud/PandoraEntry.java",
"source": "android/code/MainActivity.patch",
"action": "replace"
},
{
"type": "resource",
"target": "res/drawable-xxhdpi/splash.png",
"source": "android/res/drawable-xxhdpi/splash.png",
"action": "replace"
},
{
"type": "config",
"target": "AndroidManifest.xml",
"source": "android/config/AndroidManifest.patch.xml",
"action": "merge"
}
]
},
"ios": {
"enabled": true,
"patches": [
{
"type": "code",
"target": "AppDelegate.m",
"source": "ios/code/AppDelegate.patch.m",
"action": "replace"
},
{
"type": "config",
"target": "Info.plist",
"source": "ios/config/Info.patch.plist",
"action": "merge"
}
]
}
}Action 类型
replace: 完全替换目标文件merge: 合并到目标文件insert: 插入到指定位置delete: 删除指定内容
高级用法
条件 Patch
根据条件应用不同的 Patch:
{
"android": {
"patches": [
{
"type": "code",
"target": "MainActivity.java",
"source": "android/code/MainActivity.patch",
"condition": {
"minSdkVersion": 28
}
}
]
}
}脚本 Patch
使用脚本动态生成 Patch:
{
"android": {
"patches": [
{
"type": "script",
"script": "scripts/generate-patch.js",
"args": ["--flavor", "production"]
}
]
}
}脚本示例 scripts/generate-patch.js:
const fs = require('fs')
const flavor = process.argv[3] // 获取参数
// 根据参数生成不同的 Patch
const patchContent = `
// Generated for ${flavor}
public class Config {
public static final String FLAVOR = "${flavor}";
}
`
fs.writeFileSync('generated/Config.java', patchContent)正则替换
使用正则表达式替换:
{
"android": {
"patches": [
{
"type": "regex",
"target": "build.gradle",
"pattern": "versionCode \\d+",
"replacement": "versionCode 100"
}
]
}
}应用 Patch
自动应用
工具在打包时会自动应用配置的 Patch:
- 读取
nativePatches/config.json - 根据配置查找 Patch 文件
- 按顺序应用 Patch
- 生成最终的原生代码
手动应用
在工具中:
- 打开"工具" > "Patch 管理"
- 查看可用的 Patch 列表
- 选择要应用的 Patch
- 点击"应用 Patch"
验证 Patch
应用后验证:
- 查看应用日志,确认 Patch 已应用
- 检查生成的代码/资源文件
- 运行应用测试功能
常见 Patch 示例
1. 修改应用主题色
Android - nativePatches/android/res/values/colors.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#FF6B6B</color>
<color name="colorPrimaryDark">#EE5A5A</color>
<color name="colorAccent">#4ECDC4</color>
</resources>iOS - nativePatches/ios/code/ThemeConfig.patch.swift:
extension UIColor {
static var appPrimary: UIColor {
return UIColor(red: 1.0, green: 0.42, blue: 0.42, alpha: 1.0)
}
}2. 禁用横屏
Android - nativePatches/android/config/AndroidManifest.patch.xml:
<manifest>
<application>
<activity android:name="io.dcloud.PandoraEntry"
android:screenOrientation="portrait" />
</application>
</manifest>iOS - nativePatches/ios/config/Info.patch.plist:
<dict>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
</array>
</dict>3. 自定义状态栏
Android - nativePatches/android/code/StatusBarPatch.java:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 设置状态栏透明
Window window = getWindow();
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.TRANSPARENT);
}4. 修改网络请求超时
创建 Patch 修改网络配置:
{
"android": {
"patches": [
{
"type": "regex",
"target": "io/dcloud/common/network/NetworkConfig.java",
"pattern": "private static final int TIMEOUT = \\d+",
"replacement": "private static final int TIMEOUT = 30000"
}
]
}
}版本管理
Patch 版本
建议为 Patch 添加版本号:
nativePatches/
├── v1.0.0/
│ └── android/
├── v1.1.0/
│ └── android/
└── current -> v1.1.0/ # 软链接指向当前版本升级处理
应用版本升级时:
- 备份当前 Patch
- 测试新版本兼容性
- 调整或重新创建 Patch
- 验证功能正常
调试 Patch
查看应用结果
打包后检查:
# Android - 解压 APK
unzip app-release.apk -d output/
# 查看修改的文件
cat output/AndroidManifest.xml日志输出
在 Patch 中添加日志:
// Android
android.util.Log.d("PATCH", "Patch applied successfully");
// iOS
NSLog(@"PATCH: Patch applied successfully");真机测试
- 应用 Patch 后打包
- 安装到真机
- 运行并观察效果
- 查看日志输出
最佳实践
1. 文档化
为每个 Patch 添加注释:
/**
* PATCH v1.0.0
* 修改内容: 添加自定义初始化逻辑
* 原因: 需要在应用启动时初始化第三方 SDK
* 作者: zhangsan
* 日期: 2024-01-01
*/
private void initCustomFeature() {
// ...
}2. 版本控制
将 Patch 纳入版本控制:
# 不要忽略 nativePatches
!nativePatches/3. 测试充分
- 在多个设备上测试
- 测试不同系统版本
- 回归测试原有功能
4. 最小化修改
- 只修改必要的部分
- 避免大范围替换
- 保持与原代码风格一致
常见问题
Patch 未生效
解决方案:
- 检查 config.json 配置正确
- 确认文件路径匹配
- 清理构建缓存重新打包
- 查看打包日志
Patch 冲突
问题: 多个 Patch 修改同一文件
解决方案:
- 合并 Patch 内容
- 调整 Patch 顺序
- 使用更精确的匹配规则
升级后 Patch 失效
解决方案:
- 检查目标代码是否改变
- 更新 Patch 内容
- 测试验证
安全提示
- 🔒 不要在 Patch 中硬编码敏感信息
- 🔐 Patch 文件也需要代码审查
- 📝 记录所有 Patch 的变更历史
- 🧪 充分测试避免引入安全漏洞
