├── README.md
├── app
├── build.gradle
├── package.gradle
├── proguard-rules.pro
├── src
│ ├── androidTest
│ │ └── java
│ │ │ └── com
│ │ │ └── wustor
│ │ │ └── gradlemodule
│ │ │ └── ExampleInstrumentedTest.java
│ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── wustor
│ │ │ │ └── gradlemodule
│ │ │ │ └── MainActivity.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
│ │ │ ├── colors.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ └── test
│ │ └── java
│ │ └── com
│ │ └── wustor
│ │ └── gradlemodule
│ │ └── ExampleUnitTest.java
└── tinker.gradle
├── build.gradle
├── config.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── keystore.properties
├── settings.gradle
├── upToFir.py
└── upload.gradle
/README.md:
--------------------------------------------------------------------------------
1 |
2 | ## exclude配置
3 |
4 | 其实我的博客里面忽略了一种情况,就是dependencies中有需要exclude的情况,多谢[Vanish](https://github.com/Vanish136)提出了这个问题,如果单纯的去遍历一个map是不可以的,比如说下面Glide的配置情况
5 |
6 |
7 |
8 | compile("com.github.bumptech.glide:glide:4.3.1") {
9 | exclude(group: 'com.android.support', module: 'support-v4')
10 | exclude(group: 'com.android.support', module: 'appcompat-v7')
11 | exclude(group: 'com.squareup.okhttp3', module: 'okhttp3')
12 | }
13 |
14 | 对于这种情况的话,需要额外定义一个关于exclude的map,因为可能多个依赖需要exclude,下面就用Glide举个栗子:
15 |
16 | ### config.gradle
17 |
18 | map的key是compile的依赖,然后value是一个数组,因为有可能需要剔除多个重复依赖,所以用数组来表示
19 |
20 | excludes = ["com.github.bumptech.glide:glide:4.3.1":
21 | [
22 | 'com.android.support' : 'support-v4',
23 | 'com.android.support' : 'appcompat-v7',
24 | 'com.squareup.okhttp3': 'okhttp3']]
25 |
26 | ### build.gradle
27 |
28 | ```java
29 | excludes.each { entry ->
30 | compile(entry.key) {
31 | entry.value.each { childEntry ->
32 | exclude(group: childEntry.key, module: childEntry.value)
33 | }
34 | }
35 | }
36 | ```
37 |
38 | 然后在build.gradle中拿到这个map,然后进行遍历就好,代码已上传,有需要的可以下载进行查看。
39 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply from: "package.gradle"
3 |
4 | def cfg = rootProject.ext.android
5 | def librarys = rootProject.ext.dependencies
6 | def excludes = rootProject.ext.excludes
7 | def tinker = rootProject.ext.tinker
8 | def url = rootProject.ext.url
9 | def startUpload = rootProject.ext.startUpload
10 | def keystoreProperties = new Properties()
11 | def keystorePropertiesFile = rootProject.file("keystore.properties")
12 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
13 | android {
14 | signingConfigs {
15 | config {
16 | keyAlias keystoreProperties['keyAlias']
17 | keyPassword keystoreProperties['keyPassword']
18 | storeFile file(keystoreProperties['storeFile'])
19 | storePassword keystoreProperties['storePassword']
20 | }
21 | }
22 |
23 | compileSdkVersion cfg.compileSdkVersion
24 | buildToolsVersion cfg.buildToolsVersion
25 | dexOptions {
26 | jumboMode = true
27 | }
28 |
29 | defaultConfig {
30 | ndk { abiFilters "armeabi", "armeabi-v7a", "x86", "mips" }
31 | applicationId cfg.applicationId
32 | minSdkVersion cfg.minSdkVersion
33 | targetSdkVersion cfg.targetSdkVersion
34 | versionCode cfg.versionCode//更新次数
35 | versionName cfg.versionName//版本号
36 | // manifestPlaceholders = [UMENG_CHANNEL_VALUE: "umeng"]
37 | //Tinker配置信息
38 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
39 | multiDexEnabled true
40 | buildConfigField "String", "MESSAGE", "\"${tinker.message}\""
41 | buildConfigField "String", "TINKER_ID", "\"${tinker.id}\""
42 | buildConfigField "String", "PLATFORM", "\"${tinker.plateform}\""
43 | //Tinker相关配置end======================================
44 | }
45 | lintOptions {
46 | checkReleaseBuilds false
47 | abortOnError false
48 | }
49 | configurations.all {
50 | resolutionStrategy.force 'com.google.code.findbugs:jsr305:1.3.9'
51 | }
52 | buildTypes {
53 | release {
54 | minifyEnabled true
55 | shrinkResources true
56 | buildConfigField "String", "AlphaUrl", "\"${url["release"]}\""
57 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
58 | signingConfig signingConfigs.config
59 | }
60 | debug {
61 | minifyEnabled true
62 | shrinkResources true
63 | debuggable true
64 | buildConfigField "String", "AlphaUrl", "\"${url["release"]}\""
65 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
66 | signingConfig signingConfigs.config
67 | }
68 | }
69 | }
70 | apply from: "tinker.gradle"
71 | dependencies {
72 | compile fileTree(include: ['*.jar'], dir: 'libs')
73 | librarys.each { k, v -> compile v }
74 | excludes.each { entry ->
75 | compile(entry.key) {
76 | entry.value.each { childEntry ->
77 | exclude(group: childEntry.key, module: childEntry.value)
78 | }
79 | }
80 |
81 | }
82 | }
83 |
84 | task toFir << {
85 | startUpload()
86 | }
87 |
--------------------------------------------------------------------------------
/app/package.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'packer'
2 | packer {
3 | archiveNameFormat = '${buildType}-v${versionName}-${channel}'
4 | archiveOutput = new File(project.rootProject.buildDir, "apks")
5 | channelList = ['xiaomi','meizu']
6 | // channelFile = new File(project.rootDir, "markets.txt")
7 | // channelMap = [
8 | // "Cat" : project.rootProject.file("channels/cat.txt"),
9 | // "Dog" : project.rootProject.file("channels/dog.txt"),
10 | // "Fish": project.rootProject.file("channels/channels.txt")
11 | // ]
12 | }
--------------------------------------------------------------------------------
/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
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/wustor/gradlemodule/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.wustor.gradlemodule;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.wustor.gradlemodule", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/java/com/wustor/gradlemodule/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.wustor.gradlemodule;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 |
6 | public class MainActivity extends AppCompatActivity {
7 |
8 | @Override
9 | protected void onCreate(Bundle savedInstanceState) {
10 | super.onCreate(savedInstanceState);
11 | setContentView(R.layout.activity_main);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
11 |
16 |
21 |
26 |
31 |
36 |
41 |
46 |
51 |
56 |
61 |
66 |
71 |
76 |
81 |
86 |
91 |
96 |
101 |
106 |
111 |
116 |
121 |
126 |
131 |
136 |
141 |
146 |
151 |
156 |
161 |
166 |
171 |
172 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/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/wustor/GradleModule/928679e32c7a439f9e0f0663dc4ff033752e9182/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wustor/GradleModule/928679e32c7a439f9e0f0663dc4ff033752e9182/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wustor/GradleModule/928679e32c7a439f9e0f0663dc4ff033752e9182/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wustor/GradleModule/928679e32c7a439f9e0f0663dc4ff033752e9182/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wustor/GradleModule/928679e32c7a439f9e0f0663dc4ff033752e9182/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wustor/GradleModule/928679e32c7a439f9e0f0663dc4ff033752e9182/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wustor/GradleModule/928679e32c7a439f9e0f0663dc4ff033752e9182/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wustor/GradleModule/928679e32c7a439f9e0f0663dc4ff033752e9182/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wustor/GradleModule/928679e32c7a439f9e0f0663dc4ff033752e9182/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wustor/GradleModule/928679e32c7a439f9e0f0663dc4ff033752e9182/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | GradleModule
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/wustor/gradlemodule/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.wustor.gradlemodule;
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() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/app/tinker.gradle:
--------------------------------------------------------------------------------
1 |
2 | def bakPath = file("${buildDir}/bakApk/")
3 | ext {
4 | //是否打开tinker的功能
5 | tinkerEnabled = true
6 | //old apk地址
7 | tinkerOldApkPath = "${bakPath}/app-debug.apk"
8 | //old apk 混淆文件地址
9 | tinkerApplyMappingPath = "${bakPath}/app-debug-mapping.txt"
10 | //old apk R 文件地址
11 | tinkerApplyResourcePath = "${bakPath}/app-debug-R.txt"
12 |
13 | //only use for build all flavor, if not, just ignore this field
14 | tinkerBuildFlavorDirectory = "${buildDir}"
15 | }
16 |
17 | /**
18 | * you can use assembleRelease to build you base apk
19 | * use tinkerPatchRelease -POLD_APK= -PAPPLY_MAPPING= -PAPPLY_RESOURCE= to build patch
20 | * add apk from the build/bakApk
21 | */
22 |
23 |
24 | def getOldApkPath() {
25 | return hasProperty("OLD_APK") ? OLD_APK : ext.tinkerOldApkPath
26 | }
27 |
28 | def getApplyMappingPath() {
29 | return hasProperty("APPLY_MAPPING") ? APPLY_MAPPING : ext.tinkerApplyMappingPath
30 | }
31 |
32 | def getApplyResourceMappingPath() {
33 | return hasProperty("APPLY_RESOURCE") ? APPLY_RESOURCE : ext.tinkerApplyResourcePath
34 | }
35 |
36 | def getTinkerIdValue() {
37 | return rootProject.ext.tinker.id
38 |
39 | }
40 |
41 | def buildWithTinker() {
42 | return hasProperty("TINKER_ENABLE") ? TINKER_ENABLE : ext.tinkerEnabled
43 | }
44 |
45 | def getTinkerBuildFlavorDirectory() {
46 | return ext.tinkerBuildFlavorDirectory
47 | }
48 |
49 | if (buildWithTinker()) {
50 | apply plugin: 'com.tencent.tinker.patch'
51 |
52 | tinkerPatch {
53 | /**
54 | * necessary,default 'null'
55 | * the old apk path, use to diff with the new apk to build
56 | * add apk from the build/bakApk
57 | */
58 | oldApk = getOldApkPath()
59 | /**
60 | * optional,default 'false'
61 | * there are some cases we may get some warnings
62 | * if ignoreWarning is true, we would just assert the patch process
63 | * case 1: minSdkVersion is below 14, but you are using dexMode with raw.
64 | * it must be crash when load.
65 | * case 2: newly added Android Component in AndroidManifest.xml,
66 | * it must be crash when load.
67 | * case 3: loader classes in dex.loader{} are not keep in the main dex,
68 | * it must be let tinker not work.
69 | * case 4: loader classes in dex.loader{} changes,
70 | * loader classes is ues to load patch dex. it is useless to change them.
71 | * it won't crash, but these changes can't effect. you may ignore it
72 | * case 5: resources.arsc has changed, but we don't use applyResourceMapping to build
73 | */
74 | ignoreWarning = false
75 |
76 | /**
77 | * optional,default 'true'
78 | * whether sign the patch file
79 | * if not, you must do yourself. otherwise it can't check success during the patch loading
80 | * we will use the sign config with your build type
81 | */
82 | useSign = true
83 |
84 | /**
85 | * optional,default 'true'
86 | * whether use tinker to build
87 | */
88 | tinkerEnable = buildWithTinker()
89 |
90 | /**
91 | * Warning, applyMapping will affect the normal android build!
92 | */
93 | buildConfig {
94 | /**
95 | * optional,default 'null'
96 | * if we use tinkerPatch to build the patch apk, you'd better to apply the old
97 | * apk mapping file if minifyEnabled is enable!
98 | * Warning:
99 | * you must be careful that it will affect the normal assemble build!
100 | */
101 | applyMapping = getApplyMappingPath()
102 | /**
103 | * optional,default 'null'
104 | * It is nice to keep the resource id from R.txt file to reduce java changes
105 | */
106 | applyResourceMapping = getApplyResourceMappingPath()
107 |
108 | /**
109 | * necessary,default 'null'
110 | * because we don't want to check the base apk with md5 in the runtime(it is slow)
111 | * tinkerId is use to identify the unique base apk when the patch is tried to apply.
112 | * we can use git rev, svn rev or simply versionCode.
113 | * we will gen the tinkerId in your manifest automatic
114 | */
115 | tinkerId = getTinkerIdValue()
116 |
117 | /**
118 | * if keepDexApply is true, class in which dex refer to the old apk.
119 | * open this can reduce the dex diff file size.
120 | */
121 | keepDexApply = false
122 |
123 | //是否开启加固
124 | isProtectedApp = false
125 | }
126 |
127 | dex {
128 | /**
129 | * optional,default 'jar'
130 | * only can be 'raw' or 'jar'. for raw, we would keep its original format
131 | * for jar, we would repack dexes with zip format.
132 | * if you want to support below 14, you must use jar
133 | * or you want to save rom or check quicker, you can use raw mode also
134 | */
135 | dexMode = "jar"
136 |
137 | /**
138 | * necessary,default '[]'
139 | * what dexes in apk are expected to deal with tinkerPatch
140 | * it support * or ? pattern.
141 | */
142 | pattern = ["classes*.dex",
143 | "assets/secondary-dex-?.jar"]
144 | /**
145 | * necessary,default '[]'
146 | * Warning, it is very very important, loader classes can't change with patch.
147 | * thus, they will be removed from patch dexes.
148 | * you must put the following class into main dex.
149 | * Simply, you should add your own application {@code tinker.sample.android.SampleApplication}
150 | * own tinkerLoader, and the classes you use in them
151 | *
152 | */
153 | loader = [
154 | //use sample, let BaseBuildInfo unchangeable with tinker
155 | "tinker.sample.android.app.BaseBuildInfo"
156 | ]
157 | }
158 |
159 | lib {
160 | /**
161 | * optional,default '[]'
162 | * what library in apk are expected to deal with tinkerPatch
163 | * it support * or ? pattern.
164 | * for library in assets, we would just recover them in the patch directory
165 | * you can get them in TinkerLoadResult with Tinker
166 | */
167 | pattern = ["lib/*/*.so"]
168 | }
169 |
170 | res {
171 | /**
172 | * optional,default '[]'
173 | * what resource in apk are expected to deal with tinkerPatch
174 | * it support * or ? pattern.
175 | * you must include all your resources in apk here,
176 | * otherwise, they won't repack in the new apk resources.
177 | */
178 | pattern = ["res/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]
179 |
180 | /**
181 | * optional,default '[]'
182 | * the resource file exclude patterns, ignore add, delete or modify resource change
183 | * it support * or ? pattern.
184 | * Warning, we can only use for files no relative with resources.arsc
185 | */
186 | ignoreChange = ["assets/sample_meta.txt"]
187 |
188 | /**
189 | * default 100kb
190 | * for modify resource, if it is larger than 'largeModSize'
191 | * we would like to use bsdiff algorithm to reduce patch file size
192 | */
193 | largeModSize = 100
194 | }
195 |
196 | packageConfig {
197 | /**
198 | * optional,default 'TINKER_ID, TINKER_ID_VALUE' 'NEW_TINKER_ID, NEW_TINKER_ID_VALUE'
199 | * package meta file gen. path is assets/package_meta.txt in patch file
200 | * you can use securityCheck.getPackageProperties() in your ownPackageCheck method
201 | * or TinkerLoadResult.getPackageConfigByName
202 | * we will get the TINKER_ID from the old apk manifest for you automatic,
203 | * other config files (such as patchMessage below)is not necessary
204 | */
205 | configField("patchMessage", "tinker is sample to use")
206 | /**
207 | * just a sample case, you can use such as sdkVersion, brand, channel...
208 | * you can parse it in the SamplePatchListener.
209 | * Then you can use patch conditional!
210 | */
211 | configField("platform", "all")
212 | /**
213 | * patch version via packageConfig
214 | */
215 | configField("patchVersion", android.defaultConfig.versionName)
216 | }
217 | //or you can add config filed outside, or get meta value from old apk
218 | //project.tinkerPatch.packageConfig.configField("test1", project.tinkerPatch.packageConfig.getMetaDataFromOldApk("Test"))
219 | //project.tinkerPatch.packageConfig.configField("test2", "sample")
220 |
221 | /**
222 | * if you don't use zipArtifact or path, we just use 7za to try
223 | */
224 | sevenZip {
225 | /**
226 | * optional,default '7za'
227 | * the 7zip artifact path, it will use the right 7za with your platform
228 | */
229 | zipArtifact = "com.tencent.mm:SevenZip:1.1.10"
230 | /**
231 | * optional,default '7za'
232 | * you can specify the 7za path yourself, it will overwrite the zipArtifact value
233 | */
234 | // path = "/usr/local/bin/7za"
235 | }
236 | }
237 |
238 | List flavors = new ArrayList<>();
239 | project.android.productFlavors.each { flavor ->
240 | flavors.add(flavor.name)
241 | }
242 | boolean hasFlavors = flavors.size() > 0
243 | def date = new Date().format("MMdd-HH-mm-ss")
244 |
245 | /**
246 | * bak apk and mapping
247 | */
248 | android.applicationVariants.all { variant ->
249 | /**
250 | * task type, you want to bak
251 | */
252 | def taskName = variant.name
253 |
254 | tasks.all {
255 | if ("assemble${taskName.capitalize()}".equalsIgnoreCase(it.name)) {
256 |
257 | it.doLast {
258 | copy {
259 | def fileNamePrefix = "${project.name}-${variant.baseName}"
260 | def newFileNamePrefix = hasFlavors ? "${fileNamePrefix}" : "${fileNamePrefix}"
261 |
262 | def destPath = hasFlavors ? file("${bakPath}/${project.name}/${variant.flavorName}") : bakPath
263 | from variant.outputs.outputFile
264 | into destPath
265 | rename { String fileName ->
266 | fileName.replace("${fileNamePrefix}.apk", "${newFileNamePrefix}.apk")
267 | }
268 |
269 | from "${buildDir}/outputs/mapping/${variant.dirName}/mapping.txt"
270 | into destPath
271 | rename { String fileName ->
272 | fileName.replace("mapping.txt", "${newFileNamePrefix}-mapping.txt")
273 | }
274 |
275 | from "${buildDir}/intermediates/symbols/${variant.dirName}/R.txt"
276 | into destPath
277 | rename { String fileName ->
278 | fileName.replace("R.txt", "${newFileNamePrefix}-R.txt")
279 | }
280 | }
281 | }
282 | }
283 | }
284 | }
285 | project.afterEvaluate {
286 | //sample use for build all flavor for one time
287 | if (hasFlavors) {
288 | task(tinkerPatchAllFlavorRelease) {
289 | group = 'tinker'
290 | def originOldPath = getTinkerBuildFlavorDirectory()
291 | for (String flavor : flavors) {
292 | def tinkerTask = tasks.getByName("tinkerPatch${flavor.capitalize()}Release")
293 | dependsOn tinkerTask
294 | def preAssembleTask = tasks.getByName("process${flavor.capitalize()}ReleaseManifest")
295 | preAssembleTask.doFirst {
296 | String flavorName = preAssembleTask.name.substring(7, 8).toLowerCase() + preAssembleTask.name.substring(8, preAssembleTask.name.length() - 15)
297 | project.tinkerPatch.oldApk = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-release.apk"
298 | project.tinkerPatch.buildConfig.applyMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-release-mapping.txt"
299 | project.tinkerPatch.buildConfig.applyResourceMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-release-R.txt"
300 |
301 | }
302 |
303 | }
304 | }
305 |
306 | task(tinkerPatchAllFlavorDebug) {
307 | group = 'tinker'
308 | def originOldPath = getTinkerBuildFlavorDirectory()
309 | for (String flavor : flavors) {
310 | def tinkerTask = tasks.getByName("tinkerPatch${flavor.capitalize()}Debug")
311 | dependsOn tinkerTask
312 | def preAssembleTask = tasks.getByName("process${flavor.capitalize()}DebugManifest")
313 | preAssembleTask.doFirst {
314 | String flavorName = preAssembleTask.name.substring(7, 8).toLowerCase() + preAssembleTask.name.substring(8, preAssembleTask.name.length() - 13)
315 | project.tinkerPatch.oldApk = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-debug.apk"
316 | project.tinkerPatch.buildConfig.applyMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-debug-mapping.txt"
317 | project.tinkerPatch.buildConfig.applyResourceMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-debug-R.txt"
318 | }
319 |
320 | }
321 | }
322 | }
323 | }
324 | }
325 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | apply from: "config.gradle"
3 | apply from: "upload.gradle"
4 | buildscript {
5 | repositories {
6 | mavenCentral()
7 | jcenter()
8 | google()
9 |
10 | maven {
11 | url 'https://maven.google.com/'
12 | name 'Google'
13 | }
14 | }
15 | dependencies {
16 | classpath 'com.android.tools.build:gradle:2.3.3'
17 | classpath 'com.mcxiaoke.packer-ng:plugin:2.0.0'
18 | classpath "com.tencent.tinker:tinker-patch-gradle-plugin:${TINKER_VERSION}"
19 |
20 | // NOTE: Do not place your application dependencies here; they belong
21 | // in the individual module build.gradle files
22 | }
23 | }
24 |
25 | allprojects {
26 | repositories {
27 | mavenCentral()
28 | jcenter()
29 | maven { url "https://jitpack.io" }
30 | google()
31 | maven {
32 | url 'https://maven.google.com/'
33 | name 'Google'
34 | }
35 | }
36 | }
37 |
38 | task clean(type: Delete) {
39 | delete rootProject.buildDir
40 | }
41 |
--------------------------------------------------------------------------------
/config.gradle:
--------------------------------------------------------------------------------
1 | /**
2 | * 在主项目的根目录下创建config.gradle文件
3 | * 在这里单独处理统一依赖问题
4 | * 注意需要在根目录的build.gradle中进行引入
5 | */
6 | ext {
7 | android = [
8 | compileSdkVersion: 25,
9 | buildToolsVersion: "25.0.3",
10 | minSdkVersion : 16,
11 | targetSdkVersion : 25,
12 | applicationId : "com.wustor.gradlemodule",
13 | versionCode : 9,
14 | versionName : "1.6.3"
15 |
16 | ]
17 | tinker = [
18 | id : android.versionName,
19 | message : "I am the base apk",
20 | plateform: "all",
21 | version : "1.9.1",
22 | ]
23 | url = [
24 | "debug" : "debugUrl1",
25 | //"debug" : "debugUrl2",
26 | //"debug" : "debugUrl3",
27 | "release": "releaseUrl1",
28 | // "release": "releaseUrl2"
29 |
30 |
31 | ]
32 |
33 | //Version
34 | supportLibrary = "25.4.0"
35 | tinkerVerison = "1.9.1"
36 | dependencies = [
37 | /*support库*/
38 | "multidex" : "com.android.support:multidex:1.0.1",
39 | "cardview" : "com.android.support:cardview-v7:${supportLibrary}",
40 | "constraint" : "com.android.support.constraint:constraint-layout:1.0.2",
41 | "recyclerview" : "com.android.support:recyclerview-v7:${supportLibrary}",
42 | "supportV4" : "com.android.support:support-v4:${supportLibrary}",
43 | "suppoutDesign" : "com.android.support:design:${supportLibrary}",
44 | "supportAppcompat": "com.android.support:appcompat-v7:${supportLibrary}",
45 | /*tinker*/
46 | "tinker-lib" : "com.tencent.tinker:tinker-android-lib:${tinkerVerison}",
47 | "tinker-anno" : "com.tencent.tinker:tinker-android-anno:${tinkerVerison}",
48 | "packer-ng" : "com.mcxiaoke.packer-ng:helper:2.0.0"
49 |
50 |
51 | ]
52 | excludes = ["com.github.bumptech.glide:glide:4.3.1":
53 | [
54 | 'com.android.support' : 'support-v4',
55 | 'com.android.support' : 'appcompat-v7',
56 | 'com.squareup.okhttp3': 'okhttp3']]
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 | TINKER_VERSION=1.8.1
19 | ICON_PATH=""
20 | APK_PATH=""
21 | CONTENT=""
22 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wustor/GradleModule/928679e32c7a439f9e0f0663dc4ff033752e9182/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Dec 03 11:52:45 CST 2017
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-4.1-all.zip
7 |
--------------------------------------------------------------------------------
/keystore.properties:
--------------------------------------------------------------------------------
1 | storePassword=""
2 | keyPassword=""
3 | keyAlias=""
4 | storeFile=""
5 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/upToFir.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # encoding = utf-8
3 | import requests
4 | import sys
5 |
6 |
7 | def upToFir():
8 | # 打印传递过来的参数数组长度,便于校验
9 | print(len(sys.argv))
10 | upUrl = sys.argv[1]
11 | appName = sys.argv[2]
12 | bundleId = sys.argv[3]
13 | verName = sys.argv[4]
14 | apiToken = sys.argv[5]
15 | print (apiToken)
16 | iconPath = sys.argv[6]
17 | apkPath = sys.argv[7]
18 | buildNumber = sys.argv[8]
19 | changeLog = sys.argv[9]
20 | queryData = {'type': 'android', 'bundle_id': bundleId, 'api_token': apiToken}
21 | iconDict = {}
22 | binaryDict = {}
23 | # 获取上传信息
24 | try:
25 | response = requests.post(url=upUrl, data=queryData)
26 | json = response.json()
27 | iconDict = (json["cert"]["icon"])
28 | binaryDict = (json["cert"]["binary"])
29 | except Exception as e:
30 | print('query:' + e)
31 |
32 | # 上传apk
33 | try:
34 | file = {'file': open(apkPath, 'rb')}
35 | param = {"key": binaryDict['key'],
36 | 'token': binaryDict['token'],
37 | "x:name": appName,
38 | "x:version": verName,
39 | "x:build": buildNumber,
40 | "x:changelog": changeLog.encode("utf-8")}
41 | req = requests.post(url=binaryDict['upload_url'], files=file, data=param, verify=False)
42 | print(req.status_code)
43 | except Exception as e:
44 | print('error_apk:' + e)
45 |
46 | # 上传logo
47 | try:
48 | file = {'file': open(iconPath, 'rb')}
49 | param = {"key": iconDict['key'],
50 | 'token': iconDict['token']}
51 | req = requests.post(url=iconDict['upload_url'], files=file, data=param, verify=False)
52 | print(req.status_code)
53 | except Exception as e:
54 | print('error_icon:' + e)
55 |
56 |
57 | if __name__ == '__main__':
58 | upToFir()
59 |
--------------------------------------------------------------------------------
/upload.gradle:
--------------------------------------------------------------------------------
1 | ext {
2 | upUrl = "http://api.fir.im/apps"
3 | appName = "创美e药"
4 | bundleId = rootProject.ext.android.applicationId
5 | verName = rootProject.ext.android.versionName
6 | apiToken = "5d30a26ef96ca348e58c1cef17fab7c8"
7 | iconPath = ICON_PATH
8 | apkPath = APK_PATH
9 | buildNumber = rootProject.ext.android.versionCode
10 | changeLog = CONTENT
11 | startUpload = this.&startUpload
12 | }
13 |
14 | //上传至fir
15 | def startUpload() {
16 | println("开始上传至fir")
17 | // def process = "python upToFir.py ${upUrl} ${appName} ${bundleId} ${verName} ${apiToken} ${iconPath} ${apkPath} ${buildNumber} ${changeLog}".execute()
18 | def process = "python upToFir.py $upUrl $appName $bundleId $verName $apiToken $iconPath $apkPath $buildNumber $changeLog".execute()
19 | //获取Python脚本日志,便于出错调试
20 | ByteArrayOutputStream result = new ByteArrayOutputStream()
21 | def inputStream = process.getInputStream()
22 | byte[] buffer = new byte[1024]
23 | int length
24 | while ((length = inputStream.read(buffer)) != -1) {
25 | result.write(buffer, 0, length)
26 | }
27 | println(result.toString("UTF-8"))
28 | println "上传结束 "
29 | }
30 |
31 |
--------------------------------------------------------------------------------