从API version 10开始,开启混淆后代码中的方法、属性或路径将被混淆。但在运行时,通过混淆前的原始名称访问已被混淆的方法、属性或路径,可能会导致功能失效。因此需要根据不同的场景配置相应的保留选项。
排查场景和配置字段时,推荐使用混淆助手配置保留选项,快速识别需要配置的保留选项和白名单字段。
| 功能 | 选项 | 起始API版本 |
|---|---|---|
| 指定保留属性名称 | -keep-property-name | 10 |
| 指定保留顶层作用域或导入导出元素名称 | -keep-global-name | 10 |
| 指定保留文件/文件夹名称 | -keep-file-name | 10 |
| 指定保留注释 | -keep-comments | 12 |
| 指定保留声明文件中的所有名称 | -keep-dts | 12 |
| 指定保留源码文件中的所有名称 | -keep | 12 |
| 名称类和路径类的保留选项支持通配符 | 保留选项支持的通配符 | 12 |
| 在代码压缩时排除指定路径的文件 | -keep-uncompact | 26.0.0 |
指定想保留的属性名,支持使用名称类通配符。按如下方式进行配置,表示保留名称为firstName和lastName的属性:
- -keep-property-name
- firstName
- lastName
使用该选项时,需要注意以下事项:
需要手动配置白名单的属性名:
如果代码中通过字符串拼接、变量访问或使用defineProperty方法定义对象属性,则这些属性名应被保留。
- // ArkGuardAbility.js
- var obj = {x0: '0', x1: '1', x2: '2'};
- for (var i = 0; i <= 2; i++) {
- console.info(obj['x' + i]); // x0, x1, x2应该被保留
- }
-
- Object.defineProperty(obj, 'y', {}); // y应该被保留
- Object.getOwnPropertyDescriptor(obj, 'y'); // y应该被保留
- console.info(obj.y);
-
- obj.s1 = 'a';
- let key = 's1';
- console.info(obj[key]); // key对应的变量值s1应该被保留
-
- obj.t1 = 'b';
- console.info(obj['t' + '1']); // t1应该被保留
对于如下的字符串常量形式的属性调用,可以选择性保留:
- // 混淆配置:
- // -enable-property-obfuscation
- // -enable-string-property-obfuscation
-
- // ArkGuardAbility.ts
- var obj2 = {t:'1', m:'2'};
- obj2.t = 'a';
- console.info(obj2['t']); // 此时,'t'会被正确混淆,t可以选择性保留
-
- obj2['m'] = 'b';
- console.info(obj2['m']); // 此时,'m'会被正确混淆,m可以选择性保留
对于间接或直接导出的类或对象的属性名的场景,如果混淆后出现问题,可以使用-keep-property-name来保留这些属性名。
- // 间接导出MyClass07
- class MyClass07 {
- greet() {}
- }
- let alias = new MyClass07();
- export { alias };
-
- // 直接导出MyClass08
- export class MyClass08 {
- exampleName: 'jack'
- exampleAge: 100
- }
在ArkTS/TS/JS文件中使用so库的API(如示例中的addNum)时,需手动保留API名称。
- // src/main/cpp/types/libentry/Index.d.ts
- export const addNum: (a: number, b: number) => number;
- // ArkGuardAbility.ets
- import testNapi from 'libentry.so';
- // ...
- testNapi.addNum(2, 3); // addNum需要保留,示例如:-keep-property-name addNum
JSON数据解析和对象序列化时,需要保留使用到的字段。
- {
- "jsonProperty": "value",
- "otherProperty": "value2"
- }
- import jsonData from './ImportJson.json';
- // ...
- let jsonProp = jsonData.jsonProperty; // jsonProperty应该被保留
-
- class JsonTest {
- prop1: string = '';
- prop2: number = 0
- }
-
- let obj = new JsonTest();
- const jsonStr = JSON.stringify(obj); // prop1 和 prop2 会被混淆,应该被保留
使用到的数据库相关的字段,需要手动保留。例如,数据库键值对类型(ValuesBucket)中的属性:
- import { ValuesBucket } from '@kit.ArkData';
- // ...
- const valueBucket: ValuesBucket = {
- ID1: 'ID1', // ID1应该被保留
- NAME1: 'jack', // NAME1应该被保留
- AGE1: 20, // AGE1应该被保留
- SALARY1: 100 // SALARY1应该被保留
- }
源码中自定义装饰器修饰了成员变量、成员方法、参数,同时其源码编译的中间产物为js文件时(如编译release源码HAR或者源码包含@ts-ignore、@ts-nocheck),这些装饰器所在的成员变量/成员方法名称需要被保留。这是由于ts高级语法特性转换为js标准语法时,将上述装饰器所在的成员变量/成员方法名称硬编码为字符串常量。
- function CustomDecorator(target: Object, propertyKey: string) {}
- function MethodDecorator(target: Object, propertyKey: string, descriptor: PropertyDescriptor) {}
- function ParamDecorator(target: Object, propertyKey: string, parameterIndex: number) {}
-
- class A {
- // 1.成员变量装饰器
- @CustomDecorator
- propertyName1: string = "" // propertyName1 需要被保留
- // 2.成员方法装饰器
- @MethodDecorator
- methodName1() {} // methodName1 需要被保留
- // 3.方法参数装饰器
- methodName2(@ParamDecorator param: string): void {} // methodName2 需要被保留
- }
使用到的数据请求相关的字段需要手动保留,例如,传递给数据请求方的字段需要手动保留:
- // ArkGuardAbility.ets
- import { UIAbility } from '@kit.AbilityKit';
- import { http } from '@kit.NetworkKit';
- // ...
- export default class EntryAbility extends UIAbility {
- onForeground(): void {
- let httpRequest = http.createHttp();
- httpRequest.request('https://www.example/Login',
- {
- method: http.RequestMethod.POST,
- header: { 'Content-Type': 'application/json' },
- extraData: { usernameTest: 'test1', passwordTest: 'test2'}, // usernameTest 和 passwordTest 需要被保留
- })
- }
- }
使用到的数字字面量属性需要手动保留。
- class MyClass09 {
- 123 = 'numeric-prop'; // 数字字面量属性
- [456] = 'computed'; // 计算属性中的数字
- method() {
- console.info(this[123]); // 123和456需要被保留
- console.info(this[456]);
- }
- }
指定要保留的顶层作用域及导入和导出元素的名称,支持使用名称类通配符。配置方式如下:
- -keep-global-name
- Person
- printPersonName
namespace中导出的名称也可以通过-keep-global-name选项保留。
- // ArkGuardAbility.ts
- export namespace Ns {
- export const myAge = 18 // -keep-global-name myAge 保留变量myAge
- export function myFunc() {} // -keep-global-name myFunc 保留函数myFunc
- }
使用该选项时,需要注意以下事项:
需要手动配置白名单的顶层作用域名称:
当以命名导入的方式导入so库的API时,如果同时开启-enable-toplevel-obfuscation和-enable-export-obfuscation选项,需要手动保留API的名称。
- // src/main/cpp/types/libentry/Index.d.ts
- declare function testNapi2(): void;
- declare function testNapi3(): void;
- // ArkGuardAbility.ets
- import { testNapi2, testNapi3 as myNapi } from 'libentry.so'; // testNapi2 和 testNapi3 应该被保留
- // ...
- testNapi2();
- myNapi();
指定要保留的文件或文件夹名称(不需要写文件后缀),支持使用名称类通配符。
以文件路径"utils/file.ets"为例,配置白名单的方法如下:
- -keep-file-name
- utils
- file
使用该选项时,需要注意以下事项:
- # 这种写法仅保留该条路径,pages目录下的文件和文件夹名称依旧会被混淆
- -keep-file-name
- ./src/main/ets/components/pages/**
需要手动配置白名单的文件名:
在使用require引入文件路径时,由于ArkTS不支持CommonJS模块语法,因此这种情况下require引入的文件路径应该被保留。
- // ArkGuardAbility.js
- const module1 = require('./RequireFile'); // RequireFile 应该被保留
对于动态导入的路径名,由于无法识别import函数中的参数是否为路径,因此在这种情况下应保留动态导入的路径名。
- // DynamicImportFile.ts
- export function foo () {}
- // ArkGuardAbility.ts
- const moduleName = './DynamicImportFile'; // moduleName对应的路径名DynamicImportFile应该被保留
- async function func2() {
- const modules = await import(moduleName);
- const result = modules.foo();
- }
对于API version 19及之前版本,使用Navigation跨包路由进行路由跳转时,传递给动态路由的路径应被保留。动态路由提供系统路由表和自定义路由表两种方式:
若采用自定义路由表进行跳转,配置白名单的方式与第二种动态引用场景一致。
若采用系统路由表进行跳转,则需将模块下resources/base/profile/route_map.json文件中pageSourceFile字段对应的路径添加到白名单中。
对于API version 20及之后版本,不再需要手动配置白名单。
- {
- "routerMap": [
- {
- "name": "PageOne",
- "pageSourceFile": "src/main/ets/pages/directory/PageOne.ets",
- "buildFunction": "PageOneBuilder",
- "data": {
- "description" : "this is PageOne"
- }
- }
- ]
- }
对于API version 19及之前版本,使用应用启动框架AppStartup时,启动参数配置文件和启动任务文件的路径应保留。这些路径配置在本模块的resources/base/profile/startup_config.json文件中,分别对应configEntry字段和startupTasks对象的srcEntry字段。
对于API version 20及之后版本,不再需要手动配置白名单。
startup_config.json文件示例如下:
- {
- "startupTasks": [
- {
- "name": "StartupTask_001",
- "srcEntry": "./ets/startup/StartupTask_001.ets",
- "dependencies": [
- "StartupTask_002"
- ],
- "runOnThread": "taskPool",
- "waitOnMainThread": false
- },
- {
- "name": "StartupTask_002",
- "srcEntry": "./ets/startup/StartupTask_002.ets",
- "runOnThread": "taskPool",
- "waitOnMainThread": false
- }
- ],
- "configEntry": "./ets/startup/StartupConfig.ets"
- }
配置白名单方式如下:
- -keep-file-name
- # 启动任务文件路径为:"./ets/startup/StartupTask_001.ets" 和 "./ets/startup/StartupTask_002.ets"。
- startup
- StartupTask_001
- StartupTask_002
-
- # 启动参数配置文件路径为:"./ets/startup/StartupConfig.ets"。
- StartupConfig
使用三方库提供的路由跳转方法时开启文件名混淆规则,文件路径将被混淆,从而导致跳转失败。因此需要将路由跳转的路径都配置到-keep-file-name下,防止文件路径被混淆。
保留编译生成的声明文件中class、function、namespace、enum、struct、interface、module、type及属性上方的JsDoc注释,支持使用名称类通配符。例如想保留声明文件中Human类上方的JsDoc注释,可进行以下配置:
- -keep-comments
- Human
使用该选项时,需要注意以下事项:
该选项在开启-remove-comments时生效。
当编译生成的声明文件中class、function、namespace、enum、struct、interface、module、type及属性的名称被混淆时,该元素上方的JsDoc注释无法通过-keep-comments保留。例如,当在-keep-comments中配置了exportClass时,如果exportClass类名被混淆,其JsDoc注释无法被保留。
- /**
- * @class exportClass
- */
- export class exportClass {}
指定路径filepath的.d.ts文件中的名称(如变量名、类名、属性名等)将被添加到-keep-global-name和-keep-property-name白名单中。请确保filepath为绝对路径,也可以指定为一个目录。如果指定为目录,则该目录下所有.d.ts文件中的名称都将被保留。
保留指定相对路径filepath中的所有名称(例如变量名、类名、属性名等)不被混淆。filepath可以是文件或文件夹,若是文件夹,则文件夹下的文件及子文件夹中文件都不混淆。
filepath仅支持相对路径,./和../为相对于混淆配置文件所在目录,支持使用路径类通配符。
- -keep
- ./src/main/ets/fileName.ts // fileName.ts中的名称不混淆
- ../folder // folder目录下文件及子文件夹中的名称都不混淆
- ../oh_modules/json5 // 引用的三方库json5里所有文件中的名称都不混淆
如何在模块中保留远程HAR包
方式一:指定远程HAR包在模块级oh_modules中的具体路径(该路径为软链接路径,真实路径为工程级oh_modules中的文件路径)。因为在配置模块级oh_modules中的路径作为白名单时,需要具体到包名或之后的目录才能正确地软链接到真实的目录路径,所以不能仅配置HAR包的上级目录名称。
- // 正例
- -keep
- ./oh_modules/harName1 // harName1目录下所有文件及子文件夹中的名称都不混淆
- ./oh_modules/harName1/src // src目录下所有文件及子文件夹中的名称都不混淆
- ./oh_modules/folder/harName2 // harName2目录下所有文件及子文件夹中的名称都不混淆
-
- // 反例
- -keep
- ./oh_modules // 保留模块级oh_modules里HAR包时,不支持配置HAR包的上级目录名称
方式二:指定远程HAR包在工程级oh_modules中的具体路径。工程级oh_modules中的文件路径均为真实路径,可直接配置。
- -keep
- ../oh_modules // 工程级oh_modules目录下所有文件及子文件夹中的名称都不混淆
- ../oh_modules/harName3 // harName3目录下所有文件及子文件夹中的名称都不混淆
模块级oh_modules和工程级oh_modules在DevEco Studio中的目录结构如下图所示:

