├── .github
└── workflows
│ └── android.yml
├── .gitignore
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── wzc
│ │ └── gradle
│ │ ├── plugin
│ │ ├── MainActivity.java
│ │ └── Test.java
│ │ └── test
│ │ └── Test.java
│ └── res
│ ├── drawable-v24
│ └── ic_launcher_foreground.xml
│ ├── drawable
│ └── ic_launcher_background.xml
│ ├── layout
│ └── activity_main.xml
│ ├── mipmap-anydpi-v26
│ ├── ic_launcher.xml
│ └── ic_launcher_round.xml
│ ├── mipmap-hdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-mdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── values-night
│ └── themes.xml
│ └── values
│ ├── colors.xml
│ ├── strings.xml
│ └── themes.xml
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── jitpack.yml
├── plugin_libs
├── StringObfuscatePlugin-1.0.0.jar
├── StringObfuscatePlugin-2.0.0.jar
└── StringObfuscatePlugin-2.0.1.jar
├── resources
└── diff.png
├── settings.gradle
├── string_obfuscated
├── .gitignore
├── build.gradle
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── wzc
│ │ └── gradle
│ │ └── plugin
│ │ ├── CreateTestClass.java
│ │ ├── MethodAdapter.java
│ │ ├── ScanClassVisitor.java
│ │ └── utils
│ │ └── StringEncryptionTestUtil.java
│ └── kotlin
│ └── com
│ └── wzc
│ └── gradle
│ └── plugin
│ ├── PluginLaunch.kt
│ ├── model
│ └── StringObfuscateExtensions.kt
│ └── utils
│ └── Logger.kt
└── test_library
├── .gitignore
├── build.gradle
├── consumer-rules.pro
├── proguard-rules.pro
└── src
├── androidTest
└── java
│ └── com
│ └── wzc
│ └── test_library
│ └── ExampleInstrumentedTest.java
├── main
├── AndroidManifest.xml
└── java
│ └── com
│ └── wzc
│ ├── test
│ └── Test1.java
│ └── test_library
│ └── Test2.java
└── test
└── java
└── com
└── wzc
└── test_library
└── ExampleUnitTest.java
/.github/workflows/android.yml:
--------------------------------------------------------------------------------
1 | name: Android CI
2 |
3 | on:
4 | push:
5 | branches: [ "master" ]
6 | pull_request:
7 | branches: [ "master" ]
8 |
9 | jobs:
10 | build:
11 |
12 | runs-on: ubuntu-latest
13 |
14 | steps:
15 | - uses: actions/checkout@v3
16 | - name: set up JDK 11
17 | uses: actions/setup-java@v3
18 | with:
19 | java-version: '11'
20 | distribution: 'temurin'
21 | cache: gradle
22 |
23 | - name: Grant execute permission for gradlew
24 | run: chmod +x gradlew
25 |
26 | - name: Build library
27 | run: ./gradlew string_obfuscated:assemble
28 |
29 | - name: Save library to artifacts
30 | uses: actions/upload-artifact@v2
31 | with:
32 | path: string_obfuscated/build/libs/*
33 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /.idea
4 | /local.properties
5 | .DS_Store
6 | /build
7 | /captures
8 | .externalNativeBuild
9 | .cxx
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 字符串混淆插件
2 |
3 | 1. 插件只能混淆java层代码
4 | 2. 插件只能在application类型的module中使用,插件会混淆application module所依赖的module和jar。所以请添加你要混淆的包名,否则所有第三方SDK都会被混淆
5 |
6 | ### 使用
7 |
8 | #### 方式一
9 |
10 | 在项目跟目录下的`build.gradle`文件中添加以下代码
11 |
12 | ```groovy
13 | buildscript {
14 | repositories {
15 | ...
16 | maven { url 'https://jitpack.io' }
17 | }
18 | dependencies {
19 | ...
20 | classpath 'com.github.wuzuchang:StringObfuscatedPlugin:2.1.0'
21 | }
22 | }
23 |
24 | allprojects {
25 | repositories {
26 | ...
27 | maven { url 'https://jitpack.io' }
28 | }
29 | }
30 | ```
31 |
32 | 在app module下的`build.gradle`中添加以下代码
33 |
34 | ```groovy
35 | plugins {
36 | id 'com.android.application'
37 | // 字符串混淆插件
38 | id 'com.wzc.string.obfuscate'
39 | }
40 | stringObfuscate{
41 | openLog = true
42 | // 添加需要混淆的包名
43 | packageName = ["com.wzc.gradle.plugin","com.wzc.test"]
44 | }
45 | ```
46 |
47 | #### 方式二
48 |
49 | 下载[插件jar](https://github.com/wuzuchang/StringObfuscatedPlugin/raw/master/plugin_libs/StringObfuscatePlugin-1.0.0.jar)文件,复制到项目中的plugin_libs目录下(没有就新增目录)
50 |
51 | ```java
52 | ├── plugin_libs
53 | │ └── StringObfuscatePlugin-1.0.0.jar
54 | ├── app
55 | │ ├── ..
56 | │ └── ..
57 | └── module
58 | │ ├── ..
59 | │ └── ..
60 | ```
61 |
62 | 在项目根目录`build.gradle`中添加以下代码
63 |
64 | ```groovy
65 | buildscript {
66 | repositories {
67 | ...
68 | flatDir {
69 | dirs 'plugin_libs'
70 | }
71 | }
72 | dependencies {
73 | ...
74 | classpath "com.wzc.string.obfuscate:StringObfuscatePlugin:1.0.0"
75 | }
76 | }
77 |
78 | allprojects {
79 | repositories {
80 | ...
81 | flatDir {
82 | dirs 'plugin_libs'
83 | }
84 | }
85 | }
86 | ```
87 |
88 | 在app module下的`build.gradle`中添加以下代码
89 |
90 | ```groovy
91 | plugins {
92 | id 'com.android.application'
93 | // 字符串混淆插件
94 | id 'com.wzc.string.obfuscate'
95 | }
96 | stringObfuscate{
97 | openLog = true
98 | // 添加需要混淆的包名
99 | packageName = ["com.wzc.gradle.plugin","com.wzc.test"]
100 | }
101 | ```
102 |
103 | 混淆前后对比
104 |
105 |
106 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | // 字符串混淆插件
4 | id 'com.wzc.string.obfuscate'
5 | }
6 | stringObfuscate{
7 | openLog = true
8 | packageName = ["com.wzc.gradle.plugin","com.wzc.test"]
9 | }
10 |
11 | android {
12 | compileSdkVersion 33
13 | buildToolsVersion "29.0.3"
14 |
15 | defaultConfig {
16 | applicationId "com.wzc.gradle.plugin"
17 | minSdkVersion 21
18 | targetSdkVersion 33
19 | versionCode 1
20 | versionName "1.0"
21 |
22 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
23 | }
24 |
25 | buildTypes {
26 | release {
27 | minifyEnabled false
28 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
29 | }
30 | }
31 | compileOptions {
32 | sourceCompatibility JavaVersion.VERSION_1_8
33 | targetCompatibility JavaVersion.VERSION_1_8
34 | }
35 | }
36 |
37 | dependencies {
38 |
39 | implementation 'androidx.appcompat:appcompat:1.4.1'
40 | implementation 'com.google.android.material:material:1.5.0'
41 | implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
42 | implementation project(path: ':test_library')
43 | testImplementation 'junit:junit:'
44 | androidTestImplementation 'androidx.test.ext:junit:1.1.3'
45 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
46 | }
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/wzc/gradle/plugin/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.wzc.gradle.plugin;
2 |
3 | import android.os.Bundle;
4 | import android.widget.TextView;
5 |
6 | import androidx.appcompat.app.AppCompatActivity;
7 |
8 | import com.wzc.test.Test1;
9 | import com.wzc.test_library.Test2;
10 |
11 | public class MainActivity extends AppCompatActivity {
12 |
13 |
14 | @Override
15 | protected void onCreate(Bundle savedInstanceState) {
16 | super.onCreate(savedInstanceState);
17 | setContentView(R.layout.activity_main);
18 | Test test = new Test();
19 | Test1 test1 = new Test1();
20 | Test2 test2 = new Test2();
21 | test.test();
22 | TextView textView = findViewById(R.id.text);
23 | textView.setText(" text=" +test + " text1=" + test1 + " test2=" + test2);
24 | }
25 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/wzc/gradle/plugin/Test.java:
--------------------------------------------------------------------------------
1 | package com.wzc.gradle.plugin;
2 |
3 |
4 | import android.util.Log;
5 |
6 | public class Test {
7 |
8 | private String string1 = "测试中文";
9 | private final String string2 = "test English";
10 | private static String string3 = "测试特殊字符@!#¥%……&*()~+——()_=";
11 | private static final String string4 = "测试换行\n换行了";
12 | private static final String string6 = "测试\r\n换行了";
13 | private static final String string5;
14 | private boolean test = false;
15 |
16 | static {
17 | string5 = "测试静态代码块"; //会生成
18 | }
19 |
20 | public String test() {
21 | Log.d("wzc==", "switch is open");
22 | return string1 + string2 + string3 +string4 +string5 ;
23 | }
24 |
25 | public void test(String a) {
26 | if (test) {
27 | Log.d("wzc==", "switch is open");
28 | }
29 | }
30 |
31 | class InnerClassTest {
32 | private int i;
33 | private String s;
34 |
35 | private void doSth(String p) {
36 | p = "hahah";
37 | Log.d("wzc==", p);
38 | }
39 | }
40 |
41 | @Override
42 | public String toString() {
43 | return "Test{" +
44 | "string1='" + string1 + '\'' +
45 | ", string2='" + string2 + '\'' +
46 | ", string3='" + string3 + '\'' +
47 | ", string4='" + string4 + '\'' +
48 | ", string5='" + string5 + '\'' +
49 | ", string6='" + string6 + '\'' +
50 | ", test=" + test +
51 | '}';
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/app/src/main/java/com/wzc/gradle/test/Test.java:
--------------------------------------------------------------------------------
1 | package com.wzc.gradle.test;
2 |
3 |
4 | import android.util.Log;
5 |
6 | public class Test {
7 |
8 | private String string1 = "测试中文";
9 | private final String string2 = "test English";
10 | private static String string3 = "测试特殊字符@!#¥%……&*()~+——()_="; //会生成
11 | private static final String string4 = "测试换行\n换行了";
12 | private static final String string6 = "测试\r\n换行了";
13 | private static final String string5;
14 | private boolean test = false;
15 |
16 | static {
17 | string5 = "测试静态代码块"; //会生成
18 | }
19 |
20 | public String test() {
21 | Log.d("wzc==", "switch is open");
22 | return string1 + string2 + string3 +string4 +string5 ;
23 | }
24 |
25 | public void test(String a) {
26 | if (test) {
27 | Log.d("wzc==", "switch is open");
28 | }
29 | }
30 |
31 | class InnerClassTest {
32 | private int i;
33 | private String s;
34 |
35 | private void doSth(String p) {
36 | p = "hahah";
37 | Log.d("wzc==", p);
38 | }
39 | }
40 |
41 | // @RequiresApi(api = Build.VERSION_CODES.O)
42 | // private static String stringDecrypt(String value, int key) {
43 | // if (value == null) {
44 | // return null;
45 | // }
46 | // return new String(Base64.getDecoder().decode(value));
47 | // }
48 | }
49 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuzuchang/StringObfuscatedPlugin/1aa1b3ea2bdfcc4011617775708ef5be2ba69fb2/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuzuchang/StringObfuscatedPlugin/1aa1b3ea2bdfcc4011617775708ef5be2ba69fb2/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuzuchang/StringObfuscatedPlugin/1aa1b3ea2bdfcc4011617775708ef5be2ba69fb2/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuzuchang/StringObfuscatedPlugin/1aa1b3ea2bdfcc4011617775708ef5be2ba69fb2/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuzuchang/StringObfuscatedPlugin/1aa1b3ea2bdfcc4011617775708ef5be2ba69fb2/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuzuchang/StringObfuscatedPlugin/1aa1b3ea2bdfcc4011617775708ef5be2ba69fb2/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuzuchang/StringObfuscatedPlugin/1aa1b3ea2bdfcc4011617775708ef5be2ba69fb2/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuzuchang/StringObfuscatedPlugin/1aa1b3ea2bdfcc4011617775708ef5be2ba69fb2/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuzuchang/StringObfuscatedPlugin/1aa1b3ea2bdfcc4011617775708ef5be2ba69fb2/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuzuchang/StringObfuscatedPlugin/1aa1b3ea2bdfcc4011617775708ef5be2ba69fb2/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | MyGradlePlugin
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories{
3 | flatDir {
4 | dirs 'plugin_libs'
5 | }
6 | }
7 | dependencies {
8 | // classpath 'com.github.wuzuchang:StringObfuscatePlugin:1.0.0'
9 | classpath 'com.wzc.string.obfuscate:StringObfuscatePlugin:2.0.0'
10 | }
11 | }
12 |
13 | plugins {
14 | id 'com.android.application' version '7.4.1' apply false
15 | id 'com.android.library' version '7.4.1' apply false
16 | id 'org.jetbrains.kotlin.android' version '1.8.0' apply false
17 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuzuchang/StringObfuscatedPlugin/1aa1b3ea2bdfcc4011617775708ef5be2ba69fb2/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Aug 06 14:20:31 CST 2021
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/jitpack.yml:
--------------------------------------------------------------------------------
1 | jdk:
2 | - openjdk11
--------------------------------------------------------------------------------
/plugin_libs/StringObfuscatePlugin-1.0.0.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuzuchang/StringObfuscatedPlugin/1aa1b3ea2bdfcc4011617775708ef5be2ba69fb2/plugin_libs/StringObfuscatePlugin-1.0.0.jar
--------------------------------------------------------------------------------
/plugin_libs/StringObfuscatePlugin-2.0.0.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuzuchang/StringObfuscatedPlugin/1aa1b3ea2bdfcc4011617775708ef5be2ba69fb2/plugin_libs/StringObfuscatePlugin-2.0.0.jar
--------------------------------------------------------------------------------
/plugin_libs/StringObfuscatePlugin-2.0.1.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuzuchang/StringObfuscatedPlugin/1aa1b3ea2bdfcc4011617775708ef5be2ba69fb2/plugin_libs/StringObfuscatePlugin-2.0.1.jar
--------------------------------------------------------------------------------
/resources/diff.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuzuchang/StringObfuscatedPlugin/1aa1b3ea2bdfcc4011617775708ef5be2ba69fb2/resources/diff.png
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | gradlePluginPortal()
6 | maven { url 'https://jitpack.io' }
7 | }
8 | }
9 | dependencyResolutionManagement {
10 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
11 | repositories {
12 | google()
13 | mavenCentral()
14 | maven { url 'https://jitpack.io' }
15 | }
16 | }
17 | rootProject.name = "StringObfuscatedPlugin"
18 | include ':string_obfuscated'
19 | include ':app'
20 | include ':test_library'
21 |
--------------------------------------------------------------------------------
/string_obfuscated/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/string_obfuscated/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'java-gradle-plugin'
3 | id 'org.jetbrains.kotlin.jvm'
4 | id 'maven-publish'
5 | }
6 |
7 | def group = 'com.wzc.string.obfuscate'
8 | def versionCode = '2.0.1' //版本
9 | def pluginName = 'StringObfuscatePlugin' //唯一标示
10 |
11 | //插件配置,这样就不需要创建META-INF/gradle-plugins文件目录了
12 | gradlePlugin {
13 | plugins {
14 | //定义的信息可以和发布信息不相同
15 | MethodTracer {
16 | id = group //定义插件id
17 | version = versionCode
18 | implementationClass = 'com.wzc.gradle.plugin.PluginLaunch' //定义插件实现类
19 | }
20 | }
21 | }
22 |
23 | publishing {
24 | publications {
25 | //PubName
26 | plugin(MavenPublication) {
27 | groupId = group
28 | artifactId = pluginName
29 | version = versionCode
30 | artifact("../plugin_libs/StringObfuscatePlugin-${version}.jar")
31 | }
32 | }
33 | repositories {
34 | maven {
35 | // change to point to your repo, e.g. http://my.org/repo
36 | url = "$buildDir/repo"
37 | }
38 | }
39 | }
40 |
41 | dependencies {
42 | implementation gradleApi()
43 | // 实现Transform的时候会用到,gradle插件版本:https://developer.android.com/studio/releases/gradle-plugin?hl=zh-cn#updating-gradle
44 | // 这里注意使用的gradle插件(AGP)版本不要超过你Android studio 最大支持的版本:https://developer.android.com/studio/releases?hl=zh-cn#android_gradle_plugin_and_android_studio_compatibility
45 | // gradle/wrapper 中设置你gradle版本
46 | implementation("com.android.tools.build:gradle:7.3.1") {
47 | exclude group: "org.ow2.asm"
48 | }
49 | implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.10") {
50 | exclude group: "org.ow2.asm"
51 | }
52 | implementation("org.ow2.asm:asm:9.3")
53 | implementation("org.ow2.asm:asm-commons:9.3")
54 | implementation("org.ow2.asm:asm-util:9.3")
55 | }
56 |
57 | java {
58 | sourceCompatibility = JavaVersion.VERSION_1_8
59 | targetCompatibility = JavaVersion.VERSION_1_8
60 | }
61 |
62 | task copyPluginFile {
63 | doLast {
64 | copy {
65 | from '../string_obfuscated/build/libs/'
66 | into '../plugin_libs'
67 | rename "string_obfuscated-${versionCode}.jar", "${pluginName}-${versionCode}.jar"
68 | }
69 | }
70 | }
71 |
72 | assemble.finalizedBy(copyPluginFile)
73 | build.finalizedBy(copyPluginFile)
74 | jar.finalizedBy(copyPluginFile)
--------------------------------------------------------------------------------
/string_obfuscated/src/main/java/com/wzc/gradle/plugin/CreateTestClass.java:
--------------------------------------------------------------------------------
1 | package com.wzc.gradle.plugin;
2 |
3 | import org.gradle.internal.impldep.org.apache.commons.io.FileUtils;
4 | import org.objectweb.asm.AnnotationVisitor;
5 | import org.objectweb.asm.ClassWriter;
6 | import org.objectweb.asm.FieldVisitor;
7 | import org.objectweb.asm.MethodVisitor;
8 | import org.objectweb.asm.Opcodes;
9 |
10 | import java.io.File;
11 | import java.io.IOException;
12 |
13 | /**
14 | * 通过ASM创建class,但是ASM创建的class不会自动导包。建议使用javapoet生成class文件
15 | */
16 | public class CreateTestClass {
17 |
18 |
19 | public void create(String javac, String transforms, String packageName) throws IOException {
20 | //定义一个叫做Example的类
21 | ClassWriter cw = new ClassWriter(0);
22 | cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "Example", null, "java/lang/Object", null);
23 |
24 | //生成默认的构造方法
25 | MethodVisitor mw = cw.visitMethod(Opcodes.ACC_PUBLIC,
26 | "",
27 | "()V",
28 | null,
29 | null);
30 |
31 | //生成构造方法的字节码指令
32 | mw.visitVarInsn(Opcodes.ALOAD, 0);
33 | mw.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "", "()V", false);
34 | mw.visitInsn(Opcodes.RETURN);
35 | mw.visitMaxs(1, 1);
36 | mw.visitEnd();
37 |
38 |
39 | //生成String name字段
40 | FieldVisitor fv = cw.visitField(Opcodes.ACC_PUBLIC, "name", "Ljava/lang/String;", null, null);
41 | AnnotationVisitor av = fv.visitAnnotation("LNotNull;", true);
42 | av.visit("value", "abc");
43 | av.visitEnd();
44 | fv.visitEnd();
45 |
46 |
47 | //生成main方法
48 | mw = cw.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC,
49 | "main",
50 | "([Ljava/lang/String;)V",
51 | null,
52 | null);
53 |
54 | //生成main方法中的字节码指令
55 | mw.visitFieldInsn(Opcodes.GETSTATIC,
56 | "java/lang/System",
57 | "out",
58 | "Ljava/io/PrintStream;");
59 |
60 | mw.visitLdcInsn("Hello world!");
61 | mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
62 | "java/io/PrintStream",
63 | "println",
64 | "(Ljava/lang/String;)V",
65 | false);
66 | mw.visitInsn(Opcodes.RETURN);
67 | mw.visitMaxs(2, 2);
68 |
69 | //字节码生成完成
70 | mw.visitEnd();
71 |
72 | // 获取生成的class文件对应的二进制流
73 | byte[] code = cw.toByteArray();
74 |
75 | //拷贝到javac
76 | FileUtils.writeByteArrayToFile(
77 | new File(javac + packageName + "Example.class"), code);
78 | //将生成类拷贝到transforms,用于dex
79 | FileUtils.writeByteArrayToFile(
80 | new File(transforms + packageName + "Example.class"), code);
81 | }
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/string_obfuscated/src/main/java/com/wzc/gradle/plugin/MethodAdapter.java:
--------------------------------------------------------------------------------
1 | package com.wzc.gradle.plugin;
2 |
3 | import com.wzc.gradle.plugin.utils.StringEncryptionTestUtil;
4 |
5 | import org.objectweb.asm.MethodVisitor;
6 | import org.objectweb.asm.Opcodes;
7 | import org.objectweb.asm.commons.AdviceAdapter;
8 |
9 | import java.util.HashMap;
10 | import java.util.Set;
11 |
12 | /**
13 | * 方法修改
14 | * AdviceAdapter API:https://asm.ow2.io/javadoc/org/objectweb/asm/commons/AdviceAdapter.html
15 | * AdviceAdapter:实现了MethodVisitor接口,主要访问方法的信息。用来对具体方法进行字节码操作;
16 | * FieldVisitor:访问具体的类成员;
17 | * AnnotationVisitor:访问具体的注解信息
18 | */
19 | public class MethodAdapter extends AdviceAdapter {
20 |
21 | private final HashMap mStaticFinalField;
22 | private final String mMethodName;
23 | private final String mOwner;
24 | private final int key1;
25 | private final int key2;
26 | private final int key3;
27 |
28 | protected MethodAdapter(int api, MethodVisitor methodVisitor, int access, String name, String descriptor, HashMap staticFinalField, String owner, int key1, int key2, int key3) {
29 | super(api, methodVisitor, access, name, descriptor);
30 | mStaticFinalField = staticFinalField;
31 | mMethodName = name;
32 | mOwner = owner;
33 | this.key1 = key1;
34 | this.key2 = key2;
35 | this.key3 = key3;
36 | }
37 |
38 | @Override
39 | public void visitCode() {
40 | super.visitCode();
41 | if ("".equals(mMethodName)) {
42 | Set strings = mStaticFinalField.keySet();
43 | for (String field : strings) {
44 | if ("".equals(field)){
45 | super.visitLdcInsn(field);
46 | continue;
47 | }
48 | String value = mStaticFinalField.get(field);
49 | String encryption = StringEncryptionTestUtil.encryption(value, key1, key2, key3);
50 | mv.visitLdcInsn(encryption);
51 | mv.visitIntInsn(BIPUSH, key1);
52 | mv.visitMethodInsn(INVOKESTATIC, mOwner, "stringDecrypt", "(Ljava/lang/String;I)Ljava/lang/String;", false);
53 | mv.visitFieldInsn(Opcodes.PUTSTATIC, mOwner, field, "Ljava/lang/String;");
54 | ScanClassVisitor.hasString = true;
55 | }
56 | }
57 | }
58 |
59 | @Override
60 | public void visitLdcInsn(Object value) {
61 | if (value instanceof String && !"".equals(value)) {
62 | String encryption = StringEncryptionTestUtil.encryption((String) value, key1, key2, key3);
63 | mv.visitLdcInsn(encryption);
64 | mv.visitIntInsn(BIPUSH, key1);
65 | mv.visitMethodInsn(INVOKESTATIC, mOwner, "stringDecrypt", "(Ljava/lang/String;I)Ljava/lang/String;", false);
66 | ScanClassVisitor.hasString = true;
67 | } else {
68 | super.visitLdcInsn(value);
69 | }
70 | }
71 |
72 |
73 | @Override
74 | public void visitVarInsn(int opcode, int var) {
75 | super.visitVarInsn(opcode, var);
76 | }
77 |
78 | @Override
79 | public void endMethod() {
80 | super.endMethod();
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/string_obfuscated/src/main/java/com/wzc/gradle/plugin/ScanClassVisitor.java:
--------------------------------------------------------------------------------
1 | package com.wzc.gradle.plugin;
2 |
3 |
4 | import static org.gradle.internal.impldep.bsh.org.objectweb.asm.Constants.*;
5 |
6 | import com.wzc.gradle.plugin.utils.Logger;
7 |
8 | import org.objectweb.asm.ClassVisitor;
9 | import org.objectweb.asm.FieldVisitor;
10 | import org.objectweb.asm.Label;
11 | import org.objectweb.asm.MethodVisitor;
12 | import org.objectweb.asm.Opcodes;
13 |
14 | import java.util.HashMap;
15 | import java.util.Random;
16 |
17 | /**
18 | * ClassVisitor API: https://asm.ow2.io/javadoc/org/objectweb/asm/ClassVisitor.html
19 | */
20 | public class ScanClassVisitor extends ClassVisitor {
21 |
22 | private static final int ACC_STATIC_FINAL = Opcodes.ACC_STATIC | Opcodes.ACC_FINAL;
23 | private static final String STRING_DESCRIPTOR = "Ljava/lang/String;";
24 | private final HashMap mStaticFinalField = new HashMap<>();
25 | private boolean hasClinit;
26 | public static boolean hasString;
27 | private boolean hasStringDecrypt;
28 | private String mOwner;
29 | private final int key1;
30 | // 同一个类里面 key2和key3是固定的,不同类key2和key3不一样
31 | private final int key2;
32 | private final int key3;
33 |
34 |
35 | public ScanClassVisitor(int api, ClassVisitor classVisitor) {
36 | super(api, classVisitor);
37 | hasString = false;
38 | hasStringDecrypt = false;
39 | key1 = getRandomKey();
40 | key2 = getRandomKey();
41 | key3 = getRandomKey();
42 | }
43 |
44 | private int getRandomKey() {
45 | Random rand = new Random();
46 | return rand.nextInt(127);
47 | }
48 |
49 | /**
50 | * 可以拿到类的详细信息,然后对满足条件的类进行过滤
51 | *
52 | * @param version JDK版本
53 | * @param access 类的修饰符
54 | * @param name 类的名称,通常使用完整包名+类名
55 | * @param signature 泛型信息,如果没有定义泛型该参数为null
56 | * @param superName 当前类的父类,所有类的父类java.lang.Object
57 | * @param interfaces 实现的接口列表
58 | */
59 | @Override
60 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
61 | super.visit(version, access, name, signature, superName, interfaces);
62 | mOwner = name;
63 | }
64 |
65 | /**
66 | * 访问内部类信息
67 | *
68 | * @param name 内部类名称
69 | * @param outerName 内部类所属的类的名称
70 | * @param innerName 内部类在其封闭类中的(简单)名称。对于匿名内部类,可能为 null。
71 | * @param access 内部类的修饰符
72 | */
73 | @Override
74 | public void visitInnerClass(String name, String outerName, String innerName, int access) {
75 | super.visitInnerClass(name, outerName, innerName, access);
76 | }
77 |
78 | /**
79 | * 类中字段
80 | *
81 | * @param access 修饰符
82 | * @param name 字段名
83 | * @param descriptor 字段类型
84 | * @param signature 泛型描述
85 | * @param value 默认值
86 | */
87 | @Override
88 | public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
89 |
90 | boolean isStaticFinal = (access & ACC_STATIC_FINAL) == ACC_STATIC_FINAL;
91 | if (value instanceof String && !"".equals(value)) {
92 | hasString = true;
93 | // 将 final + static 修饰的变量置空(不然无法在静态块中初始化),之后再在中赋值
94 | if (STRING_DESCRIPTOR.equals(descriptor) && isStaticFinal) {
95 | mStaticFinalField.put(name, (String) value);
96 | return super.visitField(access, name, descriptor, signature, null);
97 | }
98 | }
99 | return super.visitField(access, name, descriptor, signature, value);
100 | }
101 |
102 | /**
103 | * 拿到要修改的方法,然后进行修改
104 | *
105 | * @param access 方法的修饰符
106 | * @param name 方法名
107 | * @param descriptor 方法签名,返回值
108 | * @param signature 泛型相关信息
109 | * @param exceptions 抛出的异常,没有异常抛出该参数为null
110 | * @return
111 | */
112 | @Override
113 | public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
114 | MethodVisitor methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions);
115 | if ("stringDecrypt".equals(name)) {
116 | hasStringDecrypt = true;
117 | return methodVisitor;
118 | }
119 | if ("".equals(name)) {
120 | hasClinit = true;
121 | }
122 | return new MethodAdapter(Opcodes.ASM9, methodVisitor, access, name, descriptor
123 | , mStaticFinalField, mOwner, key1, key2, key3);
124 | }
125 |
126 | /**
127 | * 该方法是最后一个被调用的方法,用于通知访问者该类的所有字段和方法都已访问。
128 | */
129 | @Override
130 | public void visitEnd() {
131 |
132 | // 如果没有扫描方法,则说明全是final + static;需要插入static ()方法
133 | if (!hasClinit && mStaticFinalField.size() > 0) {
134 | Logger.INSTANCE.d(mOwner + " add method");
135 | MethodVisitor mv = visitMethod(Opcodes.ACC_STATIC, "", "()V", null, null);
136 | mv.visitCode();
137 | mv.visitInsn(Opcodes.RETURN);
138 | mv.visitMaxs(1, 0);
139 | mv.visitEnd();
140 | }
141 | if (hasString && !hasStringDecrypt) {
142 | Logger.INSTANCE.d(mOwner + " add stringDecrypt method");
143 | addMethod(key2, key3);
144 | }
145 | super.visitEnd();
146 | }
147 |
148 | private void addMethod(int key2, int key3) {
149 | MethodVisitor methodVisitor = this.visitMethod(ACC_PUBLIC | ACC_STATIC, "stringDecrypt", "(Ljava/lang/String;I)Ljava/lang/String;", null, null);
150 | methodVisitor.visitCode();
151 | Label label0 = new Label();
152 | Label label1 = new Label();
153 | Label label2 = new Label();
154 | methodVisitor.visitTryCatchBlock(label0, label1, label2, "java/lang/Exception");
155 | methodVisitor.visitLabel(label0);
156 | methodVisitor.visitLineNumber(54, label0);
157 | methodVisitor.visitVarInsn(ALOAD, 0);
158 | methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "length", "()I", false);
159 | methodVisitor.visitInsn(ICONST_2);
160 | methodVisitor.visitInsn(IDIV);
161 | methodVisitor.visitVarInsn(ISTORE, 2);
162 | Label label3 = new Label();
163 | methodVisitor.visitLabel(label3);
164 | methodVisitor.visitLineNumber(55, label3);
165 | methodVisitor.visitVarInsn(ALOAD, 0);
166 | methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "toCharArray", "()[C", false);
167 | methodVisitor.visitVarInsn(ASTORE, 3);
168 | Label label4 = new Label();
169 | methodVisitor.visitLabel(label4);
170 | methodVisitor.visitLineNumber(56, label4);
171 | methodVisitor.visitVarInsn(ILOAD, 2);
172 | methodVisitor.visitIntInsn(NEWARRAY, T_BYTE);
173 | methodVisitor.visitVarInsn(ASTORE, 4);
174 | Label label5 = new Label();
175 | methodVisitor.visitLabel(label5);
176 | methodVisitor.visitLineNumber(57, label5);
177 | methodVisitor.visitInsn(ICONST_0);
178 | methodVisitor.visitVarInsn(ISTORE, 5);
179 | Label label6 = new Label();
180 | methodVisitor.visitLabel(label6);
181 | methodVisitor.visitFrame(Opcodes.F_FULL, 6, new Object[]{"java/lang/String", Opcodes.INTEGER, Opcodes.INTEGER, "[C", "[B", Opcodes.INTEGER}, 0, new Object[]{});
182 | methodVisitor.visitVarInsn(ILOAD, 5);
183 | methodVisitor.visitVarInsn(ILOAD, 2);
184 | Label label7 = new Label();
185 | methodVisitor.visitJumpInsn(IF_ICMPGE, label7);
186 | Label label8 = new Label();
187 | methodVisitor.visitLabel(label8);
188 | methodVisitor.visitLineNumber(58, label8);
189 | methodVisitor.visitVarInsn(ILOAD, 5);
190 | methodVisitor.visitInsn(ICONST_2);
191 | methodVisitor.visitInsn(IMUL);
192 | methodVisitor.visitVarInsn(ISTORE, 6);
193 | Label label9 = new Label();
194 | methodVisitor.visitLabel(label9);
195 | methodVisitor.visitLineNumber(59, label9);
196 | methodVisitor.visitVarInsn(ALOAD, 4);
197 | methodVisitor.visitVarInsn(ILOAD, 5);
198 | methodVisitor.visitLdcInsn("0123456789abcdef");
199 | methodVisitor.visitVarInsn(ALOAD, 3);
200 | methodVisitor.visitVarInsn(ILOAD, 6);
201 | methodVisitor.visitInsn(CALOAD);
202 | methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "indexOf", "(I)I", false);
203 | methodVisitor.visitInsn(ICONST_4);
204 | methodVisitor.visitInsn(ISHL);
205 | methodVisitor.visitLdcInsn("0123456789abcdef");
206 | methodVisitor.visitVarInsn(ALOAD, 3);
207 | methodVisitor.visitVarInsn(ILOAD, 6);
208 | methodVisitor.visitInsn(ICONST_1);
209 | methodVisitor.visitInsn(IADD);
210 | methodVisitor.visitInsn(CALOAD);
211 | methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "indexOf", "(I)I", false);
212 | methodVisitor.visitInsn(IOR);
213 | methodVisitor.visitInsn(I2B);
214 | methodVisitor.visitInsn(BASTORE);
215 | Label label10 = new Label();
216 | methodVisitor.visitLabel(label10);
217 | methodVisitor.visitLineNumber(57, label10);
218 | methodVisitor.visitIincInsn(5, 1);
219 | methodVisitor.visitJumpInsn(GOTO, label6);
220 | methodVisitor.visitLabel(label7);
221 | methodVisitor.visitLineNumber(61, label7);
222 | methodVisitor.visitFrame(Opcodes.F_CHOP, 1, null, 0, null);
223 | methodVisitor.visitVarInsn(ILOAD, 1);
224 | methodVisitor.visitIntInsn(SIPUSH, key2);
225 | methodVisitor.visitInsn(IXOR);
226 | methodVisitor.visitInsn(I2B);
227 | methodVisitor.visitVarInsn(ISTORE, 5);
228 | Label label11 = new Label();
229 | methodVisitor.visitLabel(label11);
230 | methodVisitor.visitLineNumber(63, label11);
231 | methodVisitor.visitVarInsn(ALOAD, 4);
232 | methodVisitor.visitInsn(ICONST_0);
233 | methodVisitor.visitVarInsn(ALOAD, 4);
234 | methodVisitor.visitInsn(ICONST_0);
235 | methodVisitor.visitInsn(BALOAD);
236 | methodVisitor.visitIntInsn(SIPUSH, key3);
237 | methodVisitor.visitInsn(IXOR);
238 | methodVisitor.visitInsn(I2B);
239 | methodVisitor.visitInsn(BASTORE);
240 | Label label12 = new Label();
241 | methodVisitor.visitLabel(label12);
242 | methodVisitor.visitLineNumber(64, label12);
243 | methodVisitor.visitVarInsn(ALOAD, 4);
244 | methodVisitor.visitInsn(ICONST_0);
245 | methodVisitor.visitInsn(BALOAD);
246 | methodVisitor.visitVarInsn(ISTORE, 6);
247 | Label label13 = new Label();
248 | methodVisitor.visitLabel(label13);
249 | methodVisitor.visitLineNumber(65, label13);
250 | methodVisitor.visitInsn(ICONST_1);
251 | methodVisitor.visitVarInsn(ISTORE, 7);
252 | Label label14 = new Label();
253 | methodVisitor.visitLabel(label14);
254 | methodVisitor.visitFrame(Opcodes.F_APPEND, 3, new Object[]{Opcodes.INTEGER, Opcodes.INTEGER, Opcodes.INTEGER}, 0, null);
255 | methodVisitor.visitVarInsn(ILOAD, 7);
256 | methodVisitor.visitVarInsn(ALOAD, 4);
257 | methodVisitor.visitInsn(ARRAYLENGTH);
258 | Label label15 = new Label();
259 | methodVisitor.visitJumpInsn(IF_ICMPGE, label15);
260 | Label label16 = new Label();
261 | methodVisitor.visitLabel(label16);
262 | methodVisitor.visitLineNumber(66, label16);
263 | methodVisitor.visitVarInsn(ALOAD, 4);
264 | methodVisitor.visitVarInsn(ILOAD, 7);
265 | methodVisitor.visitVarInsn(ALOAD, 4);
266 | methodVisitor.visitVarInsn(ILOAD, 7);
267 | methodVisitor.visitInsn(BALOAD);
268 | methodVisitor.visitVarInsn(ILOAD, 6);
269 | methodVisitor.visitInsn(IXOR);
270 | methodVisitor.visitVarInsn(ILOAD, 5);
271 | methodVisitor.visitInsn(IXOR);
272 | methodVisitor.visitInsn(I2B);
273 | methodVisitor.visitInsn(BASTORE);
274 | Label label17 = new Label();
275 | methodVisitor.visitLabel(label17);
276 | methodVisitor.visitLineNumber(67, label17);
277 | methodVisitor.visitVarInsn(ALOAD, 4);
278 | methodVisitor.visitVarInsn(ILOAD, 7);
279 | methodVisitor.visitInsn(BALOAD);
280 | methodVisitor.visitVarInsn(ISTORE, 6);
281 | Label label18 = new Label();
282 | methodVisitor.visitLabel(label18);
283 | methodVisitor.visitLineNumber(65, label18);
284 | methodVisitor.visitIincInsn(7, 1);
285 | methodVisitor.visitJumpInsn(GOTO, label14);
286 | methodVisitor.visitLabel(label15);
287 | methodVisitor.visitLineNumber(70, label15);
288 | methodVisitor.visitFrame(Opcodes.F_CHOP, 1, null, 0, null);
289 | methodVisitor.visitTypeInsn(NEW, "java/lang/String");
290 | methodVisitor.visitInsn(DUP);
291 | methodVisitor.visitVarInsn(ALOAD, 4);
292 | methodVisitor.visitLdcInsn("UTF-8");
293 | methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/String", "", "([BLjava/lang/String;)V", false);
294 | methodVisitor.visitLabel(label1);
295 | methodVisitor.visitInsn(ARETURN);
296 | methodVisitor.visitLabel(label2);
297 | methodVisitor.visitLineNumber(71, label2);
298 | methodVisitor.visitFrame(Opcodes.F_FULL, 2, new Object[]{"java/lang/String", Opcodes.INTEGER}, 1, new Object[]{"java/lang/Exception"});
299 | methodVisitor.visitVarInsn(ASTORE, 2);
300 | Label label19 = new Label();
301 | methodVisitor.visitLabel(label19);
302 | methodVisitor.visitLineNumber(72, label19);
303 | methodVisitor.visitVarInsn(ALOAD, 2);
304 | methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Exception", "printStackTrace", "()V", false);
305 | Label label20 = new Label();
306 | methodVisitor.visitLabel(label20);
307 | methodVisitor.visitLineNumber(74, label20);
308 | methodVisitor.visitLdcInsn("");
309 | methodVisitor.visitInsn(ARETURN);
310 | Label label21 = new Label();
311 | methodVisitor.visitLabel(label21);
312 | methodVisitor.visitLocalVariable("pos", "I", null, label9, label10, 6);
313 | methodVisitor.visitLocalVariable("i", "I", null, label6, label7, 5);
314 | methodVisitor.visitLocalVariable("i", "I", null, label14, label15, 7);
315 | methodVisitor.visitLocalVariable("length", "I", null, label3, label2, 2);
316 | methodVisitor.visitLocalVariable("hexChars", "[C", null, label4, label2, 3);
317 | methodVisitor.visitLocalVariable("stringByte", "[B", null, label5, label2, 4);
318 | methodVisitor.visitLocalVariable("keyXor", "B", null, label11, label2, 5);
319 | methodVisitor.visitLocalVariable("temp", "B", null, label13, label2, 6);
320 | methodVisitor.visitLocalVariable("e", "Ljava/lang/Exception;", null, label19, label20, 2);
321 | methodVisitor.visitLocalVariable("ciphertext", "Ljava/lang/String;", null, label0, label21, 0);
322 | methodVisitor.visitLocalVariable("key1", "I", null, label0, label21, 1);
323 | methodVisitor.visitMaxs(7, 8);
324 | methodVisitor.visitEnd();
325 | }
326 |
327 | }
328 |
--------------------------------------------------------------------------------
/string_obfuscated/src/main/java/com/wzc/gradle/plugin/utils/StringEncryptionTestUtil.java:
--------------------------------------------------------------------------------
1 | package com.wzc.gradle.plugin.utils;
2 |
3 |
4 | import java.nio.charset.StandardCharsets;
5 | import java.util.Arrays;
6 | import java.util.Random;
7 |
8 | public class StringEncryptionTestUtil {
9 |
10 |
11 | private static int getRandomKey() {
12 | Random rand = new Random();
13 | return rand.nextInt(127);
14 | }
15 |
16 | public static void main(String[] args) {
17 | int key1 = getRandomKey();
18 | int key2 = getRandomKey();
19 | int key3 = getRandomKey();
20 | int key4 = getRandomKey();
21 | // String s0 = encryption("", key1, key2, key3);
22 | // String s1 = encryption("wzc@123哈哈哈", key1, key2, key3);
23 | // String s2 = encryption("english\n中文@#¥……%……&*()——+!@#$%^&*()123哈哈哈", key4, key2, key3);
24 | // System.out.println("加密后:s1=" + s1 + " s2=" + s2);
25 | // String d1 = decrypt(s1, key1, key2, key3);
26 | // String d2 = decrypt("aa1974290d703b165f013a5b", 11, 1, 76);
27 | // System.out.println("解密后:d1=" + d1 + " d2=" + d2);
28 | }
29 |
30 |
31 | public static String decrypt(String ciphertext, int key1, int key2, int key3) {
32 |
33 | try {
34 | int length = ciphertext.length() / 2;
35 | char[] hexChars = ciphertext.toCharArray();
36 | byte[] stringByte = new byte[length];
37 | for (int i = 0; i < length; i++) {
38 | int pos = i * 2;
39 | stringByte[i] = (byte) ("0123456789abcdef".indexOf(hexChars[pos]) << 4 | "0123456789abcdef".indexOf(hexChars[pos + 1]));
40 | }
41 | byte keyXor = (byte) (key1 ^ key2);
42 | System.out.println("解密前:" + Arrays.toString(stringByte));
43 | stringByte[0] = (byte) (stringByte[0] ^ key3);
44 | byte temp = stringByte[0]; //解密之后的数据
45 | for (int i = 1; i < stringByte.length; i++) {
46 | stringByte[i] = (byte) (stringByte[i] ^ temp ^ keyXor);
47 | temp = stringByte[i];
48 | }
49 | System.out.println("解密后:" + Arrays.toString(stringByte));
50 | return new String(stringByte, "UTF-8");
51 | } catch (Exception e) {
52 | e.printStackTrace();
53 | }
54 | return "";
55 | }
56 |
57 | /**
58 | * 字符串加密
59 | * String -> byte
60 | * 按byte加密,加密之后参与下一个字节的加密
61 | *
62 | * @param string 原始字符串
63 | * @param key1 key1
64 | * @param key2 key2
65 | * @param key3 key3
66 | * @return 加密后的字符串
67 | */
68 | public static String encryption(String string, int key1, int key2, int key3) {
69 | byte[] stringByte = string.getBytes(StandardCharsets.UTF_8);
70 | // System.out.println("string加密前:" + Arrays.toString(stringByte));
71 | byte keyXor = (byte) (key1 ^ key2);
72 | StringBuilder result = new StringBuilder();
73 | byte c = (byte) (stringByte[0] ^ key3);
74 | result.append(String.format("%02x", c));
75 | byte key4 = stringByte[0];
76 | for (int i = 1; i < stringByte.length; i++) {
77 | byte temp = stringByte[i];
78 | c = (byte) (stringByte[i] ^ key4 ^ keyXor);
79 | key4 = temp;
80 | // System.out.println("string[" + i + "]加密后:" + c + " 转16进制" + String.format("%02x", c));
81 | result.append(String.format("%02x", c));
82 | }
83 | return result.toString();
84 | }
85 |
86 | }
87 |
--------------------------------------------------------------------------------
/string_obfuscated/src/main/kotlin/com/wzc/gradle/plugin/PluginLaunch.kt:
--------------------------------------------------------------------------------
1 | package com.wzc.gradle.plugin
2 |
3 | import com.android.build.api.instrumentation.*
4 | import com.android.build.api.variant.AndroidComponentsExtension
5 | import com.android.build.gradle.AppPlugin
6 | import com.android.build.gradle.LibraryPlugin
7 | import com.wzc.gradle.plugin.model.StringObfuscateExtensions
8 | import com.wzc.gradle.plugin.utils.Logger
9 | import org.gradle.api.Plugin
10 | import org.gradle.api.Project
11 | import org.gradle.api.tasks.Internal
12 | import org.objectweb.asm.ClassVisitor
13 | import org.objectweb.asm.Opcodes
14 |
15 | class PluginLaunch : Plugin {
16 |
17 | private val EXTENSIONS_NAME = "stringObfuscate"
18 | override fun apply(project: Project) {
19 | Logger.d("###############################################")
20 | Logger.d("######### 欢迎使用字符串混淆插件!!! #########")
21 | Logger.d("###############################################")
22 | val app = project.plugins.hasPlugin(AppPlugin::class.java)
23 | val library = project.plugins.hasPlugin(LibraryPlugin::class.java)
24 | project.extensions.create(EXTENSIONS_NAME, StringObfuscateExtensions::class.java)
25 | val androidComponentsExtension =
26 | project.extensions.getByType(AndroidComponentsExtension::class.java)
27 | androidComponentsExtension.onVariants { variant ->
28 | val extensions =
29 | project.extensions.getByName(EXTENSIONS_NAME) as StringObfuscateExtensions
30 | Logger.openLog = extensions.openLog
31 | variant.instrumentation.apply {
32 | if (app) {
33 | // ALL检测当前项目的类及其库依赖项。
34 | transformClassesWith(
35 | ObfuscateAsmClassVisitorFactory::class.java, InstrumentationScope.ALL
36 | ) { params ->
37 | // parameters configuration
38 | params.packageList = extensions.packageName
39 | }
40 | } else if (library) {
41 | Logger.d("请在app module中使用插件!!!")
42 | }
43 | setAsmFramesComputationMode(FramesComputationMode.COPY_FRAMES)
44 | }
45 | }
46 | }
47 |
48 | interface ParametersImpl : InstrumentationParameters {
49 | @get:Internal
50 | var packageList: List
51 | }
52 |
53 | abstract class ObfuscateAsmClassVisitorFactory : AsmClassVisitorFactory {
54 |
55 | override fun createClassVisitor(
56 | classContext: ClassContext,
57 | nextClassVisitor: ClassVisitor
58 | ): ClassVisitor {
59 | return ScanClassVisitor(Opcodes.ASM9, nextClassVisitor)
60 | }
61 |
62 | override fun isInstrumentable(classData: ClassData): Boolean {
63 | val packageList = parameters.get().packageList
64 | for (packageName in packageList) {
65 | if (classData.className.startsWith(packageName)) {
66 | return true
67 | }
68 | }
69 | return false
70 | }
71 | }
72 | }
--------------------------------------------------------------------------------
/string_obfuscated/src/main/kotlin/com/wzc/gradle/plugin/model/StringObfuscateExtensions.kt:
--------------------------------------------------------------------------------
1 | package com.wzc.gradle.plugin.model
2 |
3 | open class StringObfuscateExtensions {
4 |
5 | var packageName: List = ArrayList()
6 | var openLog: Boolean = true
7 | }
8 |
--------------------------------------------------------------------------------
/string_obfuscated/src/main/kotlin/com/wzc/gradle/plugin/utils/Logger.kt:
--------------------------------------------------------------------------------
1 | package com.wzc.gradle.plugin.utils
2 |
3 | object Logger {
4 |
5 | var openLog = true
6 |
7 | fun d(message: String) {
8 | if (openLog)
9 | println("######### $message")
10 | }
11 | }
--------------------------------------------------------------------------------
/test_library/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/test_library/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.library'
3 | }
4 |
5 | android {
6 | compileSdkVersion 32
7 |
8 | defaultConfig {
9 | minSdkVersion 21
10 | targetSdkVersion 32
11 |
12 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
13 | consumerProguardFiles "consumer-rules.pro"
14 | }
15 |
16 | buildTypes {
17 | release {
18 | minifyEnabled false
19 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
20 | }
21 | }
22 | }
23 |
24 | dependencies {
25 |
26 | implementation 'androidx.appcompat:appcompat:1.4.1'
27 | implementation 'com.google.android.material:material:1.5.0'
28 | testImplementation 'junit:junit:4.13.2'
29 | androidTestImplementation 'androidx.test.ext:junit:1.1.3'
30 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
31 | }
--------------------------------------------------------------------------------
/test_library/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuzuchang/StringObfuscatedPlugin/1aa1b3ea2bdfcc4011617775708ef5be2ba69fb2/test_library/consumer-rules.pro
--------------------------------------------------------------------------------
/test_library/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/test_library/src/androidTest/java/com/wzc/test_library/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.wzc.test_library;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.test.platform.app.InstrumentationRegistry;
6 | import androidx.test.ext.junit.runners.AndroidJUnit4;
7 |
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 |
11 | import static org.junit.Assert.*;
12 |
13 | /**
14 | * Instrumented test, which will execute on an Android device.
15 | *
16 | * @see Testing documentation
17 | */
18 | @RunWith(AndroidJUnit4.class)
19 | public class ExampleInstrumentedTest {
20 | @Test
21 | public void useAppContext() {
22 | // Context of the app under test.
23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
24 | assertEquals("com.wzc.test_library.test", appContext.getPackageName());
25 | }
26 | }
--------------------------------------------------------------------------------
/test_library/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/test_library/src/main/java/com/wzc/test/Test1.java:
--------------------------------------------------------------------------------
1 | package com.wzc.test;
2 |
3 | public class Test1 {
4 | private String string1 = "测试中文";
5 | private final String string2 = "test English";
6 | private static String string3 = "测试特殊字符@!#¥%……&*()~+——()_="; //会生成
7 | private static final String string4 = "测试换行\n换行了";
8 | private static final String string6 = "测试\r\n换行了";
9 | private static final String string5;
10 |
11 | static {
12 | string5 = "测试静态代码块"; //会生成
13 | }
14 |
15 | @Override
16 | public String toString() {
17 | return "Test1{" + "string1='" + string1 + '\'' + ", string2='" + string2 + '\'' + ", string3='" + string3 + '\'' + ", string4='" + string4 + '\'' + ", string5='" + string5 + '\'' + ", string6='" + string6 + '\'' + '}';
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/test_library/src/main/java/com/wzc/test_library/Test2.java:
--------------------------------------------------------------------------------
1 | package com.wzc.test_library;
2 |
3 | public class Test2 {
4 | private String string1 = "测试中文";
5 | private final String string2 = "test English";
6 | private static String string3 = "测试特殊字符@!#¥%……&*()~+——()_="; //会生成
7 | private static final String string4 = "测试换行\n换行了";
8 | private static final String string6 = "测试\r\n换行了";
9 | private static final String string5;
10 |
11 | static {
12 | string5 = "测试静态代码块"; //会生成
13 | }
14 | @Override
15 | public String toString() {
16 | return "Test1{" + "string1='" + string1 + '\'' + ", string2='" + string2 + '\'' + ", string3='" + string3 + '\'' + ", string4='" + string4 + '\'' + ", string5='" + string5 + '\'' + ", string6='" + string6 + '\'' + '}';
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/test_library/src/test/java/com/wzc/test_library/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.wzc.test_library;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------