├── .gitignore
├── .idea
├── compiler.xml
├── copyright
│ └── profiles_settings.xml
├── gradle.xml
├── misc.xml
├── modules.xml
└── runConfigurations.xml
├── README.md
├── app
├── .gitignore
├── build.gradle
├── keystore
│ └── TinkerDemo.keystore
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── tinker
│ │ └── deeson
│ │ └── mytinkerdemo
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── tinker
│ │ │ └── deeson
│ │ │ └── mytinkerdemo
│ │ │ ├── MainActivity.java
│ │ │ ├── SampleApplicationLike.java
│ │ │ ├── SampleResultService.java
│ │ │ └── Utils.java
│ └── res
│ │ ├── layout
│ │ └── activity_main.xml
│ │ ├── mipmap-hdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── tinker_added.png
│ │ ├── mipmap-xxhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxxhdpi
│ │ └── ic_launcher.png
│ │ ├── values-w820dp
│ │ └── dimens.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── tinker
│ └── deeson
│ └── mytinkerdemo
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
18 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | 1.8
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TinkerDemo
2 |
3 | **Android热更新之微信Tinker框架的接入与测试**
4 |
5 | 项目教程在这里,简书:
6 | http://www.jianshu.com/p/aadcf2ea69a6
7 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | def javaVersion = JavaVersion.VERSION_1_7
4 | android {
5 | compileSdkVersion 23
6 | buildToolsVersion "23.0.2"
7 |
8 | compileOptions {
9 | sourceCompatibility javaVersion
10 | targetCompatibility javaVersion
11 | }
12 | //recommend
13 | dexOptions {
14 | jumboMode = true
15 | }
16 |
17 |
18 | defaultConfig {
19 | applicationId "com.tinker.deeson.mytinkerdemo"
20 | minSdkVersion 15
21 | targetSdkVersion 22
22 | versionCode 1
23 | versionName "1.0"
24 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
25 |
26 | buildConfigField "String", "MESSAGE", "\"I am the base apk\""
27 |
28 | buildConfigField "String", "TINKER_ID", "\"${getTinkerIdValue()}\""
29 | buildConfigField "String", "PLATFORM", "\"all\""
30 | }
31 |
32 | signingConfigs {
33 | release {
34 | try {
35 | storeFile file("./keystore/TinkerDemo.keystore")
36 | storePassword "TinkerDemo"
37 | keyAlias "TinkerDemo"
38 | keyPassword "TinkerDemo"
39 | } catch (ex) {
40 | throw new InvalidUserDataException(ex.toString())
41 | }
42 | }
43 |
44 | debug {
45 | storeFile file("./keystore/TinkerDemo.keystore")
46 | storePassword "TinkerDemo"
47 | keyAlias "TinkerDemo"
48 | keyPassword "TinkerDemo"
49 | }
50 | }
51 |
52 | buildTypes {
53 | release {
54 | minifyEnabled true
55 | signingConfig signingConfigs.release
56 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
57 | }
58 | debug {
59 | debuggable true
60 | minifyEnabled false
61 | signingConfig signingConfigs.debug
62 | }
63 | }
64 |
65 | sourceSets {
66 | main {
67 | jniLibs.srcDirs = ['libs']
68 | }
69 | }
70 |
71 |
72 | }
73 |
74 | dependencies {
75 | compile fileTree(dir: 'libs', include: ['*.jar'])
76 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
77 | exclude group: 'com.android.support', module: 'support-annotations'
78 | })
79 | compile "com.android.support:appcompat-v7:23.1.1"
80 | testCompile 'junit:junit:4.12'
81 |
82 | compile("com.tencent.tinker:tinker-android-lib:${TINKER_VERSION}") { changing = true }
83 | provided("com.tencent.tinker:tinker-android-anno:${TINKER_VERSION}") { changing = true }
84 | compile "com.android.support:multidex:1.0.1"
85 | }
86 |
87 | def gitSha() {
88 | try {
89 | // String gitRev = 'git rev-parse --short HEAD'.execute(null, project.rootDir).text.trim()
90 | String gitRev = "1008611"
91 | if (gitRev == null) {
92 | throw new GradleException("can't get git rev, you should add git to system path or just input test value, such as 'testTinkerId'")
93 | }
94 | return gitRev
95 | } catch (Exception e) {
96 | throw new GradleException("can't get git rev, you should add git to system path or just input test value, such as 'testTinkerId'")
97 | }
98 | }
99 |
100 | def bakPath = file("${buildDir}/bakApk/")
101 |
102 | ext {
103 | //for some reason, you may want to ignore tinkerBuild, such as instant run debug build?
104 | tinkerEnabled = true
105 |
106 | //for normal build
107 | //old apk file to build patch apk
108 | tinkerOldApkPath = "${bakPath}/app-release-0421-17-23-16.apk"
109 | //proguard mapping file to build patch apk
110 | tinkerApplyMappingPath = "${bakPath}/app-release-0421-17-23-16-mapping.txt"
111 | //resource R.txt to build patch apk, must input if there is resource changed
112 | tinkerApplyResourcePath = "${bakPath}/app-release-0421-17-23-16-R.txt"
113 |
114 | //only use for build all flavor, if not, just ignore this field
115 | tinkerBuildFlavorDirectory = "${bakPath}/app-0421-17-11-26"
116 | }
117 |
118 | def getOldApkPath() {
119 | return hasProperty("OLD_APK") ? OLD_APK : ext.tinkerOldApkPath
120 | }
121 |
122 | def getApplyMappingPath() {
123 | return hasProperty("APPLY_MAPPING") ? APPLY_MAPPING : ext.tinkerApplyMappingPath
124 | }
125 |
126 | def getApplyResourceMappingPath() {
127 | return hasProperty("APPLY_RESOURCE") ? APPLY_RESOURCE : ext.tinkerApplyResourcePath
128 | }
129 |
130 | def getTinkerIdValue() {
131 | return hasProperty("TINKER_ID") ? TINKER_ID : gitSha()
132 | }
133 |
134 | def buildWithTinker() {
135 | return hasProperty("TINKER_ENABLE") ? TINKER_ENABLE : ext.tinkerEnabled
136 | }
137 |
138 | def getTinkerBuildFlavorDirectory() {
139 | return ext.tinkerBuildFlavorDirectory
140 | }
141 |
142 | if (buildWithTinker()) {
143 | apply plugin: 'com.tencent.tinker.patch'
144 |
145 | tinkerPatch {
146 | /**
147 | * 默认为null
148 | * 将旧的apk和新的apk建立关联
149 | * 从build / bakApk添加apk
150 | */
151 | oldApk = getOldApkPath()
152 | /**
153 | * 可选,默认'false'
154 | *有些情况下我们可能会收到一些警告
155 | *如果ignoreWarning为true,我们只是断言补丁过程
156 | * case 1:minSdkVersion低于14,但是你使用dexMode与raw。
157 | * case 2:在AndroidManifest.xml中新添加Android组件,
158 | * case 3:装载器类在dex.loader {}不保留在主要的dex,
159 | * 它必须让tinker不工作。
160 | * case 4:在dex.loader {}中的loader类改变,
161 | * 加载器类是加载补丁dex。改变它们是没有用的。
162 | * 它不会崩溃,但这些更改不会影响。你可以忽略它
163 | * case 5:resources.arsc已经改变,但是我们不使用applyResourceMapping来构建
164 | */
165 | ignoreWarning = false
166 |
167 | /**
168 | *可选,默认为“true”
169 | * 是否签名补丁文件
170 | * 如果没有,你必须自己做。否则在补丁加载过程中无法检查成功
171 | * 我们将使用sign配置与您的构建类型
172 | */
173 | useSign = true
174 |
175 | /**
176 | 可选,默认为“true”
177 | 是否使用tinker构建
178 | */
179 | tinkerEnable = buildWithTinker()
180 |
181 | /**
182 | * 警告,applyMapping会影响正常的android build!
183 | */
184 | buildConfig {
185 | /**
186 | *可选,默认为'null'
187 | * 如果我们使用tinkerPatch构建补丁apk,你最好应用旧的
188 | * apk映射文件如果minifyEnabled是启用!
189 | * 警告:你必须小心,它会影响正常的组装构建!
190 | */
191 | applyMapping = getApplyMappingPath()
192 | /**
193 | *可选,默认为'null'
194 | * 很高兴保持资源ID从R.txt文件,以减少java更改
195 | */
196 | applyResourceMapping = getApplyResourceMappingPath()
197 |
198 | /**
199 | *必需,默认'null'
200 | * 因为我们不想检查基地apk与md5在运行时(它是慢)
201 | * tinkerId用于在试图应用补丁时标识唯一的基本apk。
202 | * 我们可以使用git rev,svn rev或者简单的versionCode。
203 | * 我们将在您的清单中自动生成tinkerId
204 | */
205 | tinkerId = getTinkerIdValue()
206 |
207 | /**
208 | *如果keepDexApply为true,则表示dex指向旧apk的类。
209 | * 打开这可以减少dex diff文件大小。
210 | */
211 | keepDexApply = false
212 | }
213 |
214 | dex {
215 | /**
216 | *可选,默认'jar'
217 | * 只能是'raw'或'jar'。对于原始,我们将保持其原始格式
218 | * 对于jar,我们将使用zip格式重新包装dexes。
219 | * 如果你想支持下面14,你必须使用jar
220 | * 或者你想保存rom或检查更快,你也可以使用原始模式
221 | */
222 | dexMode = "jar"
223 |
224 | /**
225 | *必需,默认'[]'
226 | * apk中的dexes应该处理tinkerPatch
227 | * 它支持*或?模式。
228 | */
229 | pattern = ["classes*.dex",
230 | "assets/secondary-dex-?.jar"]
231 | /**
232 | *必需,默认'[]'
233 | * 警告,这是非常非常重要的,加载类不能随补丁改变。
234 | * 因此,它们将从补丁程序中删除。
235 | * 你必须把下面的类放到主要的dex。
236 | * 简单地说,你应该添加自己的应用程序{@code tinker.sample.android.SampleApplication}
237 | * 自己的tinkerLoader,和你使用的类
238 | *
239 | */
240 | loader = [
241 | //use sample, let BaseBuildInfo unchangeable with tinker
242 | "tinker.sample.android.app.BaseBuildInfo"
243 | ]
244 | }
245 |
246 | lib {
247 | /**
248 | 可选,默认'[]'
249 | apk中的图书馆应该处理tinkerPatch
250 | 它支持*或?模式。
251 | 对于资源库,我们只是在补丁目录中恢复它们
252 | 你可以得到他们在TinkerLoadResult与Tinker
253 | */
254 | pattern = ["lib/armeabi/*.so"]
255 | }
256 |
257 | res {
258 | /**
259 | *可选,默认'[]'
260 | * apk中的什么资源应该处理tinkerPatch
261 | * 它支持*或?模式。
262 | * 你必须包括你在这里的所有资源,
263 | * 否则,他们不会重新包装在新的apk资源。
264 | */
265 | pattern = ["res/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]
266 |
267 | /**
268 | *可选,默认'[]'
269 | *资源文件排除模式,忽略添加,删除或修改资源更改
270 | * *它支持*或?模式。
271 | * *警告,我们只能使用文件没有relative与resources.arsc
272 | */
273 | ignoreChange = ["assets/sample_meta.txt"]
274 |
275 | /**
276 | *默认100kb
277 | * *对于修改资源,如果它大于'largeModSize'
278 | * *我们想使用bsdiff算法来减少补丁文件的大小
279 | */
280 | largeModSize = 100
281 | }
282 |
283 | packageConfig {
284 | /**
285 | *可选,默认'TINKER_ID,TINKER_ID_VALUE','NEW_TINKER_ID,NEW_TINKER_ID_VALUE'
286 | * 包元文件gen。路径是修补程序文件中的assets / package_meta.txt
287 | * 你可以在您自己的PackageCheck方法中使用securityCheck.getPackageProperties()
288 | * 或TinkerLoadResult.getPackageConfigByName
289 | * 我们将从旧的apk清单为您自动获取TINKER_ID,
290 | * 其他配置文件(如下面的patchMessage)不是必需的
291 | */
292 | configField("patchMessage", "tinker is sample to use")
293 | /**
294 | *只是一个例子,你可以使用如sdkVersion,品牌,渠道...
295 | * 你可以在SamplePatchListener中解析它。
296 | * 然后你可以使用补丁条件!
297 | */
298 | configField("platform", "all")
299 | /**
300 | * 补丁版本通过packageConfig
301 | */
302 | configField("patchVersion", "1.0")
303 | }
304 | //或者您可以添加外部的配置文件,或从旧apk获取元值
305 | //project.tinkerPatch.packageConfig.configField("test1", project.tinkerPatch.packageConfig.getMetaDataFromOldApk("Test"))
306 | //project.tinkerPatch.packageConfig.configField("test2", "sample")
307 |
308 | /**
309 | * 如果你不使用zipArtifact或者path,我们只是使用7za来试试
310 | */
311 | sevenZip {
312 | /**
313 | * 可选,默认'7za'
314 | * 7zip工件路径,它将使用正确的7za与您的平台
315 | */
316 | zipArtifact = "com.tencent.mm:SevenZip:1.1.10"
317 | /**
318 | * 可选,默认'7za'
319 | * 你可以自己指定7za路径,它将覆盖zipArtifact值
320 | */
321 | // path = "/usr/local/bin/7za"
322 | }
323 | }
324 |
325 | List flavors = new ArrayList<>();
326 | project.android.productFlavors.each {flavor ->
327 | flavors.add(flavor.name)
328 | }
329 | boolean hasFlavors = flavors.size() > 0
330 | /**
331 | * bak apk and mapping
332 | */
333 | android.applicationVariants.all { variant ->
334 | /**
335 | * task type, you want to bak
336 | */
337 | def taskName = variant.name
338 | def date = new Date().format("MMdd-HH-mm-ss")
339 |
340 | tasks.all {
341 | if ("assemble${taskName.capitalize()}".equalsIgnoreCase(it.name)) {
342 |
343 | it.doLast {
344 | copy {
345 | def fileNamePrefix = "${project.name}-${variant.baseName}"
346 | def newFileNamePrefix = hasFlavors ? "${fileNamePrefix}" : "${fileNamePrefix}-${date}"
347 |
348 | def destPath = hasFlavors ? file("${bakPath}/${project.name}-${date}/${variant.flavorName}") : bakPath
349 | from variant.outputs.outputFile
350 | into destPath
351 | rename { String fileName ->
352 | fileName.replace("${fileNamePrefix}.apk", "${newFileNamePrefix}.apk")
353 | }
354 |
355 | from "${buildDir}/outputs/mapping/${variant.dirName}/mapping.txt"
356 | into destPath
357 | rename { String fileName ->
358 | fileName.replace("mapping.txt", "${newFileNamePrefix}-mapping.txt")
359 | }
360 |
361 | from "${buildDir}/intermediates/symbols/${variant.dirName}/R.txt"
362 | into destPath
363 | rename { String fileName ->
364 | fileName.replace("R.txt", "${newFileNamePrefix}-R.txt")
365 | }
366 | }
367 | }
368 | }
369 | }
370 | }
371 | project.afterEvaluate {
372 | //sample use for build all flavor for one time
373 | if (hasFlavors) {
374 | task(tinkerPatchAllFlavorRelease) {
375 | group = 'tinker'
376 | def originOldPath = getTinkerBuildFlavorDirectory()
377 | for (String flavor : flavors) {
378 | def tinkerTask = tasks.getByName("tinkerPatch${flavor.capitalize()}Release")
379 | dependsOn tinkerTask
380 | def preAssembleTask = tasks.getByName("process${flavor.capitalize()}ReleaseManifest")
381 | preAssembleTask.doFirst {
382 | String flavorName = preAssembleTask.name.substring(7, 8).toLowerCase() + preAssembleTask.name.substring(8, preAssembleTask.name.length() - 15)
383 | project.tinkerPatch.oldApk = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-release.apk"
384 | project.tinkerPatch.buildConfig.applyMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-release-mapping.txt"
385 | project.tinkerPatch.buildConfig.applyResourceMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-release-R.txt"
386 |
387 | }
388 |
389 | }
390 | }
391 |
392 | task(tinkerPatchAllFlavorDebug) {
393 | group = 'tinker'
394 | def originOldPath = getTinkerBuildFlavorDirectory()
395 | for (String flavor : flavors) {
396 | def tinkerTask = tasks.getByName("tinkerPatch${flavor.capitalize()}Debug")
397 | dependsOn tinkerTask
398 | def preAssembleTask = tasks.getByName("process${flavor.capitalize()}DebugManifest")
399 | preAssembleTask.doFirst {
400 | String flavorName = preAssembleTask.name.substring(7, 8).toLowerCase() + preAssembleTask.name.substring(8, preAssembleTask.name.length() - 13)
401 | project.tinkerPatch.oldApk = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-debug.apk"
402 | project.tinkerPatch.buildConfig.applyMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-debug-mapping.txt"
403 | project.tinkerPatch.buildConfig.applyResourceMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-debug-R.txt"
404 | }
405 |
406 | }
407 | }
408 | }
409 | }
410 | }
411 |
--------------------------------------------------------------------------------
/app/keystore/TinkerDemo.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeesonWoo/TinkerDemo/9e673a11159b07b28cfe450fb24267baae6255e0/app/keystore/TinkerDemo.keystore
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in D:\programs\adt\sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/tinker/deeson/mytinkerdemo/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.tinker.deeson.mytinkerdemo;
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 | * Instrumentation 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.tinker.deeson.mytinkerdemo", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/java/com/tinker/deeson/mytinkerdemo/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.tinker.deeson.mytinkerdemo;
2 |
3 | import android.os.Environment;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.os.Bundle;
6 | import android.view.View;
7 |
8 | import com.tencent.tinker.lib.tinker.TinkerInstaller;
9 | import com.tencent.tinker.loader.shareutil.ShareTinkerInternals;
10 |
11 | public class MainActivity extends AppCompatActivity implements View.OnClickListener {
12 |
13 | @Override
14 | protected void onCreate(Bundle savedInstanceState) {
15 | super.onCreate(savedInstanceState);
16 | setContentView(R.layout.activity_main);
17 | findViewById(R.id.btn_load).setOnClickListener(this);
18 | findViewById(R.id.btn_kill).setOnClickListener(this);
19 | }
20 |
21 | @Override
22 | protected void onResume() {
23 | super.onResume();
24 | Utils.setBackground(false);
25 | }
26 |
27 | @Override
28 | protected void onPause() {
29 | super.onPause();
30 | Utils.setBackground(true);
31 | }
32 |
33 | @Override
34 | public void onClick(View v) {
35 | switch (v.getId()){
36 | case R.id.btn_load:
37 | loadPatch();
38 | break;
39 | case R.id.btn_kill:
40 | killApp();
41 | break;
42 | }
43 | }
44 |
45 | /**
46 | * 加载热补丁插件
47 | */
48 | public void loadPatch() {
49 | TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(),
50 | Environment.getExternalStorageDirectory().getAbsolutePath() + "/myTinkerDemo/TinkerPatch");
51 | }
52 |
53 | /**
54 | * 杀死应用加载补丁
55 | */
56 | public void killApp() {
57 | ShareTinkerInternals.killAllOtherProcess(getApplicationContext());
58 | android.os.Process.killProcess(android.os.Process.myPid());
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/app/src/main/java/com/tinker/deeson/mytinkerdemo/SampleApplicationLike.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Tencent is pleased to support the open source community by making Tinker available.
3 | *
4 | * Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved.
5 | *
6 | * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in
7 | * compliance with the License. You may obtain a copy of the License at
8 | *
9 | * https://opensource.org/licenses/BSD-3-Clause
10 | *
11 | * Unless required by applicable law or agreed to in writing, software distributed under the License is
12 | * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13 | * either express or implied. See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.tinker.deeson.mytinkerdemo;
18 |
19 | import android.annotation.TargetApi;
20 | import android.app.Application;
21 | import android.content.Context;
22 | import android.content.Intent;
23 | import android.os.Build;
24 | import android.support.multidex.MultiDex;
25 | import android.widget.Toast;
26 |
27 | import com.tencent.tinker.anno.DefaultLifeCycle;
28 | import com.tencent.tinker.lib.listener.DefaultPatchListener;
29 | import com.tencent.tinker.lib.patch.UpgradePatch;
30 | import com.tencent.tinker.lib.reporter.DefaultLoadReporter;
31 | import com.tencent.tinker.lib.reporter.DefaultPatchReporter;
32 | import com.tencent.tinker.lib.tinker.Tinker;
33 | import com.tencent.tinker.lib.tinker.TinkerInstaller;
34 | import com.tencent.tinker.loader.app.DefaultApplicationLike;
35 | import com.tencent.tinker.loader.shareutil.ShareConstants;
36 |
37 |
38 | @SuppressWarnings("unused")
39 | @DefaultLifeCycle(application = "com.tinker.deeson.mytinkerdemo.SampleApplication",
40 | flags = ShareConstants.TINKER_ENABLE_ALL,
41 | loadVerifyFlag = false)
42 | public class SampleApplicationLike extends DefaultApplicationLike {
43 | private static final String TAG = "Tinker.SampleApplicationLike";
44 |
45 | public SampleApplicationLike(Application application, int tinkerFlags, boolean tinkerLoadVerifyFlag,
46 | long applicationStartElapsedTime, long applicationStartMillisTime, Intent tinkerResultIntent) {
47 | super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent);
48 | }
49 |
50 | /**
51 | * install multiDex before install tinker
52 | * so we don't need to put the tinker lib classes in the main dex
53 | *
54 | * @param base
55 | */
56 | @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
57 | @Override
58 | public void onBaseContextAttached(Context base) {
59 | super.onBaseContextAttached(base);
60 | //you must install multiDex whatever tinker is installed!
61 | MultiDex.install(base);
62 | TinkerInstaller.install(this,new DefaultLoadReporter(getApplication())
63 | ,new DefaultPatchReporter(getApplication()),new DefaultPatchListener(getApplication()),SampleResultService.class,new UpgradePatch());
64 | Tinker tinker = Tinker.with(getApplication());
65 |
66 | Toast.makeText(getApplication(),"加载完成", Toast.LENGTH_SHORT).show();
67 | }
68 |
69 | @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
70 | public void registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks callback) {
71 | getApplication().registerActivityLifecycleCallbacks(callback);
72 | }
73 |
74 | @Override
75 | public void onCreate() {
76 | super.onCreate();
77 | //此处写自己的Application逻辑
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/app/src/main/java/com/tinker/deeson/mytinkerdemo/SampleResultService.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Tencent is pleased to support the open source community by making Tinker available.
3 | *
4 | * Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved.
5 | *
6 | * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in
7 | * compliance with the License. You may obtain a copy of the License at
8 | *
9 | * https://opensource.org/licenses/BSD-3-Clause
10 | *
11 | * Unless required by applicable law or agreed to in writing, software distributed under the License is
12 | * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13 | * either express or implied. See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.tinker.deeson.mytinkerdemo;
18 |
19 | import android.content.BroadcastReceiver;
20 | import android.content.Context;
21 | import android.content.Intent;
22 | import android.content.IntentFilter;
23 | import android.os.Handler;
24 | import android.os.Looper;
25 | import android.widget.Toast;
26 |
27 | import com.tencent.tinker.lib.service.DefaultTinkerResultService;
28 | import com.tencent.tinker.lib.service.PatchResult;
29 | import com.tencent.tinker.lib.util.TinkerLog;
30 | import com.tencent.tinker.lib.util.TinkerServiceInternals;
31 | import com.tencent.tinker.loader.shareutil.SharePatchFileUtil;
32 |
33 | import java.io.File;
34 |
35 |
36 | /**
37 | * optional, you can just use DefaultTinkerResultService
38 | * we can restart process when we are at background or screen off
39 | */
40 | public class SampleResultService extends DefaultTinkerResultService {
41 | private static final String TAG = "Tinker.SampleResultService";
42 |
43 |
44 | @Override
45 | public void onPatchResult(final PatchResult result) {
46 | if (result == null) {
47 | TinkerLog.e(TAG, "SampleResultService received null result!!!!");
48 | return;
49 | }
50 | TinkerLog.i(TAG, "SampleResultService receive result: %s", result.toString());
51 |
52 | //first, we want to kill the recover process
53 | TinkerServiceInternals.killTinkerPatchServiceProcess(getApplicationContext());
54 |
55 | Handler handler = new Handler(Looper.getMainLooper());
56 | handler.post(new Runnable() {
57 | @Override
58 | public void run() {
59 | if (result.isSuccess) {
60 | Toast.makeText(getApplicationContext(), "patch success, please restart process", Toast.LENGTH_LONG).show();
61 | } else {
62 | Toast.makeText(getApplicationContext(), "patch fail, please check reason", Toast.LENGTH_LONG).show();
63 | }
64 | }
65 | });
66 | // is success and newPatch, it is nice to delete the raw file, and restart at once
67 | // for old patch, you can't delete the patch file
68 | if (result.isSuccess) {
69 | File rawFile = new File(result.rawPatchFilePath);
70 | if (rawFile.exists()) {
71 | TinkerLog.i(TAG, "save delete raw patch file");
72 | SharePatchFileUtil.safeDeleteFile(rawFile);
73 | }
74 | //not like TinkerResultService, I want to restart just when I am at background!
75 | //if you have not install tinker this moment, you can use TinkerApplicationHelper api
76 | if (checkIfNeedKill(result)) {
77 | if (Utils.isBackground()) {
78 | TinkerLog.i(TAG, "it is in background, just restart process");
79 | restartProcess();
80 | } else {
81 | //we can wait process at background, such as onAppBackground
82 | //or we can restart when the screen off
83 | TinkerLog.i(TAG, "tinker wait screen to restart process");
84 | new ScreenState(getApplicationContext(), new ScreenState.IOnScreenOff() {
85 | @Override
86 | public void onScreenOff() {
87 | restartProcess();
88 | }
89 | });
90 | }
91 | } else {
92 | TinkerLog.i(TAG, "I have already install the newly patch version!");
93 | }
94 | }
95 | }
96 |
97 | /**
98 | * you can restart your process through service or broadcast
99 | */
100 | private void restartProcess() {
101 | TinkerLog.i(TAG, "app is background now, i can kill quietly");
102 | //you can send service or broadcast intent to restart your process
103 | android.os.Process.killProcess(android.os.Process.myPid());
104 | }
105 |
106 | static class ScreenState {
107 | interface IOnScreenOff {
108 | void onScreenOff();
109 | }
110 |
111 | ScreenState(Context context, final IOnScreenOff onScreenOffInterface) {
112 | IntentFilter filter = new IntentFilter();
113 | filter.addAction(Intent.ACTION_SCREEN_OFF);
114 | context.registerReceiver(new BroadcastReceiver() {
115 |
116 | @Override
117 | public void onReceive(Context context, Intent in) {
118 | String action = in == null ? "" : in.getAction();
119 | TinkerLog.i(TAG, "ScreenReceiver action [%s] ", action);
120 | if (Intent.ACTION_SCREEN_OFF.equals(action)) {
121 |
122 | context.unregisterReceiver(this);
123 |
124 | if (onScreenOffInterface != null) {
125 | onScreenOffInterface.onScreenOff();
126 | }
127 | }
128 | }
129 | }, filter);
130 | }
131 | }
132 |
133 | }
134 |
--------------------------------------------------------------------------------
/app/src/main/java/com/tinker/deeson/mytinkerdemo/Utils.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Tencent is pleased to support the open source community by making Tinker available.
3 | *
4 | * Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved.
5 | *
6 | * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in
7 | * compliance with the License. You may obtain a copy of the License at
8 | *
9 | * https://opensource.org/licenses/BSD-3-Clause
10 | *
11 | * Unless required by applicable law or agreed to in writing, software distributed under the License is
12 | * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13 | * either express or implied. See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.tinker.deeson.mytinkerdemo;
18 |
19 | import android.os.Environment;
20 | import android.os.StatFs;
21 |
22 | import com.tencent.tinker.loader.shareutil.ShareConstants;
23 |
24 | import java.io.ByteArrayOutputStream;
25 | import java.io.File;
26 | import java.io.IOException;
27 | import java.io.PrintStream;
28 |
29 | public class Utils {
30 |
31 | /**
32 | * the error code define by myself
33 | * should after {@code ShareConstants.ERROR_PATCH_INSERVICE
34 | */
35 | public static final int ERROR_PATCH_GOOGLEPLAY_CHANNEL = -5;
36 | public static final int ERROR_PATCH_ROM_SPACE = -6;
37 | public static final int ERROR_PATCH_MEMORY_LIMIT = -7;
38 | public static final int ERROR_PATCH_ALREADY_APPLY = -8;
39 | public static final int ERROR_PATCH_CRASH_LIMIT = -9;
40 | public static final int ERROR_PATCH_RETRY_COUNT_LIMIT = -10;
41 | public static final int ERROR_PATCH_CONDITION_NOT_SATISFIED = -11;
42 |
43 | public static final String PLATFORM = "platform";
44 |
45 | public static final int MIN_MEMORY_HEAP_SIZE = 45;
46 |
47 | private static boolean background = false;
48 |
49 | public static boolean isGooglePlay() {
50 | return false;
51 | }
52 |
53 | public static boolean isBackground() {
54 | return background;
55 | }
56 |
57 | public static void setBackground(boolean back) {
58 | background = back;
59 | }
60 |
61 | public static int checkForPatchRecover(long roomSize, int maxMemory) {
62 | if (Utils.isGooglePlay()) {
63 | return Utils.ERROR_PATCH_GOOGLEPLAY_CHANNEL;
64 | }
65 | if (maxMemory < MIN_MEMORY_HEAP_SIZE) {
66 | return Utils.ERROR_PATCH_MEMORY_LIMIT;
67 | }
68 | //or you can mention user to clean their rom space!
69 | if (!checkRomSpaceEnough(roomSize)) {
70 | return Utils.ERROR_PATCH_ROM_SPACE;
71 | }
72 |
73 | return ShareConstants.ERROR_PATCH_OK;
74 | }
75 |
76 | public static boolean isXposedExists(Throwable thr) {
77 | StackTraceElement[] stackTraces = thr.getStackTrace();
78 | for (StackTraceElement stackTrace : stackTraces) {
79 | final String clazzName = stackTrace.getClassName();
80 | if (clazzName != null && clazzName.contains("de.robv.android.xposed.XposedBridge")) {
81 | return true;
82 | }
83 | }
84 | return false;
85 | }
86 |
87 | @Deprecated
88 | public static boolean checkRomSpaceEnough(long limitSize) {
89 | long allSize;
90 | long availableSize = 0;
91 | try {
92 | File data = Environment.getDataDirectory();
93 | StatFs sf = new StatFs(data.getPath());
94 | availableSize = (long) sf.getAvailableBlocks() * (long) sf.getBlockSize();
95 | allSize = (long) sf.getBlockCount() * (long) sf.getBlockSize();
96 | } catch (Exception e) {
97 | allSize = 0;
98 | }
99 |
100 | if (allSize != 0 && availableSize > limitSize) {
101 | return true;
102 | }
103 | return false;
104 | }
105 |
106 | public static String getExceptionCauseString(final Throwable ex) {
107 | final ByteArrayOutputStream bos = new ByteArrayOutputStream();
108 | final PrintStream ps = new PrintStream(bos);
109 |
110 | try {
111 | // print directly
112 | Throwable t = ex;
113 | while (t.getCause() != null) {
114 | t = t.getCause();
115 | }
116 | t.printStackTrace(ps);
117 | return toVisualString(bos.toString());
118 | } finally {
119 | try {
120 | bos.close();
121 | } catch (IOException e) {
122 | e.printStackTrace();
123 | }
124 | }
125 | }
126 |
127 | private static String toVisualString(String src) {
128 | boolean cutFlg = false;
129 |
130 | if (null == src) {
131 | return null;
132 | }
133 |
134 | char[] chr = src.toCharArray();
135 | if (null == chr) {
136 | return null;
137 | }
138 |
139 | int i = 0;
140 | for (; i < chr.length; i++) {
141 | if (chr[i] > 127) {
142 | chr[i] = 0;
143 | cutFlg = true;
144 | break;
145 | }
146 | }
147 |
148 | if (cutFlg) {
149 | return new String(chr, 0, i);
150 | } else {
151 | return src;
152 | }
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
16 |
17 |
26 |
27 |
34 |
35 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeesonWoo/TinkerDemo/9e673a11159b07b28cfe450fb24267baae6255e0/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeesonWoo/TinkerDemo/9e673a11159b07b28cfe450fb24267baae6255e0/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeesonWoo/TinkerDemo/9e673a11159b07b28cfe450fb24267baae6255e0/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/tinker_added.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeesonWoo/TinkerDemo/9e673a11159b07b28cfe450fb24267baae6255e0/app/src/main/res/mipmap-xhdpi/tinker_added.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeesonWoo/TinkerDemo/9e673a11159b07b28cfe450fb24267baae6255e0/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeesonWoo/TinkerDemo/9e673a11159b07b28cfe450fb24267baae6255e0/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | MyTinkerDemo
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/tinker/deeson/mytinkerdemo/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.tinker.deeson.mytinkerdemo;
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 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.2.0'
9 | classpath ('com.tencent.tinker:tinker-patch-gradle-plugin:1.7.7')
10 |
11 | // NOTE: Do not place your application dependencies here; they belong
12 | // in the individual module build.gradle files
13 | }
14 | }
15 |
16 | allprojects {
17 | repositories {
18 | jcenter()
19 | }
20 | }
21 |
22 | task clean(type: Delete) {
23 | delete rootProject.buildDir
24 | }
25 |
--------------------------------------------------------------------------------
/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.7.7
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeesonWoo/TinkerDemo/9e673a11159b07b28cfe450fb24267baae6255e0/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Dec 28 10:00:20 PST 2015
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-2.14.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/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 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
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 Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------