使用该选项时,需要注意以下事项:
从API版本26.0.0开始,可通过-keep-uncompact指定相对路径下的源码不参与代码压缩。
使用该选项时,需要注意以下事项:
- -compact
- -keep-uncompact
- ./src/main/ets/example/FileA.ets
- ./src/main/ets/example/folder
- ../oh_modules/somePackage/src
名称类通配符使用方式如下:
| 通配符 | 含义 | 示例 |
|---|---|---|
| ? | 匹配任意单个字符 | "AB?"能匹配"ABC"等,但不能匹配"AB"。 |
| * | 匹配任意数量的任意字符 | "*AB*"能匹配"AB"、"aABb"、"cAB"、"ABc"等。 |
使用示例:
保留所有以a开头的属性名称:
- -keep-property-name
- a*
保留所有单个字符的属性名称:
- -keep-property-name
- ?
保留所有属性名称:
- -keep-property-name
- *
路径类通配符使用方式如下:
| 通配符 | 含义 | 示例 |
|---|---|---|
| ? | 匹配任意单个字符,除了路径分隔符/。 | "../a?"能匹配"../ab"等,但不能匹配"../a/"。 |
| * | 匹配任意数量的任意字符,但不包括路径分隔符/。 | "../a*/c"能匹配"../ab/c",但不能匹配"../ab/d/s/c"。 |
| ** | 匹配任意数量的任意字符。 | "../a**/c"能匹配"../ab/c",也能匹配"../ab/d/s/c"。 |
| ! | 表示非,只能写在某个路径最前端,用来排除用户配置的白名单中已有的某种情况。 | "!../a/b/c.ets"表示匹配除了"../a/b/c.ets"以外的路径。 |
使用示例:
表示路径../a/b/中所有文件夹(不包含子文件夹)中的c.ets文件不会被混淆:
- -keep
- ../a/b/*/c.ets
表示路径../a/b/中所有文件夹(包含子文件夹)中的c.ets文件不会被混淆:
- -keep
- ../a/b/**/c.ets
表示路径../a/b/中,除了c.ets文件以外的其它文件都不会被混淆。其中,!不可单独使用,只能用来排除白名单中已有的情况:
- -keep
- ../a/b/
- !../a/b/c.ets
表示路径../a/中的所有文件(不包含子文件夹)不会被混淆:
- -keep
- ../a/*
表示路径../a/下的所有文件夹(包含子文件夹)中的所有文件不会被混淆:
- -keep
- ../a/**
表示模块内的所有文件不会被混淆:
- -keep
- ./**
使用通配符时,需要注意以下事项:
以上选项不支持将通配符*、?、!用作其他含义。
- class A {
- '*'= 1
- }
-
- -keep-property-name
- *
此时*表示匹配任意数量的任意字符,配置效果为所有属性名称都不会被混淆,而不是只有*属性不被混淆。
-keep选项中只允许使用/路径格式,不支持\或\\。
合作咨询
我们的专家服务团队将竭诚为您提供专业的合作咨询服务
解决方案
精准高效的一站式服务支持,助力开发者商业成功