├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── build.gradle ├── checkstyle.xml ├── checkstyle_suppressions.xml ├── docs ├── tinkerpatch-custom-sdk.md └── tinkerpatch-turnkey-sdk.md ├── gradle.properties ├── gradle ├── bintray.gradle ├── install.gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── integration ├── build.gradle ├── gradle.properties ├── okhttp │ ├── build.gradle │ ├── gradle.properties │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── com │ │ └── xmonster │ │ └── tkclient │ │ └── integration │ │ └── okhttp │ │ ├── OkHttpStreamFetcher.java │ │ ├── OkHttpTKClientModule.java │ │ └── OkHttpUrlLoader.java └── okhttp3 │ ├── build.gradle │ ├── gradle.properties │ └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── xmonster │ └── tkclient │ └── integration │ └── okhttp3 │ ├── OkHttp3StreamFetcher.java │ ├── OkHttp3TKClientModule.java │ └── OkHttp3UrlLoader.java ├── settings.gradle └── tinkerpatch-sdk ├── .gitignore ├── build.gradle ├── findbugs-exclude.xml ├── gradle.properties ├── pmd-ruleset.xml ├── proguard-rules.pro ├── proguard-rules.txt └── src └── main ├── AndroidManifest.xml ├── java └── com │ ├── tencent │ └── tinker │ │ ├── app │ │ ├── TinkerManager.java │ │ ├── TinkerServerManager.java │ │ ├── TinkerServerUtils.java │ │ ├── callback │ │ │ └── TinkerServerPatchRequestCallback.java │ │ ├── reporter │ │ │ ├── TinkerServerLoadReporter.java │ │ │ └── TinkerServerPatchListener.java │ │ └── service │ │ │ └── TinkerServerResultService.java │ │ └── server │ │ ├── TinkerServerClient.java │ │ ├── client │ │ ├── ConfigRequestCallback.java │ │ ├── DefaultPatchRequestCallback.java │ │ ├── PatchRequestCallback.java │ │ └── TinkerClientAPI.java │ │ ├── model │ │ ├── DataFetcher.java │ │ ├── Headers.java │ │ ├── TinkerClientUrl.java │ │ ├── request │ │ │ ├── BaseReport.java │ │ │ ├── FailReport.java │ │ │ └── SuccessReport.java │ │ └── response │ │ │ └── SyncResponse.java │ │ ├── urlconnection │ │ ├── UrlConnectionStreamFetcher.java │ │ └── UrlConnectionUrlLoader.java │ │ └── utils │ │ ├── Conditions.java │ │ ├── Debugger.java │ │ ├── NetStatusUtil.java │ │ ├── Preconditions.java │ │ ├── ServerUtils.java │ │ └── VersionUtils.java │ └── tinkerpatch │ └── sdk │ ├── TinkerPatch.java │ ├── server │ └── callback │ │ ├── ConfigRequestCallback.java │ │ ├── PatchRequestCallback.java │ │ └── RollbackCallBack.java │ └── tinker │ └── callback │ └── ResultCallBack.java └── res └── values └── strings.xml /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # Intellij 36 | *.iml 37 | .idea/workspace.xml 38 | 39 | # Keystore files 40 | *.jks 41 | 42 | .idea 43 | test.tmp 44 | reports 45 | .DS_Store 46 | 47 | example 48 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | jdk: oraclejdk8 3 | 4 | cache: 5 | directories: 6 | - .autoconf 7 | - $HOME/.m2 8 | 9 | android: 10 | components: 11 | - tools 12 | - tools # see https://github.com/travis-ci/travis-ci/issues/6040#issuecomment-219367943 13 | - platform-tools 14 | - build-tools-24.0.3 15 | - android-24 16 | - extra-android-m2repository 17 | 18 | script: ./gradlew clean check test install 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Shengjie Sim Sun 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TinkerPatch SDK 2 | [![Build Status](https://travis-ci.org/TinkerPatch/tinkerpatch-sdk.svg?branch=master)](https://travis-ci.org/TinkerPatch/tinkerpatch-sdk) 3 | [![Join Slack](https://slack.tinkerpatch.com/badge.svg)](https://slack.tinkerpatch.com) 4 | 5 | 6 | ### 一键集成SDK (推荐使用) [![Download](https://api.bintray.com/packages/simsun/maven/tinkerpatch-android-sdk/images/download.svg) ](https://bintray.com/simsun/maven/tinkerpatch-android-sdk/_latestVersion) 7 | 无需修改项目结构,集成简单、方便, 8 | 9 | [集成文档](docs/tinkerpatch-turnkey-sdk.md) 10 | 11 | 12 | 13 | 14 | ### 可定制化SDK [![Download](https://api.bintray.com/packages/simsun/maven/tinkerpatch-sdk/images/download.svg) ](https://bintray.com/simsun/maven/tinkerpatch-sdk/_latestVersion) 15 | 需要修改项目结构代码,定制性较高, 16 | 17 | [集成文档](docs/tinkerpatch-custom-sdk.md) 18 | 19 | 20 | 21 | ### 例子 22 | [一键接入例子](https://github.com/TinkerPatch/tinkerpatch-easy-sample) 23 | 24 | [完成ApplicationLike改造的例子](https://github.com/TinkerPatch/tinkerpatch-sample) 25 | 26 | [使用多Flavor和加固的例子](https://github.com/TinkerPatch/tinkerpatch-flavors-sample) 27 | 28 | [使用AndResGuard的例子](https://github.com/TinkerPatch/tinkerpatch-andresguard-sample) 29 | 30 | [使用Kotlin的例子](https://github.com/TinkerPatch/tinkerpatch-kotlin) 31 | 32 | [使用Rxjava2和Retrolambda的例子](https://github.com/TinkerPatch/tinkerpatch-rxjava2) 33 | 34 | ### 其他 35 | [更多文档](http://tinkerpatch.com/Docs/intro) 36 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | 4 | buildscript { 5 | repositories { 6 | //mavenLocal() 7 | jcenter() 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:2.2.3' 11 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7' 12 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' 13 | classpath "com.tencent.tinker:tinker-patch-gradle-plugin:${TINKER_VERSION}" 14 | } 15 | } 16 | 17 | allprojects { 18 | repositories { 19 | //mavenLocal() 20 | jcenter() 21 | } 22 | } 23 | // See http://blog.joda.org/2014/02/turning-off-doclint-in-jdk-8-javadoc.html. 24 | if (JavaVersion.current().isJava8Compatible()) { 25 | allprojects { 26 | tasks.withType(Javadoc) { 27 | options.addStringOption('Xdoclint:none', '-quiet') 28 | } 29 | } 30 | } 31 | 32 | subprojects { 33 | tasks.withType(JavaCompile) { 34 | sourceCompatibility = JavaVersion.VERSION_1_7 35 | targetCompatibility = JavaVersion.VERSION_1_7 36 | } 37 | } 38 | 39 | subprojects { project -> 40 | if (project.name.contains("sample") || project.name.contains("example")) { return; } 41 | repositories { 42 | jcenter() 43 | } 44 | apply plugin: 'checkstyle' 45 | 46 | checkstyle { 47 | toolVersion = '6.12.1' 48 | } 49 | 50 | checkstyle { 51 | configFile = rootProject.file('checkstyle.xml') 52 | configProperties.checkStyleConfigDir = rootProject.rootDir 53 | } 54 | 55 | task checkstyle(type: Checkstyle) { 56 | source 'src' 57 | include '**/*.java' 58 | exclude '**/gen/**' 59 | 60 | // empty classpath 61 | classpath = files() 62 | } 63 | 64 | afterEvaluate { 65 | if (project.tasks.findByName('check')) { 66 | check.dependsOn('checkstyle') 67 | } 68 | } 69 | 70 | gradle.projectsEvaluated { 71 | tasks.withType(JavaCompile) { 72 | options.compilerArgs << '-Xlint:unchecked' << '-Xlint:deprecation' 73 | } 74 | } 75 | } 76 | 77 | task clean(type: Delete) { 78 | delete rootProject.buildDir 79 | } 80 | -------------------------------------------------------------------------------- /checkstyle.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 | 51 | 52 | 53 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /checkstyle_suppressions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/tinkerpatch-custom-sdk.md: -------------------------------------------------------------------------------- 1 | # 定制化SDK 接入 2 | 3 | **作者已不再维护此SDK,建议使用 一键集成SDK** 4 | 5 | 6 | 这里只是针对 TinkerPatch SDK的使用说明,对于 Tinker 的基本用法,可参考[ Tinker接入指南](https://github.com/Tencent/tinker/wiki/Tinker-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97)。 7 | 8 | ## 第一步 添加gradle依赖 9 | 10 | gradle远程仓库依赖jcenter,例如 Tinker server sample中的[build.gradle](https://github.com/TinkerPatch/tinkerpatch-sample/blob/master/app/build.gradle). 11 | 12 | ```java 13 | repositories { 14 | jcenter() 15 | } 16 | ``` 17 | 18 | 再添加sdk库的dependencies依赖: 19 | 20 | ```java 21 | dependencies { 22 | compile("com.tencent.tinker:tinkerpatch-sdk:0.3.4") 23 | } 24 | ``` 25 | 26 | 如果使用`Tinker 1.7.5`的同学,请使用对应的`0.3.3`版本。0.3.4适配了Tinker 1.7.6的接口更新。 27 | 28 | ## 第二步 配置AndroidManifest文件 29 | 30 | 在AndroidManifest中声明SDK需要的权限: 31 | 32 | ```java 33 | 34 | 35 | 36 | ``` 37 | 38 | ## 第三步 配置AppKey与AppVersion 39 | 在 TinkerPatch 平台中得到的 AppKey 以及 AppVersion记住,我们可以简单的将他们写入`buildConfig` 40 | 41 | ```java 42 | buildConfigField "String", "APP_KEY", "\"f938475486f91936\"" 43 | buildConfigField "String", "APP_VERSION", "\"3.0.0\"" 44 | ``` 45 | 46 | ## 第四步 初始化 TinkerPatch SDK 47 | 48 | 我们提供默认的默认的实现,位于`TinkerManager`和`TinkerServerManager`. 49 | 首先初始化 TinkerPatch 的 SDK,例如 Sample 中 [SampleApplicationLike类](https://github.com/TinkerPatch/tinkerpatch-sample/blob/master/app/src/main/java/tinker/sample/android/app/SampleApplicationLike.java): 50 | 51 | ```java 52 | //初始化Tinker 53 | TinkerManager.installTinker(this); 54 | //初始化TinkerPatch SDK 55 | TinkerServerManager.installTinkerServer( 56 | getApplication(), Tinker.with(getApplication()), 3, 57 | BuildConfig.APP_KEY, BuildConfig.APP_VERSION, "default" 58 | ); 59 | //开始检查是否有补丁,这里配置的是每隔访问3小时服务器是否有更新。 60 | TinkerServerManager.checkTinkerUpdate(false); 61 | ``` 62 | 63 | SDK 需要Tinker已经初始化,`3`表示客户端每隔三个小时才会访问服务器一次,具体的 API 将在后面详细说明。 64 | appKey和appVersion为第三部填写的配置,可以通过`BuildConfig.APP_KEY`和`BuildConfig.APP_VERSION`得到。 65 | 由于GooglePlay渠道的限制,不能使用原生代码下发的机制更新app,我们会过滤channel中含有`google`的关键字,停止动态更新功能。 66 | 67 | 如果要使用SDK提供的默认的Service实现,需要在`AndroidManifest.xml`中声明: 68 | 69 | ```java 70 | 74 | ``` 75 | 76 | 你也可以根据自己的需求实现Manager, 所有与TinkerPatch后台交互的 API 都位于 TinkerServerClient.java中。 77 | -------------------------------------------------------------------------------- /docs/tinkerpatch-turnkey-sdk.md: -------------------------------------------------------------------------------- 1 | # SDK 接入 2 | 3 | [![Build Status](https://travis-ci.org/TinkerPatch/tinkerpatch-sample.svg?branch=master)](https://travis-ci.org/TinkerPatch/tinkerpatch-sample) 4 | [![Download](https://api.bintray.com/packages/simsun/maven/tinkerpatch-android-sdk/images/download.svg) ](https://bintray.com/simsun/maven/tinkerpatch-android-sdk/_latestVersion) 5 | [![Join Slack](https://slack.tinkerpatch.com/badge.svg)](https://slack.tinkerpatch.com) 6 | 7 | 这里只是针对 TinkerPatch SDK的使用说明,对于 Tinker 的基本用法,可参考[Tinker 接入指南](https://github.com/Tencent/tinker/wiki/Tinker-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97)。 Tinker SDK 在 Github 为大家提供了大量的范例,大家可点击前往 [[TinkerPatch Samples]](https://github.com/TinkerPatch). 8 | 9 | ## 第一步 添加 gradle 插件依赖 10 | 11 | gradle 远程仓库依赖 jcenter, 例如 TinkerPatch Sample 中的 [build.gradle](https://github.com/TinkerPatch/tinkerpatch-sample/blob/master/build.gradle). 12 | 13 | ``` 14 | buildscript { 15 | repositories { 16 | jcenter() 17 | } 18 | dependencies { 19 | // TinkerPatch 插件 20 | classpath "com.tinkerpatch.sdk:tinkerpatch-gradle-plugin:1.2.5" 21 | } 22 | } 23 | ``` 24 | 25 | **注意,在这里 SDK 使用了 fat 打包的模式,我们不能再引入任何 Tinker 的相关依赖,否则会造成版本冲突。** 26 | 27 | ## 第二步 集成 TinkerPatch SDK 28 | 29 | 添加 TinkerPatch SDK 库的 denpendencies 依赖, 可参考 Sample 中的 [app/build.gradle](https://github.com/TinkerPatch/tinkerpatch-sample/blob/master/app/build.gradle): 30 | 31 | ``` 32 | dependencies { 33 | // 若使用annotation需要单独引用,对于tinker的其他库都无需再引用 34 | provided("com.tinkerpatch.tinker:tinker-android-anno:1.9.5") 35 | compile("com.tinkerpatch.sdk:tinkerpatch-android-sdk:1.2.5") 36 | } 37 | ``` 38 | **注意,若使用 annotation 自动生成 Application, 需要单独引入 Tinker 的 tinker-android-anno 库。除此之外,我们无需再单独引入 tinker 的其他库。** 39 | 40 | 为了简单方便,我们将 TinkerPatch 相关的配置都放于 [tinkerpatch.gradle](https://github.com/TinkerPatch/tinkerpatch-sample/blob/master/app/tinkerpatch.gradle) 中, 我们需要将其引入: 41 | 42 | ``` 43 | apply from: 'tinkerpatch.gradle' 44 | ``` 45 | 46 | ## 第三步 配置 tinkerpatchSupport 参数 47 | 打开引入的 [tinkerpatch.gradle](https://github.com/TinkerPatch/tinkerpatch-sample/blob/master/app/tinkerpatch.gradle) 文件,它的具体参数如下: 48 | 49 | ``` 50 | tinkerpatchSupport { 51 | /** 可以在debug的时候关闭 tinkerPatch **/ 52 | tinkerEnable = true 53 | 54 | /** 是否使用一键接入功能 **/ 55 | reflectApplication = true 56 | 57 | /** 是否开启加固模式,只有在使用加固时才能开启此开关 **/ 58 | protectedApp = false 59 | 60 | /** 补丁是否支持新增 Activity **/ 61 | supportComponent = false 62 | 63 | autoBackupApkPath = "${bakPath}" 64 | 65 | /** 在tinkerpatch.com得到的appKey **/ 66 | appKey = "yourAppKey" 67 | /** 注意: 若发布新的全量包, appVersion一定要更新 **/ 68 | appVersion = "1.0.0" 69 | 70 | def pathPrefix = "${bakPath}/${baseInfo}/${variantName}/" 71 | def name = "${project.name}-${variantName}" 72 | 73 | baseApkFile = "${pathPrefix}/${name}.apk" 74 | baseProguardMappingFile = "${pathPrefix}/${name}-mapping.txt" 75 | baseResourceRFile = "${pathPrefix}/${name}-R.txt" 76 | } 77 | ``` 78 | 79 | 它的具体含义如下: 80 | 81 | | 参数 | 默认值 | 描述 | 82 | | ----------------- | --------- | --------- | 83 | | tinkerEnable | true | 是否开启 tinkerpatchSupport 插件功能。 | 84 | | appKey | "" | 在 TinkerPatch 平台 申请的 appkey, 例如 sample 中的 'f828475486f91936' | 85 | | appVersion | "" | 在 TinkerPatch 平台 输入的版本号, 例如 sample 中的 '1.0.0'。 **注意,我们使用 appVersion 作为 TinkerId, 我们需要保证每个发布出去的基础安装包的 appVersion 都不一样。**| 86 | | reflectApplication | false | 是否反射 Application 实现一键接入;**一般来说,接入 Tinker 我们需要改造我们的 Application, 若这里为 true, 即我们无需对应用做任何改造即可接入**。| 87 | | autoBackupApkPath |"" |将每次编译产生的 apk/mapping.txt/R.txt 归档存储的位置| 88 | | baseApkFile | "" | `基准包的文件路径, 对应 tinker 插件中的 oldApk 参数`;编译补丁包时,必需指定基准版本的 apk,默认值为空,则表示不是进行补丁包的编译。 | 89 | | baseProguardMappingFile | "" | `基准包的 Proguard mapping.txt 文件路径, 对应 tinker 插件 applyMapping 参数`;在编译新的 apk 时候,我们希望通过保持基准 apk 的 proguard 混淆方式,从而减少补丁包的大小。这是强烈推荐的,编译补丁包时,我们推荐输入基准 apk 生成的 mapping.txt 文件。 | 90 | | baseResourceRFile | "" | `基准包的资源 R.txt 文件路径, 对应 tinker 插件 applyResourceMapping 参数`;在编译新的apk时候,我们希望通基准 apk 的 R.txt 文件来保持 Resource Id 的分配,这样不仅可以减少补丁包的大小,同时也避免由于 Resource Id 改变导致 remote view 异常。 | 91 | | protectedApp | false | 是否开启支持加固,**注意:只有在使用加固时才能开启此开关**| 92 | | supportComponent | false | 是否开启支持在补丁包中动态增加Activity | 93 | | backupFileNameFormat | '${appName}-${variantName}' | 格式化命名备份文件 **这里请使用单引号** | 94 | 95 | **一般来说,我们无需修改引用 android 的编译配置,也不用修改 tinker 插件原来的配置**。针对特殊需求,具体的参数含义可参考 Tinker 文档:[Tinker 接入指南](https://github.com/Tencent/tinker/wiki/Tinker-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97). 96 | 97 | ## 第四步 初始化 TinkerPatch SDK 98 | 最后在我们的代码中,只需简单的初始化 TinkerPatch 的 SDK 即可,我们无需考虑 Tinker 是如何下载/合成/应用补丁包, 也无需引入各种各样 Tinker 的相关类。 99 | 100 | ### 1. reflectApplication = true 的情况 101 | 若我们使用 reflectApplication 模式,我们无需为接入 Tinker 而改造我们的 Application 类。初始化 SDK 可参考 tinkerpatch-easy-sample 中的 [SampleApplication 类](https://github.com/TinkerPatch/tinkerpatch-easy-sample/blob/master/app/src/main/java/com/tinkerpatch/easy_sample/SampleApplication.java). 102 | 103 | ``` 104 | public class SampleApplication extends Application { 105 | 106 | ... 107 | 108 | @Override 109 | public void onCreate() { 110 | super.onCreate(); 111 | // 我们可以从这里获得Tinker加载过程的信息 112 | tinkerApplicationLike = TinkerPatchApplicationLike.getTinkerPatchApplicationLike(); 113 | 114 | // 初始化TinkerPatch SDK, 更多配置可参照API章节中的,初始化SDK 115 | TinkerPatch.init(tinkerApplicationLike) 116 | .reflectPatchLibrary() 117 | .setPatchRollbackOnScreenOff(true) 118 | .setPatchRestartOnSrceenOff(true) 119 | .setFetchPatchIntervalByHours(3); 120 | 121 | // 每隔3个小时(通过setFetchPatchIntervalByHours设置)去访问后台时候有更新,通过handler实现轮训的效果 122 | TinkerPatch.with().fetchPatchUpdateAndPollWithInterval(); 123 | } 124 | 125 | ... 126 | 127 | ``` 128 | 129 | 我们将 Tinker 加载补丁过程的结果存放在 TinkerPatchApplicationLike 中。 130 | 131 | ### 2. reflectApplication = false 的情况 132 | 若我们已经完成了应用的 Application 改造,即将 Application 的逻辑移动到 ApplicationLike类中。我们可以参考 tinkerpatch-sample 中的 [SampleApplicationLike 类](https://github.com/TinkerPatch/tinkerpatch-sample/blob/master/app/src/main/java/tinker/sample/android/app/SampleApplicationLike.java). 133 | 134 | ``` 135 | public class SampleApplicationLike extends DefaultApplicationLike { 136 | 137 | ... 138 | 139 | @Override 140 | public void onCreate() { 141 | super.onCreate(); 142 | // 初始化TinkerPatch SDK, 更多配置可参照API章节中的,初始化 SDK 143 | TinkerPatch.init(this) 144 | .reflectPatchLibrary() 145 | .setPatchRollbackOnScreenOff(true) 146 | .setPatchRestartOnSrceenOff(true) 147 | .setFetchPatchIntervalByHours(3); 148 | 149 | // 每隔3个小时(通过setFetchPatchIntervalByHours设置)去访问后台时候有更新,通过handler实现轮训的效果 150 | TinkerPatch.with().fetchPatchUpdateAndPollWithInterval(); 151 | } 152 | 153 | ... 154 | 155 | } 156 | ``` 157 | 158 | **注意:初始化的代码建议紧跟 super.onCreate(),并且所有进程都需要初始化,已达到所有进程都可以被 patch 的目的** 159 | 160 | **如果你确定只想在主进程中初始化 tinkerPatch,那也请至少在 :patch 进程中初始化,否则会有造成 :patch 进程crash,无法使补丁生效** 161 | 162 | ## 第五步 使用步骤 163 | TinkerPatch 的使用步骤非常简单,一般来说可以参考以下几个步骤: 164 | 165 | 1. 运行 `assembleRelease` task 构建基准包(**请在发布前确保更新tinkerpatchSupport中的appVersion**),tinkerPatch会基于你填入的`autoBackupApkPath`自动备份基础包信息到相应的文件夹,包含:apk文件、R.txt文件和mapping.txt文件 166 | (**注:mapping.txt是proguard的产物,如果你没有开启proguard则不会有这个文件**) 167 | 2. 若想发布补丁包, 只需将自动保存下来的文件分别填到`tinkerpatchSupport`中的`baseApkFile`、`baseProguardMappingFile`和`baseResourceRFile` 参数中; 168 | 3. 运行 `tinkerPatchRelease` task 构建补丁包,补丁包将位于 `build/outputs/tinkerPatch`下。 169 | 170 | ## 其他 171 | 172 | ### 1. 对Flavors的支持 173 | 174 | 在TinkerPatchSupport中添加如下字段, 如果你只是多渠道的需求,建议不要使用Flavor。多flavor必须在后台建立相应的基线工程(如下例子的命名规则为:appVersion_flavorName),每次生成补丁时也必须对应的生成多个分别上传。 175 | 176 | 这里增加了`tinkerPatchAllFlavorsDebug` 和 `tinkerPatchAllFlavorsRelease` 用于一次性生成所有flavors的Patch包。 177 | 178 | 具体可以参照[tinkerpatch-flavors-sample](https://github.com/TinkerPatch/tinkerpatch-flavors-sample)。 179 | 180 | **如果只是多渠道的需求,建议不要使用flavor的方式。首先其打包很慢,其次需要维护多个基线包,后期维护成本也很大。Tinker官方推荐 [packer-ng-plugin](https://github.com/mcxiaoke/packer-ng-plugin )或者 [walle](https://github.com/Meituan-Dianping/walle) 来进行多渠道打包,其中walle是支持最新的SchemaV2签名的。** 181 | 182 | ``` 183 | /** 若有编译多flavors需求,可在flavors中覆盖以下参数 184 | * 你也可以直接通过tinkerPatchAllFlavorDebug/tinkerPatchAllFlavorRelease, 一次编译所有的flavor补丁包 185 | * 注意的是:除非你不同的flavor代码是不一样的,不然建议采用zip comment或者文件方式生成渠道信息 186 | **/ 187 | productFlavors { 188 | flavor { 189 | flavorName = "flavor1" 190 | // 后台需要按照每个flavor的appVersion来建立独立的工程,并单独下发补丁 191 | appVersion = "${tinkerpatchSupport.appVersion}_${flavorName}" 192 | 193 | pathPrefix = "${bakPath}/${baseInfo}/${flavorName}${buildType}/" 194 | name = "${project.name}-${flavorName}${buildType}" 195 | 196 | baseApkFile = "${pathPrefix}/${name}.apk" 197 | baseProguardMappingFile = "${pathPrefix}/${name}-mapping.txt" 198 | baseResourceRFile = "${pathPrefix}/${name}-R.txt" 199 | } 200 | 201 | flavor { 202 | flavorName = "flavor2" 203 | // 后台需要按照每个flavor的appVersion来建立独立的工程,并单独下发补丁 204 | appVersion = "${tinkerpatchSupport.appVersion}_${flavorName}" 205 | 206 | pathPrefix = "${bakPath}/${baseInfo}/${flavorName}${buildType}/" 207 | name = "${project.name}-${flavorName}${buildType}" 208 | 209 | baseApkFile = "${pathPrefix}/${name}.apk" 210 | baseProguardMappingFile = "${pathPrefix}/${name}-mapping.txt" 211 | baseResourceRFile = "${pathPrefix}/${name}-R.txt" 212 | } 213 | } 214 | ``` 215 | 216 | ### 2. 对加固的支持 217 | 218 | 这里默认大家有同时生成加固渠道与非加固渠道的需求,如果只是单一需要加固,可以直接在配置中开启`protectedApp = true`即可。 219 | 220 | 可以参考tinkerpatch.gradle文件,具体工程可以参照[tinkerpatch-flavors-sample](https://github.com/TinkerPatch/tinkerpatch-flavors-sample): 221 | ``` 222 | productFlavors { 223 | flavor { 224 | flavorName = "protect" 225 | appVersion = "${tinkerpatchSupport.appVersion}_${flavorName}" 226 | 227 | pathPrefix = "${bakPath}/${baseInfo}/${flavorName}-${variantName}/" 228 | name = "${project.name}-${flavorName}-${variantName}" 229 | 230 | baseApkFile = "${pathPrefix}/${name}.apk" 231 | baseProguardMappingFile = "${pathPrefix}/${name}-mapping.txt" 232 | baseResourceRFile = "${pathPrefix}/${name}-R.txt" 233 | 234 | /** 开启加固开关,上传此flavor的apk到加固网站进行加固 **/ 235 | protectedApp = true 236 | } 237 | 238 | flavor { 239 | flavorName = "flavor1" 240 | appVersion = "${tinkerpatchSupport.appVersion}_${flavorName}" 241 | 242 | pathPrefix = "${bakPath}/${baseInfo}/${flavorName}-${variantName}/" 243 | name = "${project.name}-${flavorName}-${variantName}" 244 | 245 | baseApkFile = "${pathPrefix}/${name}.apk" 246 | baseProguardMappingFile = "${pathPrefix}/${name}-mapping.txt" 247 | baseResourceRFile = "${pathPrefix}/${name}-R.txt" 248 | } 249 | } 250 | ``` 251 | 252 | #### 加固步骤: 253 | 254 | 1. 生成开启`protectedApp = true`的基础包(这里假设此APK名为:`protected.apk`); 255 | 2. 上传`protected.apk`到相应的加固网站进行加固,并发布应用市场(请遵循各个加固网站步骤,一般为下载加固包-》重新签名-》发布重签名加固包); 256 | 3. 在tinkerPatch后台根据appVersion建立相应的App版本(比如这里2个flavor,就需要建立2个App版本。App版本即为各自flavor中配置的appVersion); 257 | 4. 基于各个flavor的基础包(**这里的基础包是第一步中生成的未加固的版本**)生成相应patch包,并上传至相应的App版本中,即完成补丁发布。 258 | 259 | **protectedApp=true, 这种模式仅仅可以使用在加固应用中** 260 | 261 | #### 支持列表: 262 | 263 | | 加固厂商 | 测试 | 264 | | --------| --------- | 265 | | 乐加固 | Tested | 266 | | 爱加密 | Tested | 267 | | 梆梆加固 | Tested | 268 | | 360加固 | Tested | 269 | | 其他 | 请自行测试,只要满足下面规则的都可以支持 | 270 | 271 | 这里是否支持加固,需要加固厂商明确以下两点: 272 | 273 | 1. 不能提前导入类; 274 | 2. 在art平台若要编译oat文件,需要将内联取消。 275 | 276 | ### 3. 重命名备份文件 277 | 278 | ``` 279 | /** 280 | * (可选)重命名备份文件的格式化字符串,默认为'${appName}-${variantName}' 281 | * 282 | * Available vars: 283 | * 1. projectName 284 | * 2. appName 285 | * 3. packageName 286 | * 4. buildType 287 | * 5. versionName 288 | * 6. versionCode 289 | * 7. buildTime 290 | * 8. fileSHA1 291 | * 9. flavorName 292 | * 10. variantName 293 | * 294 | * default value: '${appName}-${variantName}' 295 | * Note: plz use single-quotation wrapping this format string 296 | ** / 297 | backupFileNameFormat = '${appName}-${variantName}' 298 | ``` 299 | 300 | ### 4. 对新增Activity的支持 301 | 302 | 基础包必须设置`supportComponent=true` 303 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | ## Project-wide Gradle settings. 2 | # 3 | # For more details on how to configure your build environment visit 4 | # http://www.gradle.org/docs/current/userguide/build_environment.html 5 | # 6 | # Specifies the JVM arguments used for the daemon process. 7 | # The setting is particularly useful for tweaking memory settings. 8 | # Default value: -Xmx1024m -XX:MaxPermSize=256m 9 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 10 | # 11 | # When configured, Gradle will run in incubating parallel mode. 12 | # This option should only be used with decoupled projects. More details, visit 13 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 14 | # org.gradle.parallel=true 15 | #Thu Dec 08 22:37:35 CST 2016 16 | POM_DEVELOPER_EMAIL=sunsj1231@gmail.com 17 | POM_DEVELOPER_ID=simsun 18 | MIN_SDK_VERSION=10 19 | org.gradle.daemon=true 20 | SUPPORT_LIB_VERSION=25.0.0 21 | POM_DEVELOPER_NAME=Shengjie Sun 22 | POM_DESCRIPTION=TinkerPatch SDK 23 | OKHTTP3_VERSION=3.4.1 24 | OKHTTP_VERSION=2.7.5 25 | org.gradle.jvmargs=-Xmx2048M 26 | FINDBUGS_VERSION=3.0.1 27 | org.gradle.configureondemand=true 28 | BUILD_TOOLS_VERSION=24.0.3 29 | VERSION_NAME=0.3.6 30 | TARGET_SDK_VERSION=24 31 | TINKER_VERSION=1.7.6 32 | POM_URL=https\://github.com/TinkerPatch/tinkerpatch-sdk 33 | COMPILE_SDK_VERSION=24 34 | GROUP=com.tencent.tinker 35 | GIT_URL=https\://github.com/TinkerPatch/tinkerpatch-sdk.git 36 | -------------------------------------------------------------------------------- /gradle/bintray.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.jfrog.bintray' 2 | apply plugin: 'maven-publish' 3 | 4 | version = libraryVersion 5 | project.archivesBaseName = ext.artifact 6 | 7 | def isAndroidProject = project.plugins.hasPlugin('com.android.application') || project.plugins.hasPlugin('com.android.library') 8 | if (isAndroidProject) { 9 | def releaseVariants = project.android.libraryVariants.findAll { 10 | it.buildType.name.equalsIgnoreCase('release') 11 | } 12 | 13 | task androidJavadocs(type: Javadoc, dependsOn: 'compileReleaseJavaWithJavac') { 14 | source = releaseVariants.collect { it.javaCompile.source } 15 | classpath = files(releaseVariants.collect { 16 | files(it.javaCompile.classpath.files, project.android.bootClasspath) 17 | }) 18 | 19 | options { 20 | encoding "UTF-8" 21 | charSet 'UTF-8' 22 | title "$libraryName $libraryVersion" 23 | author true 24 | version true 25 | links('http://docs.oracle.com/javase/7/docs/api/') 26 | linksOffline('http://d.android.com/reference', "${android.sdkDirectory}/docs/reference") 27 | } 28 | 29 | exclude '**/BuildConfig.java' 30 | exclude '**/R.java' 31 | } 32 | 33 | task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { 34 | classifier = 'javadoc' 35 | from androidJavadocs.destinationDir 36 | baseName "${project.name}${JAR_POSTFIX}" 37 | } 38 | 39 | task androidSourcesJar(type: Jar) { 40 | classifier = 'sources' 41 | from project.android.sourceSets.main.java.source 42 | baseName "${project.name}${JAR_POSTFIX}" 43 | } 44 | 45 | 46 | artifacts { 47 | archives androidSourcesJar 48 | archives androidJavadocsJar 49 | } 50 | 51 | publishing { 52 | publications { 53 | TinkerServer(MavenPublication) { 54 | groupId = group 55 | artifactId = project.getName() 56 | version = version 57 | // Tell maven to prepare the generated "*.aar" file for publishing 58 | artifact("$buildDir/outputs/aar/${project.getName()}-release.aar") 59 | artifact androidJavadocsJar 60 | } 61 | } 62 | } 63 | 64 | } else if (project.plugins.hasPlugin('java')) { // Java libraries 65 | println "[bintray.gradle]java project" 66 | task sourcesJar(type: Jar, dependsOn: classes) { 67 | classifier = 'sources' 68 | from sourceSets.main.allSource 69 | } 70 | task javadocsJar(type: Jar, dependsOn: javadoc) { 71 | classifier = 'javadoc' 72 | from javadoc.destinationDir 73 | } 74 | artifacts { 75 | archives sourcesJar 76 | archives javadocsJar 77 | } 78 | 79 | publishing { 80 | publications { 81 | TinkerServer(MavenPublication) { 82 | from components.java 83 | groupId = group 84 | artifactId = project.getName() 85 | version = version 86 | } 87 | } 88 | } 89 | } 90 | 91 | task buildAndPublishLocalMaven(dependsOn: ['build', 'publishTinkerServerPublicationToMavenLocal']) {} 92 | 93 | logger.info("Published artifacts in ${configurations.archives}:") 94 | configurations.archives.artifacts.files.files.each { logger.info("\t$it"); } 95 | 96 | Properties properties = new Properties() 97 | try { 98 | properties.load(project.rootProject.file('local.properties').newDataInputStream()) 99 | } catch (ignored) { 100 | properties.setProperty("bintray.user", "user") 101 | properties.setProperty("bintray.apikey", "key") 102 | properties.setProperty("bintray.gpg.password", "pwd") 103 | } 104 | 105 | bintray { 106 | user = properties.getProperty("bintray.user") 107 | key = properties.getProperty("bintray.apikey") 108 | 109 | configurations = ['archives'] 110 | 111 | pkg { 112 | repo = bintrayRepo 113 | name = bintrayName 114 | desc = libraryDescription 115 | websiteUrl = siteUrl 116 | vcsUrl = gitUrl 117 | licenses = allLicenses 118 | publish = true 119 | dryRun = false 120 | publicDownloadNumbers = true 121 | version { 122 | desc = libraryDescription 123 | gpg { 124 | sign = true //Determines whether to GPG sign the files. The default is false 125 | passphrase = properties.getProperty("bintray.gpg.password") 126 | //Optional. The passphrase for GPG signing' 127 | } 128 | } 129 | } 130 | } 131 | 132 | //https://www.jfrog.com/confluence/display/RTF/Gradle+Artifactory+Plugin 133 | // for SNAPSHOT VERSION 134 | 135 | //apply plugin: "com.jfrog.artifactory" 136 | //artifactory { 137 | // contextUrl = 'http://oss.jfrog.org/artifactory' //The base Artifactory URL if not overridden by the publisher/resolver 138 | // resolve { 139 | // repository { 140 | // repoKey = 'libs-release' 141 | // } 142 | // } 143 | // publish { 144 | // repository { 145 | // repoKey = 'oss-snapshot-local' //The Artifactory repository key to publish to 146 | // username = bintray.user 147 | // password = bintray.key 148 | // maven = true 149 | // } 150 | // defaults { 151 | // //这里的名字和前面bintray.configurations的值一致即可,会将其包含的输出上传到jfrog上去 152 | // publishConfigs('archives') 153 | // } 154 | // } 155 | //} -------------------------------------------------------------------------------- /gradle/install.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.github.dcendents.android-maven' 2 | 3 | group = publishedGroupId // Maven Group ID for the artifact 4 | project.archivesBaseName = ext.artifact 5 | 6 | install { 7 | repositories.mavenInstaller { 8 | // This generates POM.xml with proper parameters 9 | pom.project { 10 | packaging packageMethod 11 | groupId publishedGroupId 12 | artifactId artifact 13 | 14 | // Add your description here 15 | name libraryName 16 | description libraryDescription 17 | url siteUrl 18 | 19 | // Set your license 20 | licenses { 21 | license { 22 | name licenseName 23 | url licenseUrl 24 | } 25 | } 26 | developers { 27 | developer { 28 | id developerId 29 | name developerName 30 | email developerEmail 31 | } 32 | } 33 | scm { 34 | connection gitUrl 35 | developerConnection gitUrl 36 | url siteUrl 37 | 38 | } 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TinkerPatch/tinkerpatch-sdk/68c54b5118b0a3bb65fad28a991d49f5975308a9/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 | -------------------------------------------------------------------------------- /integration/build.gradle: -------------------------------------------------------------------------------- 1 | // keep an empty file to make sure Gradle recognizes the properties -------------------------------------------------------------------------------- /integration/gradle.properties: -------------------------------------------------------------------------------- 1 | # Postfix for source and javadoc jars. 2 | JAR_POSTFIX=-integration -------------------------------------------------------------------------------- /integration/okhttp/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | ext { 4 | bintrayRepo = 'maven' 5 | bintrayName = POM_NAME 6 | 7 | publishedGroupId = GROUP 8 | libraryName = POM_NAME 9 | artifact = POM_ARTIFACT_ID 10 | 11 | libraryDescription = POM_DESCRIPTION 12 | 13 | siteUrl = POM_URL 14 | gitUrl = GIT_URL 15 | 16 | libraryVersion = VERSION_NAME 17 | 18 | developerId = POM_DEVELOPER_ID 19 | developerName = POM_DEVELOPER_NAME 20 | developerEmail = POM_DEVELOPER_EMAIL 21 | 22 | packageMethod = POM_PACKAGING 23 | 24 | licenseName = 'The MIT License (MIT)' 25 | licenseUrl = 'https://opensource.org/licenses/mit-license.php' 26 | allLicenses = ["MIT"] 27 | } 28 | 29 | dependencies { 30 | compile project(':tkclient') 31 | compile "com.squareup.okhttp:okhttp:${OKHTTP_VERSION}" 32 | } 33 | 34 | android { 35 | compileSdkVersion COMPILE_SDK_VERSION as int 36 | buildToolsVersion BUILD_TOOLS_VERSION as String 37 | 38 | defaultConfig { 39 | minSdkVersion MIN_SDK_VERSION as int 40 | targetSdkVersion TARGET_SDK_VERSION as int 41 | 42 | versionName VERSION_NAME as String 43 | } 44 | 45 | compileOptions { 46 | sourceCompatibility JavaVersion.VERSION_1_7 47 | targetCompatibility JavaVersion.VERSION_1_7 48 | } 49 | 50 | lintOptions { 51 | warning 'InvalidPackage' 52 | } 53 | } 54 | 55 | apply from: "${rootProject.projectDir}/gradle/install.gradle" 56 | apply from: "${rootProject.projectDir}/gradle/bintray.gradle" 57 | 58 | -------------------------------------------------------------------------------- /integration/okhttp/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=TinkerClient-okhttp-integration 2 | POM_ARTIFACT_ID=okhttp-integration 3 | POM_PACKAGING=aar 4 | POM_DESCRIPTION=An integration library to use OkHttp 2.x to fetch patch from tinker server -------------------------------------------------------------------------------- /integration/okhttp/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 8 | 9 | -------------------------------------------------------------------------------- /integration/okhttp/src/main/java/com/xmonster/tkclient/integration/okhttp/OkHttpStreamFetcher.java: -------------------------------------------------------------------------------- 1 | package com.xmonster.tkclient.integration.okhttp; 2 | 3 | 4 | import android.util.Log; 5 | 6 | import com.squareup.okhttp.MediaType; 7 | import com.squareup.okhttp.OkHttpClient; 8 | import com.squareup.okhttp.Request; 9 | import com.squareup.okhttp.RequestBody; 10 | import com.squareup.okhttp.Response; 11 | import com.squareup.okhttp.ResponseBody; 12 | import com.xmonster.tkclient.model.DataFetcher; 13 | import com.xmonster.tkclient.model.TKClientUrl; 14 | import com.xmonster.tkclient.utils.ContentLengthInputStream; 15 | 16 | import java.io.IOException; 17 | import java.io.InputStream; 18 | import java.util.Map; 19 | 20 | 21 | /** 22 | * Fetches an {@link InputStream} using the okhttp library. 23 | */ 24 | public class OkHttpStreamFetcher implements DataFetcher { 25 | private static final String TAG = "OkHttpFetcher"; 26 | static MediaType mediaTypeJson = MediaType.parse("application/json; charset=utf-8"); 27 | 28 | private final OkHttpClient client; 29 | ResponseBody responseBody; 30 | InputStream stream; 31 | private TKClientUrl tkUrl; 32 | 33 | public OkHttpStreamFetcher(OkHttpClient client, TKClientUrl tkUrl) { 34 | this.client = client; 35 | this.tkUrl = tkUrl; 36 | } 37 | 38 | @Override 39 | public void loadData(final DataCallback callback) { 40 | Request.Builder requestBuilder = new Request.Builder().url(tkUrl.toStringUrl()); 41 | switch (tkUrl.getMethod()) { 42 | case "GET": 43 | requestBuilder = requestBuilder.get(); 44 | break; 45 | case "POST": 46 | RequestBody requestBody = RequestBody.create(mediaTypeJson, tkUrl.getBody()); 47 | requestBuilder = requestBuilder.post(requestBody); 48 | break; 49 | default: 50 | throw new RuntimeException("Unsupported request Method" + tkUrl.getMethod()); 51 | } 52 | for (Map.Entry headerEntry : tkUrl.getHeaders().entrySet()) { 53 | String key = headerEntry.getKey(); 54 | requestBuilder.addHeader(key, headerEntry.getValue()); 55 | } 56 | Request request = requestBuilder.build(); 57 | 58 | client.newCall(request).enqueue(new com.squareup.okhttp.Callback() { 59 | @Override 60 | public void onFailure(Request request, IOException e) { 61 | Log.d(TAG, "OkHttp failed to obtain result", e); 62 | callback.onLoadFailed(e); 63 | } 64 | 65 | @Override 66 | public void onResponse(Response response) throws IOException { 67 | responseBody = response.body(); 68 | if (response.isSuccessful()) { 69 | long contentLength = response.body().contentLength(); 70 | stream = ContentLengthInputStream.obtain(responseBody.byteStream(), contentLength); 71 | } else { 72 | Log.d(TAG, "OkHttp got error response: " + response.code() + ", " + response.message()); 73 | } 74 | callback.onDataReady(stream); 75 | } 76 | }); 77 | } 78 | 79 | @Override 80 | public void cleanup() { 81 | try { 82 | if (stream != null) { 83 | stream.close(); 84 | } 85 | } catch (IOException e) { 86 | // Ignored 87 | } 88 | if (responseBody != null) { 89 | try { 90 | responseBody.close(); 91 | } catch (IOException e) { 92 | // Ignored. 93 | } 94 | } 95 | } 96 | 97 | @Override 98 | public void cancel() { 99 | 100 | } 101 | 102 | @Override 103 | public Class getDataClass() { 104 | return InputStream.class; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /integration/okhttp/src/main/java/com/xmonster/tkclient/integration/okhttp/OkHttpTKClientModule.java: -------------------------------------------------------------------------------- 1 | package com.xmonster.tkclient.integration.okhttp; 2 | 3 | import android.content.Context; 4 | 5 | import com.xmonster.tkclient.Registry; 6 | import com.xmonster.tkclient.model.TKClientUrl; 7 | import com.xmonster.tkclient.module.TKClientModule; 8 | 9 | import java.io.InputStream; 10 | 11 | /** 12 | * A {@link TKClientModule} implementation to replace default 13 | * {@link java.net.HttpURLConnection} based {@link com.xmonster.tkclient.model.RequestLoader} 14 | * with an OkHttp based {@link com.xmonster.tkclient.model.RequestLoader}. 15 | *

16 | *

If you're using gradle, you can include this module simply by depending on the aar, the 17 | * module will be merged in by manifest merger. For other build systems or for more more 18 | * information, see {@link TKClientModule}.

19 | */ 20 | public class OkHttpTKClientModule implements TKClientModule { 21 | 22 | @Override 23 | public void register(Context context, Registry registry) { 24 | registry.register(TKClientUrl.class, InputStream.class, new OkHttpUrlLoader.Factory()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /integration/okhttp/src/main/java/com/xmonster/tkclient/integration/okhttp/OkHttpUrlLoader.java: -------------------------------------------------------------------------------- 1 | package com.xmonster.tkclient.integration.okhttp; 2 | 3 | import com.squareup.okhttp.OkHttpClient; 4 | import com.xmonster.tkclient.model.DataFetcher; 5 | import com.xmonster.tkclient.model.RequestLoader; 6 | import com.xmonster.tkclient.model.RequestLoaderFactory; 7 | import com.xmonster.tkclient.model.TKClientUrl; 8 | 9 | import java.io.InputStream; 10 | 11 | /** 12 | * A simple model loader for fetching media over http/https using OkHttp. 13 | */ 14 | public class OkHttpUrlLoader implements RequestLoader { 15 | 16 | private final OkHttpClient client; 17 | 18 | public OkHttpUrlLoader(OkHttpClient client) { 19 | this.client = client; 20 | } 21 | 22 | @Override 23 | public DataFetcher buildLoadData(TKClientUrl tkUrl) { 24 | return new OkHttpStreamFetcher(client, tkUrl); 25 | } 26 | 27 | /** 28 | * The default factory for {@link OkHttpUrlLoader}s. 29 | */ 30 | public static class Factory implements RequestLoaderFactory { 31 | private static volatile OkHttpClient internalClient; 32 | private OkHttpClient client; 33 | 34 | /** 35 | * Constructor for a new Factory that runs requests using a static singleton client. 36 | */ 37 | public Factory() { 38 | this(getInternalClient()); 39 | } 40 | 41 | /** 42 | * Constructor for a new Factory that runs requests using given client. 43 | */ 44 | public Factory(OkHttpClient client) { 45 | this.client = client; 46 | } 47 | 48 | private static OkHttpClient getInternalClient() { 49 | if (internalClient == null) { 50 | synchronized (Factory.class) { 51 | if (internalClient == null) { 52 | internalClient = new OkHttpClient(); 53 | } 54 | } 55 | } 56 | return internalClient; 57 | } 58 | 59 | @Override 60 | public RequestLoader build() { 61 | return new OkHttpUrlLoader(client); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /integration/okhttp3/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | ext { 4 | bintrayRepo = 'maven' 5 | bintrayName = POM_NAME 6 | 7 | publishedGroupId = GROUP 8 | libraryName = POM_NAME 9 | artifact = POM_ARTIFACT_ID 10 | 11 | libraryDescription = POM_DESCRIPTION 12 | 13 | siteUrl = POM_URL 14 | gitUrl = GIT_URL 15 | 16 | libraryVersion = VERSION_NAME 17 | 18 | developerId = POM_DEVELOPER_ID 19 | developerName = POM_DEVELOPER_NAME 20 | developerEmail = POM_DEVELOPER_EMAIL 21 | 22 | packageMethod = POM_PACKAGING 23 | 24 | licenseName = 'The MIT License (MIT)' 25 | licenseUrl = 'https://opensource.org/licenses/mit-license.php' 26 | allLicenses = ["MIT"] 27 | } 28 | 29 | dependencies { 30 | compile project(':tkclient') 31 | compile "com.squareup.okhttp3:okhttp:${OKHTTP3_VERSION}" 32 | } 33 | 34 | android { 35 | compileSdkVersion COMPILE_SDK_VERSION as int 36 | buildToolsVersion BUILD_TOOLS_VERSION as String 37 | 38 | defaultConfig { 39 | minSdkVersion MIN_SDK_VERSION as int 40 | targetSdkVersion TARGET_SDK_VERSION as int 41 | 42 | versionName VERSION_NAME as String 43 | } 44 | 45 | lintOptions { 46 | warning 'InvalidPackage' 47 | } 48 | } 49 | 50 | apply from: "${rootProject.projectDir}/gradle/install.gradle" 51 | apply from: "${rootProject.projectDir}/gradle/bintray.gradle" 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /integration/okhttp3/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=TinkerClient-okhttp3-integration 2 | POM_ARTIFACT_ID=okhttp3-integration 3 | POM_PACKAGING=aar 4 | POM_DESCRIPTION=An integration library to use OkHttp 3.x to fetch patch from tinker server -------------------------------------------------------------------------------- /integration/okhttp3/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 8 | 9 | -------------------------------------------------------------------------------- /integration/okhttp3/src/main/java/com/xmonster/tkclient/integration/okhttp3/OkHttp3StreamFetcher.java: -------------------------------------------------------------------------------- 1 | package com.xmonster.tkclient.integration.okhttp3; 2 | 3 | 4 | import android.util.Log; 5 | 6 | import com.xmonster.tkclient.model.DataFetcher; 7 | import com.xmonster.tkclient.model.TKClientUrl; 8 | import com.xmonster.tkclient.utils.ContentLengthInputStream; 9 | 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | import java.util.Map; 13 | 14 | import okhttp3.Call; 15 | import okhttp3.MediaType; 16 | import okhttp3.Request; 17 | import okhttp3.RequestBody; 18 | import okhttp3.Response; 19 | import okhttp3.ResponseBody; 20 | 21 | /** 22 | * Fetches an {@link InputStream} using the okhttp3 library. 23 | */ 24 | public class OkHttp3StreamFetcher implements DataFetcher { 25 | private static final String TAG = "OkHttp3Fetcher"; 26 | public static MediaType mediaTypeJson = MediaType.parse("application/json; charset=utf-8"); 27 | 28 | private final Call.Factory client; 29 | private volatile Call call; 30 | ResponseBody responseBody; 31 | InputStream stream; 32 | private TKClientUrl tkUrl; 33 | 34 | public OkHttp3StreamFetcher(Call.Factory client, TKClientUrl tkUrl) { 35 | this.client = client; 36 | this.tkUrl = tkUrl; 37 | } 38 | 39 | @Override 40 | public void loadData(final DataCallback callback) { 41 | Request.Builder requestBuilder = new Request.Builder().url(tkUrl.toStringUrl()); 42 | switch (tkUrl.getMethod()) { 43 | case "GET": 44 | requestBuilder = requestBuilder.get(); 45 | break; 46 | case "POST": 47 | RequestBody requestBody = RequestBody.create(mediaTypeJson, tkUrl.getBody()); 48 | requestBuilder = requestBuilder.post(requestBody); 49 | break; 50 | default: 51 | throw new RuntimeException("Unsupported request Method" + tkUrl.getMethod()); 52 | } 53 | for (Map.Entry headerEntry : tkUrl.getHeaders().entrySet()) { 54 | String key = headerEntry.getKey(); 55 | requestBuilder.addHeader(key, headerEntry.getValue()); 56 | } 57 | Request request = requestBuilder.build(); 58 | 59 | call = client.newCall(request); 60 | call.enqueue(new okhttp3.Callback() { 61 | @Override 62 | public void onFailure(Call call, IOException e) { 63 | Log.d(TAG, "OkHttp3 failed to obtain result", e); 64 | callback.onLoadFailed(e); 65 | } 66 | 67 | @Override 68 | public void onResponse(Call call, Response response) throws IOException { 69 | responseBody = response.body(); 70 | if (response.isSuccessful()) { 71 | long contentLength = response.body().contentLength(); 72 | Log.d(TAG, "OkHttp3 got success response: " + response.code() + ", " + response.message()); 73 | stream = ContentLengthInputStream.obtain(responseBody.byteStream(), contentLength); 74 | } else { 75 | Log.d(TAG, "OkHttp3 got error response: " + response.code() + ", " + response.message()); 76 | } 77 | callback.onDataReady(stream); 78 | } 79 | }); 80 | } 81 | 82 | @Override 83 | public void cleanup() { 84 | try { 85 | if (stream != null) { 86 | stream.close(); 87 | } 88 | } catch (Exception e) { 89 | e.printStackTrace(); 90 | } 91 | if (responseBody != null) { 92 | responseBody.close(); 93 | } 94 | } 95 | 96 | @Override 97 | public void cancel() { 98 | Call local = call; 99 | if (local != null) { 100 | local.cancel(); 101 | } 102 | } 103 | 104 | @Override 105 | public Class getDataClass() { 106 | return InputStream.class; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /integration/okhttp3/src/main/java/com/xmonster/tkclient/integration/okhttp3/OkHttp3TKClientModule.java: -------------------------------------------------------------------------------- 1 | package com.xmonster.tkclient.integration.okhttp3; 2 | 3 | import android.content.Context; 4 | 5 | import com.xmonster.tkclient.Registry; 6 | import com.xmonster.tkclient.model.TKClientUrl; 7 | import com.xmonster.tkclient.module.TKClientModule; 8 | 9 | import java.io.InputStream; 10 | 11 | /** 12 | * A {@link com.xmonster.tkclient.module.TKClientModule} implementation to replace default 13 | * {@link java.net.HttpURLConnection} based {@link com.xmonster.tkclient.model.RequestLoader} 14 | * with an OkHttp3 based {@link com.xmonster.tkclient.model.RequestLoader}. 15 | *

16 | *

If you're using gradle, you can include this module simply by depending on the aar, the 17 | * module will be merged in by manifest merger. For other build systems or for more more 18 | * information, see {@link com.xmonster.tkclient.module.TKClientModule}.

19 | */ 20 | public class OkHttp3TKClientModule implements TKClientModule { 21 | 22 | @Override 23 | public void register(Context context, Registry registry) { 24 | registry.register(TKClientUrl.class, InputStream.class, new OkHttp3UrlLoader.Factory()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /integration/okhttp3/src/main/java/com/xmonster/tkclient/integration/okhttp3/OkHttp3UrlLoader.java: -------------------------------------------------------------------------------- 1 | package com.xmonster.tkclient.integration.okhttp3; 2 | 3 | import com.xmonster.tkclient.model.DataFetcher; 4 | import com.xmonster.tkclient.model.RequestLoader; 5 | import com.xmonster.tkclient.model.RequestLoaderFactory; 6 | import com.xmonster.tkclient.model.TKClientUrl; 7 | 8 | import java.io.InputStream; 9 | 10 | import okhttp3.Call; 11 | import okhttp3.OkHttpClient; 12 | 13 | /** 14 | * A simple model loader for fetching media over http/https using OkHttp. 15 | */ 16 | public class OkHttp3UrlLoader implements RequestLoader { 17 | 18 | private final Call.Factory client; 19 | 20 | public OkHttp3UrlLoader(Call.Factory client) { 21 | this.client = client; 22 | } 23 | 24 | @Override 25 | public DataFetcher buildLoadData(TKClientUrl tkUrl) { 26 | return new OkHttp3StreamFetcher(client, tkUrl); 27 | } 28 | 29 | /** 30 | * The default factory for {@link OkHttp3UrlLoader}s. 31 | */ 32 | public static class Factory implements RequestLoaderFactory { 33 | private static volatile Call.Factory internalClient; 34 | private Call.Factory client; 35 | 36 | /** 37 | * Constructor for a new Factory that runs requests using a static singleton client. 38 | */ 39 | public Factory() { 40 | this(getInternalClient()); 41 | } 42 | 43 | /** 44 | * Constructor for a new Factory that runs requests using given client. 45 | * 46 | * @param client this is typically an instance of {@code OkHttpClient}. 47 | */ 48 | public Factory(Call.Factory client) { 49 | this.client = client; 50 | } 51 | 52 | private static Call.Factory getInternalClient() { 53 | if (internalClient == null) { 54 | synchronized (Factory.class) { 55 | if (internalClient == null) { 56 | internalClient = new OkHttpClient(); 57 | } 58 | } 59 | } 60 | return internalClient; 61 | } 62 | 63 | @Override 64 | public RequestLoader build() { 65 | return new OkHttp3UrlLoader(client); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':tinkerpatch-sdk' 2 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'findbugs' 3 | apply plugin: 'pmd' 4 | apply plugin: 'maven' 5 | 6 | ext { 7 | bintrayRepo = 'maven' 8 | bintrayName = POM_NAME 9 | 10 | publishedGroupId = GROUP 11 | libraryName = POM_NAME 12 | artifact = POM_ARTIFACT_ID 13 | 14 | libraryDescription = POM_DESCRIPTION 15 | 16 | siteUrl = POM_URL 17 | gitUrl = GIT_URL 18 | 19 | libraryVersion = VERSION_NAME 20 | 21 | developerId = POM_DEVELOPER_ID 22 | developerName = POM_DEVELOPER_NAME 23 | developerEmail = POM_DEVELOPER_EMAIL 24 | 25 | packageMethod = POM_PACKAGING 26 | 27 | licenseName = 'The MIT License (MIT)' 28 | licenseUrl = 'https://opensource.org/licenses/mit-license.php' 29 | allLicenses = ["MIT"] 30 | } 31 | 32 | android { 33 | compileSdkVersion COMPILE_SDK_VERSION as int 34 | buildToolsVersion BUILD_TOOLS_VERSION as String 35 | 36 | defaultConfig { 37 | consumerProguardFiles 'proguard-rules.txt' 38 | } 39 | 40 | lintOptions { 41 | abortOnError false 42 | } 43 | } 44 | 45 | dependencies { 46 | compile fileTree(dir: 'libs', include: ['*.jar']) 47 | 48 | compile ("com.tencent.tinker:tinker-android-lib:${TINKER_VERSION}") { changing = true } 49 | compile ("com.tencent.tinker:tinker-android-loader:${TINKER_VERSION}") { changing = true } 50 | compile ("com.tencent.tinker:aosp-dexutils:${TINKER_VERSION}") { changing = true } 51 | compile ("com.tencent.tinker:bsdiff-util:${TINKER_VERSION}") { changing = true } 52 | compile ("com.tencent.tinker:tinker-commons:${TINKER_VERSION}") { changing = true } 53 | 54 | testCompile 'junit:junit:4.12' 55 | testCompile "org.powermock:powermock-module-junit4:1.6.2" 56 | testCompile "org.powermock:powermock-module-junit4-rule:1.6.2" 57 | testCompile "org.powermock:powermock-api-mockito:1.6.2" 58 | testCompile "org.powermock:powermock-classloading-xstream:1.6.2" 59 | } 60 | 61 | task buildSdk(type: Copy, dependsOn: [build]) { 62 | from("$buildDir/outputs/aar/") { 63 | include "${project.getName()}-release.aar" 64 | } 65 | 66 | into(rootProject.file("buildSdk/android/")) 67 | rename { String fileName -> 68 | fileName.replace("release", "${version}") 69 | } 70 | } 71 | 72 | afterEvaluate { 73 | task findbugs(type: FindBugs, dependsOn: "assembleDebug") { 74 | 75 | description 'Run findbugs' 76 | group 'verification' 77 | 78 | classes = fileTree('build/intermediates/classes/debug/') 79 | source = fileTree('src/main/java') 80 | classpath = project.configurations.compile 81 | 82 | effort = 'max' 83 | 84 | excludeFilter = file("findbugs-exclude.xml") 85 | 86 | reports { 87 | xml.enabled = false 88 | html.enabled = true 89 | } 90 | } 91 | 92 | check.dependsOn('findbugs') 93 | 94 | pmd { 95 | toolVersion '5.4.0' 96 | } 97 | 98 | task pmd(type: Pmd) { 99 | targetJdk = TargetJdk.VERSION_1_7 100 | 101 | description 'Run pmd' 102 | group 'verification' 103 | 104 | // If ruleSets is not empty, it seems to contain some 105 | // defaults which override rules in the ruleset file... 106 | ruleSets = [] 107 | ruleSetFiles = files('pmd-ruleset.xml') 108 | source = fileTree('src/main/java') 109 | 110 | reports { 111 | xml.enabled = false 112 | html.enabled = true 113 | } 114 | } 115 | 116 | check.dependsOn('pmd') 117 | } 118 | 119 | apply from: "${rootProject.projectDir}/gradle/install.gradle" 120 | apply from: "${rootProject.projectDir}/gradle/bintray.gradle" 121 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/findbugs-exclude.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_ARTIFACT_ID=tinkerpatch-sdk 2 | POM_NAME=tinkerpatch-sdk 3 | POM_PACKAGING=jar 4 | # Postfix for source and javadoc jars. 5 | JAR_POSTFIX= 6 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/pmd-ruleset.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | This ruleset was created from PMD.rul 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 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/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 /Users/zhangshaowen/Library/Android/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 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/proguard-rules.txt: -------------------------------------------------------------------------------- 1 | -keep public class * implements com.xmonster.tkclient.module.TKClientModule 2 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/src/main/java/com/tencent/tinker/app/TinkerManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Shengjie Sim Sun 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package com.tencent.tinker.app; 25 | 26 | import com.tencent.tinker.app.reporter.TinkerServerLoadReporter; 27 | import com.tencent.tinker.app.reporter.TinkerServerPatchListener; 28 | import com.tencent.tinker.app.service.TinkerServerResultService; 29 | import com.tencent.tinker.lib.listener.PatchListener; 30 | import com.tencent.tinker.lib.patch.AbstractPatch; 31 | import com.tencent.tinker.lib.patch.UpgradePatch; 32 | import com.tencent.tinker.lib.reporter.DefaultPatchReporter; 33 | import com.tencent.tinker.lib.reporter.LoadReporter; 34 | import com.tencent.tinker.lib.reporter.PatchReporter; 35 | import com.tencent.tinker.lib.tinker.TinkerInstaller; 36 | import com.tencent.tinker.lib.util.TinkerLog; 37 | import com.tencent.tinker.loader.app.ApplicationLike; 38 | 39 | public final class TinkerManager { 40 | private static final String TAG = "Tinker.TinkerManager"; 41 | private static boolean isInstalled = false; 42 | 43 | private TinkerManager() { 44 | //Utils 45 | } 46 | /** 47 | * you can specify all class you want. 48 | * sometimes, you can only install tinker in some process you want! 49 | * 50 | * @param appLike ApplicationLike 51 | */ 52 | public static void installTinker(ApplicationLike appLike) { 53 | if (isInstalled) { 54 | TinkerLog.w(TAG, "install tinker, but has installed, ignore"); 55 | return; 56 | } 57 | //or you can just use DefaultLoadReporter 58 | LoadReporter loadReporter = new TinkerServerLoadReporter(appLike.getApplication()); 59 | //or you can just use DefaultPatchReporter 60 | PatchReporter patchReporter = new DefaultPatchReporter(appLike.getApplication()); 61 | //or you can just use DefaultPatchListener 62 | PatchListener patchListener = new TinkerServerPatchListener(appLike.getApplication()); 63 | //you can set your own upgrade patch if you need 64 | AbstractPatch upgradePatchProcessor = new UpgradePatch(); 65 | TinkerInstaller.install(appLike, 66 | loadReporter, patchReporter, patchListener, 67 | TinkerServerResultService.class, upgradePatchProcessor 68 | ); 69 | 70 | isInstalled = true; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/src/main/java/com/tencent/tinker/app/TinkerServerManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Shengjie Sim Sun 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.tencent.tinker.app; 26 | 27 | import android.content.Context; 28 | import android.os.Looper; 29 | import android.os.MessageQueue; 30 | 31 | import com.tencent.tinker.app.callback.TinkerServerPatchRequestCallback; 32 | import com.tencent.tinker.lib.service.PatchResult; 33 | import com.tencent.tinker.lib.tinker.Tinker; 34 | import com.tencent.tinker.lib.util.TinkerLog; 35 | import com.tencent.tinker.loader.shareutil.ShareConstants; 36 | import com.tencent.tinker.loader.shareutil.SharePatchFileUtil; 37 | import com.tencent.tinker.server.TinkerServerClient; 38 | import com.tencent.tinker.server.client.ConfigRequestCallback; 39 | import com.tencent.tinker.server.client.DefaultPatchRequestCallback; 40 | import com.tencent.tinker.server.client.PatchRequestCallback; 41 | import com.tencent.tinker.server.utils.Debugger; 42 | 43 | import org.json.JSONException; 44 | import org.json.JSONObject; 45 | 46 | import java.io.File; 47 | import java.util.HashMap; 48 | import java.util.Iterator; 49 | 50 | public class TinkerServerManager { 51 | 52 | private static final String TAG = "Tinker.ServerManager"; 53 | private static final String CONDITION_CHANNEL = "channel"; 54 | 55 | static TinkerServerClient sTinkerServerClient; 56 | static String channel; 57 | 58 | /** 59 | * 初始化 TinkerServer 实例 60 | * @param context context 61 | * @param tinker {@link Tinker} 实例 62 | * @param hours 访问服务器的时间间隔, 单位为小时, 应为 >= 0 63 | * @param appKey 从Tinkerpatch中得到的appKey 64 | * @param appVersion 在Tinkerpatch中填写的appVersion 65 | * @param channel 发布的渠道名称,由于GooglePlay渠道的政策限制,我们会停止所有channel中含有google关键字的动态下发功能。 66 | */ 67 | public static void installTinkerServer( 68 | Context context, 69 | Tinker tinker, 70 | int hours, 71 | String appKey, 72 | String appVersion, 73 | String channel 74 | ) { 75 | installTinkerServer( 76 | context, 77 | tinker, 78 | hours, 79 | appKey, 80 | appVersion, 81 | channel, 82 | new TinkerServerPatchRequestCallback() 83 | ); 84 | } 85 | 86 | /** 87 | * 初始化 TinkerServer 实例 88 | * @param context context 89 | * @param tinker {@link Tinker} 实例 90 | * @param hours 访问服务器的时间间隔, 单位为小时, 应为 >= 0 91 | * @param appKey 从Tinkerpatch中得到的appKey 92 | * @param appVersion 在Tinkerpatch中填写的appVersion 93 | * @param channel 发布的渠道名称,由于GooglePlay渠道的政策限制,我们会停止所有channel中含有google关键字的动态下发功能。 94 | * @param patchRequestCallback {@link PatchRequestCallback} patch请求的callback 95 | */ 96 | public static void installTinkerServer( 97 | Context context, 98 | Tinker tinker, 99 | int hours, 100 | String appKey, 101 | String appVersion, 102 | String channel, 103 | PatchRequestCallback patchRequestCallback 104 | ) { 105 | final boolean debug = Debugger.getInstance(context).isDebug(); 106 | TinkerLog.i(TAG, String.format("installTinkerServer, debug value: %s appVersion: %s, channel: %s", 107 | String.valueOf(debug), appVersion, channel) 108 | ); 109 | sTinkerServerClient = TinkerServerClient.init( 110 | context, 111 | tinker, 112 | appKey, 113 | appVersion, 114 | debug, 115 | patchRequestCallback 116 | ); 117 | // add channel condition 118 | sTinkerServerClient.updateTinkerCondition(CONDITION_CHANNEL, channel); 119 | sTinkerServerClient.setCheckIntervalByHours(hours); 120 | TinkerServerManager.channel = channel; 121 | } 122 | 123 | /** 124 | * 检查服务器是否有补丁更新 125 | * @param immediately 是否立刻检查,忽略时间间隔限制 126 | */ 127 | public static void checkTinkerUpdate(final boolean immediately) { 128 | if (sTinkerServerClient == null) { 129 | TinkerLog.e(TAG, "checkTinkerUpdate, sTinkerServerClient == null"); 130 | return; 131 | } 132 | Tinker tinker = sTinkerServerClient.getTinker(); 133 | //only check at the main process 134 | if (tinker.isMainProcess()) { 135 | Looper.getMainLooper().myQueue().addIdleHandler(new MessageQueue.IdleHandler() { 136 | @Override public boolean queueIdle() { 137 | sTinkerServerClient.checkTinkerUpdate(immediately); 138 | return false; 139 | } 140 | }); 141 | } 142 | } 143 | 144 | /** 145 | * 向服务器请求在线参数信息 146 | * @param configRequestCallback 147 | * @param immediately 是否立刻请求,忽略时间间隔限制 148 | */ 149 | public static void getDynamicConfig(final ConfigRequestCallback configRequestCallback, final boolean immediately) { 150 | if (sTinkerServerClient == null) { 151 | TinkerLog.e(TAG, "checkTinkerUpdate, sTinkerServerClient == null"); 152 | return; 153 | } 154 | Tinker tinker = sTinkerServerClient.getTinker(); 155 | //only check at the main process 156 | if (tinker.isMainProcess()) { 157 | Looper.getMainLooper().myQueue().addIdleHandler(new MessageQueue.IdleHandler() { 158 | @Override public boolean queueIdle() { 159 | sTinkerServerClient.getDynamicConfig(configRequestCallback, immediately); 160 | return false; 161 | } 162 | }); 163 | } 164 | } 165 | 166 | /** 167 | * 设置在线参数的时间间隔 168 | * @param hours 大于等于0的整数 169 | */ 170 | public static void setGetConfigIntervalByHours(int hours) { 171 | if (sTinkerServerClient == null) { 172 | TinkerLog.e(TAG, "setGetConfigIntervalByHours, sTinkerServerClient == null"); 173 | return; 174 | } 175 | sTinkerServerClient.setGetConfigIntervalByHours(hours); 176 | } 177 | 178 | /** 179 | * 将在线参数返回的 json 转化为 Hashmap 180 | * @param jsonString 181 | * @return 182 | * @throws JSONException 183 | */ 184 | public static HashMap jsonToMap(String jsonString) throws JSONException { 185 | HashMap map = new HashMap<>(); 186 | JSONObject jObject = new JSONObject(jsonString); 187 | Iterator keys = jObject.keys(); 188 | 189 | while (keys.hasNext()) { 190 | String key = keys.next(); 191 | String value = jObject.getString(key); 192 | map.put(key, value); 193 | } 194 | return map; 195 | } 196 | 197 | 198 | /** 199 | * 设置条件下发的属性 200 | * @param key 201 | * @param value 202 | */ 203 | public void updateTinkerCondition(String key, String value) { 204 | if (sTinkerServerClient == null) { 205 | TinkerLog.e(TAG, "updateTinkerCondition, sTinkerServerClient == null"); 206 | return; 207 | } 208 | sTinkerServerClient.updateTinkerCondition(key, value); 209 | 210 | } 211 | 212 | /** 213 | * 上报补丁合成情况 214 | * @param patchResult 215 | */ 216 | public static void reportTinkerPatchFail(PatchResult patchResult) { 217 | if (sTinkerServerClient == null) { 218 | TinkerLog.e(TAG, "reportTinkerPatchFail, sTinkerServerClient == null"); 219 | return; 220 | } 221 | if (patchResult == null) { 222 | TinkerLog.e(TAG, "reportTinkerPatchFail, patchResult == null"); 223 | return; 224 | } 225 | 226 | if (patchResult.isSuccess) { 227 | TinkerLog.i(TAG, "reportTinkerPatchFail, patch success, just return"); 228 | return; 229 | } 230 | String patchMd5 = (patchResult.patchVersion != null) 231 | ? patchResult.patchVersion : SharePatchFileUtil.getMD5(new File(patchResult.rawPatchFilePath)); 232 | 233 | if (!patchMd5.equals(sTinkerServerClient.getCurrentPatchMd5())) { 234 | TinkerLog.e(TAG, "reportTinkerPatchFail, md5 not equal, patchMd5:%s, currentPatchMd5:%s", 235 | patchMd5, sTinkerServerClient.getCurrentPatchMd5() 236 | ); 237 | return; 238 | } 239 | sTinkerServerClient.reportPatchFail( 240 | sTinkerServerClient.getCurrentPatchVersion(), 241 | DefaultPatchRequestCallback.ERROR_PATCH_FAIL 242 | ); 243 | } 244 | 245 | /** 246 | * 上报补丁合成情况 247 | * @param patchMd5 248 | */ 249 | public static void reportTinkerPatchListenerFail(int returnCode, String patchMd5) { 250 | if (sTinkerServerClient == null) { 251 | TinkerLog.e(TAG, "reportTinkerPatchListenerFail, sTinkerServerClient == null"); 252 | return; 253 | } 254 | if (returnCode == ShareConstants.ERROR_PATCH_OK) { 255 | return; 256 | } 257 | if (patchMd5 == null) { 258 | TinkerLog.e(TAG, "reportTinkerPatchListenerFail, patchMd5 == null"); 259 | return; 260 | } 261 | if (!patchMd5.equals(sTinkerServerClient.getCurrentPatchMd5())) { 262 | TinkerLog.e(TAG, "reportTinkerPatchListenerFail, md5 not equal, patchMd5:%s, currentPatchMd5:%s", 263 | patchMd5, sTinkerServerClient.getCurrentPatchMd5() 264 | ); 265 | return; 266 | } 267 | sTinkerServerClient.reportPatchFail( 268 | sTinkerServerClient.getCurrentPatchVersion(), 269 | DefaultPatchRequestCallback.ERROR_LISTENER_CHECK_FAIL 270 | ); 271 | } 272 | 273 | 274 | /** 275 | * 上报补丁加载情况 276 | */ 277 | public static void reportTinkerLoadFail() { 278 | if (sTinkerServerClient == null) { 279 | TinkerLog.e(TAG, "reportTinkerPatchFail, sTinkerServerClient == null"); 280 | return; 281 | } 282 | sTinkerServerClient.reportPatchFail( 283 | sTinkerServerClient.getCurrentPatchVersion(), 284 | DefaultPatchRequestCallback.ERROR_LOAD_FAIL 285 | ); 286 | } 287 | 288 | public static boolean isGooglePlayChannel() { 289 | return channel.contains("google"); 290 | } 291 | } 292 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/src/main/java/com/tencent/tinker/app/TinkerServerUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Shengjie Sim Sun 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.tencent.tinker.app; 26 | 27 | import android.content.BroadcastReceiver; 28 | import android.content.Context; 29 | import android.content.Intent; 30 | import android.content.IntentFilter; 31 | 32 | import com.tencent.tinker.lib.util.TinkerLog; 33 | 34 | public final class TinkerServerUtils { 35 | private static final String TAG = "Tinker.TinkerServerUtils"; 36 | private static boolean background = false; 37 | 38 | public interface IOnScreenOff { 39 | void onScreenOff(); 40 | } 41 | 42 | private TinkerServerUtils() { 43 | // Utility 44 | } 45 | 46 | public static boolean isBackground() { 47 | return background; 48 | } 49 | 50 | public static void setBackground(boolean back) { 51 | background = back; 52 | } 53 | 54 | public static class ScreenState { 55 | public ScreenState(Context context, final IOnScreenOff onScreenOffInterface) { 56 | IntentFilter filter = new IntentFilter(); 57 | filter.addAction(Intent.ACTION_SCREEN_OFF); 58 | context.registerReceiver(new BroadcastReceiver() { 59 | 60 | @Override 61 | public void onReceive(Context context, Intent in) { 62 | String action = in == null ? "" : in.getAction(); 63 | TinkerLog.i(TAG, "ScreenReceiver action [%s] ", action); 64 | if (Intent.ACTION_SCREEN_OFF.equals(action)) { 65 | 66 | context.unregisterReceiver(this); 67 | 68 | if (onScreenOffInterface != null) { 69 | onScreenOffInterface.onScreenOff(); 70 | } 71 | } 72 | } 73 | }, filter); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/src/main/java/com/tencent/tinker/app/callback/TinkerServerPatchRequestCallback.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Shengjie Sim Sun 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.tencent.tinker.app.callback; 26 | 27 | import android.content.Context; 28 | import android.content.SharedPreferences; 29 | 30 | import com.tencent.tinker.app.TinkerServerManager; 31 | import com.tencent.tinker.app.TinkerServerUtils; 32 | import com.tencent.tinker.lib.tinker.Tinker; 33 | import com.tencent.tinker.lib.tinker.TinkerInstaller; 34 | import com.tencent.tinker.lib.tinker.TinkerLoadResult; 35 | import com.tencent.tinker.lib.util.TinkerLog; 36 | import com.tencent.tinker.loader.shareutil.SharePatchFileUtil; 37 | import com.tencent.tinker.server.TinkerServerClient; 38 | import com.tencent.tinker.server.client.DefaultPatchRequestCallback; 39 | import com.tencent.tinker.server.utils.ServerUtils; 40 | 41 | import java.io.File; 42 | 43 | 44 | public class TinkerServerPatchRequestCallback extends DefaultPatchRequestCallback { 45 | private static final String TAG = "Tinker.TinkerServerDefaultRequestCallback"; 46 | 47 | public static final String TINKER_RETRY_PATCH = "tinker_retry_patch"; 48 | public static final int TINKER_MAX_RETRY_COUNT = 3; 49 | 50 | @Override 51 | public boolean beforePatchRequest() { 52 | boolean result = super.beforePatchRequest(); 53 | if (result) { 54 | TinkerServerClient client = TinkerServerClient.get(); 55 | Tinker tinker = client.getTinker(); 56 | Context context = client.getContext(); 57 | 58 | if (!tinker.isMainProcess()) { 59 | TinkerLog.e(TAG, "beforePatchRequest, only request on the main process"); 60 | return false; 61 | } 62 | if (TinkerServerManager.isGooglePlayChannel()) { 63 | TinkerLog.e(TAG, "beforePatchRequest, google play channel, return false"); 64 | return false; 65 | } 66 | // main process must be the newly version 67 | // check whether it is pending work 68 | String currentPatchMd5 = client.getCurrentPatchMd5(); 69 | TinkerLoadResult tinkerLoadResult = tinker.getTinkerLoadResultIfPresent(); 70 | 71 | if (tinkerLoadResult.currentVersion == null || !currentPatchMd5.equals(tinkerLoadResult.currentVersion)) { 72 | Integer version = client.getCurrentPatchVersion(); 73 | if (version > 0) { 74 | File patchFile = ServerUtils.getServerFile( 75 | context, client.getAppVersion(), String.valueOf(version) 76 | ); 77 | if (patchFile.exists() && patchFile.isFile() && handlePatchFile(context, version, patchFile)) { 78 | return false; 79 | } 80 | } 81 | } 82 | } 83 | return result; 84 | } 85 | 86 | private boolean handlePatchFile(Context context, Integer version, File patchFile) { 87 | SharedPreferences sp = context.getSharedPreferences( 88 | TinkerServerClient.SHARE_SERVER_PREFERENCE_CONFIG, Context.MODE_PRIVATE 89 | ); 90 | int current = sp.getInt(TINKER_RETRY_PATCH, 0); 91 | if (current >= TINKER_MAX_RETRY_COUNT) { 92 | SharePatchFileUtil.safeDeleteFile(patchFile); 93 | sp.edit().putInt(TINKER_RETRY_PATCH, 0).commit(); 94 | TinkerLog.w(TAG, 95 | "beforePatchRequest, retry patch install more than %d times, version: %d, patch:%s", 96 | current, version, patchFile.getPath() 97 | ); 98 | } else { 99 | TinkerLog.w(TAG, "beforePatchRequest, have pending patch to install, version: %d, patch:%s", 100 | version, patchFile.getPath() 101 | ); 102 | 103 | sp.edit().putInt(TINKER_RETRY_PATCH, ++current).commit(); 104 | TinkerInstaller.onReceiveUpgradePatch(context, patchFile.getAbsolutePath()); 105 | return true; 106 | } 107 | return false; 108 | } 109 | 110 | @Override 111 | public void onPatchRollback() { 112 | TinkerLog.w(TAG, "onPatchRollback"); 113 | TinkerServerClient client = TinkerServerClient.get(); 114 | 115 | if (!client.getTinker().isTinkerLoaded()) { 116 | TinkerLog.w(TAG, "onPatchRollback, tinker is not loaded, just return"); 117 | return; 118 | } 119 | 120 | if (TinkerServerUtils.isBackground()) { 121 | TinkerLog.i(TAG, "onPatchRollback, it is in background, just clean patch and kill all process"); 122 | rollbackPatchDirectly(); 123 | } else { 124 | //we can wait process at background, such as onAppBackground 125 | //or we can restart when the screen off 126 | TinkerLog.i(TAG, "tinker wait screen to clean patch and kill all process"); 127 | new TinkerServerUtils.ScreenState(client.getContext(), new TinkerServerUtils.IOnScreenOff() { 128 | @Override 129 | public void onScreenOff() { 130 | rollbackPatchDirectly(); 131 | } 132 | }); 133 | } 134 | } 135 | 136 | @Override 137 | public void onPatchDownloadFail(Exception e, Integer newVersion, Integer currentVersion) { 138 | super.onPatchDownloadFail(e, newVersion, currentVersion); 139 | } 140 | 141 | @Override 142 | public void onPatchSyncFail(Exception e) { 143 | super.onPatchSyncFail(e); 144 | } 145 | 146 | @Override 147 | public boolean onPatchUpgrade(File file, Integer newVersion, Integer currentVersion) { 148 | boolean result = super.onPatchUpgrade(file, newVersion, currentVersion); 149 | if (result) { 150 | TinkerServerClient client = TinkerServerClient.get(); 151 | Context context = client.getContext(); 152 | SharedPreferences sp = context.getSharedPreferences( 153 | TinkerServerClient.SHARE_SERVER_PREFERENCE_CONFIG, Context.MODE_PRIVATE 154 | ); 155 | sp.edit().putInt(TINKER_RETRY_PATCH, 0).commit(); 156 | } 157 | return result; 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/src/main/java/com/tencent/tinker/app/reporter/TinkerServerLoadReporter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Shengjie Sim Sun 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package com.tencent.tinker.app.reporter; 25 | 26 | import android.content.Context; 27 | 28 | import com.tencent.tinker.lib.reporter.DefaultLoadReporter; 29 | 30 | import com.tencent.tinker.app.TinkerServerManager; 31 | 32 | /** 33 | * optional, you can just use DefaultLoadReporter 34 | * Created by zhangshaowen on 16/4/13. 35 | */ 36 | public class TinkerServerLoadReporter extends DefaultLoadReporter { 37 | 38 | public TinkerServerLoadReporter(Context context) { 39 | super(context); 40 | } 41 | 42 | 43 | @Override 44 | public void onLoadException(Throwable e, int errorCode) { 45 | super.onLoadException(e, errorCode); 46 | 47 | //把这个添加到你的PatchListener实现中 48 | TinkerServerManager.reportTinkerLoadFail(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/src/main/java/com/tencent/tinker/app/reporter/TinkerServerPatchListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Shengjie Sim Sun 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.tencent.tinker.app.reporter; 26 | 27 | import android.content.Context; 28 | 29 | import com.tencent.tinker.lib.listener.DefaultPatchListener; 30 | import com.tencent.tinker.lib.util.TinkerLog; 31 | import com.tencent.tinker.loader.shareutil.SharePatchFileUtil; 32 | 33 | import java.io.File; 34 | 35 | import com.tencent.tinker.app.TinkerServerManager; 36 | 37 | /** 38 | * Created by zhangshaowen on 16/4/30. 39 | * optional, you can just use DefaultPatchListener 40 | * we can check whatever you want whether we actually send a patch request 41 | * such as we can check rom space or apk channel 42 | */ 43 | public class TinkerServerPatchListener extends DefaultPatchListener { 44 | private static final String TAG = "Tinker.TinkerServerPatchListener"; 45 | 46 | public TinkerServerPatchListener(Context context) { 47 | super(context); 48 | } 49 | 50 | /** 51 | * because we use the defaultCheckPatchReceived method 52 | * the error code define by myself should after {@code ShareConstants.ERROR_RECOVER_INSERVICE 53 | * 54 | * @param path 55 | * @param newPatch 56 | * @return 57 | */ 58 | @Override 59 | public int patchCheck(String path) { 60 | File patchFile = new File(path); 61 | TinkerLog.i(TAG, "receive a patch file: %s, file size:%d", 62 | path, SharePatchFileUtil.getFileOrDirectorySize(patchFile) 63 | ); 64 | int returnCode = super.patchCheck(path); 65 | 66 | //把这个添加到你的PatchListener实现中 67 | String patchMd5 = SharePatchFileUtil.getMD5(patchFile); 68 | TinkerServerManager.reportTinkerPatchListenerFail(returnCode, patchMd5); 69 | return returnCode; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/src/main/java/com/tencent/tinker/app/service/TinkerServerResultService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Shengjie Sim Sun 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.tencent.tinker.app.service; 26 | 27 | import com.tencent.tinker.app.TinkerServerUtils; 28 | import com.tencent.tinker.app.TinkerServerManager; 29 | import com.tencent.tinker.lib.service.DefaultTinkerResultService; 30 | import com.tencent.tinker.lib.service.PatchResult; 31 | import com.tencent.tinker.lib.tinker.Tinker; 32 | import com.tencent.tinker.lib.util.TinkerLog; 33 | import com.tencent.tinker.lib.util.TinkerServiceInternals; 34 | import com.tencent.tinker.loader.shareutil.SharePatchFileUtil; 35 | 36 | import java.io.File; 37 | 38 | /** 39 | * optional, you can just use DefaultTinkerResultService 40 | * we can restart process when we are at background or screen off 41 | */ 42 | public class TinkerServerResultService extends DefaultTinkerResultService { 43 | private static final String TAG = "Tinker.TinkerServerResultService"; 44 | 45 | 46 | @Override 47 | public void onPatchResult(final PatchResult result) { 48 | if (result == null) { 49 | TinkerLog.e(TAG, "received null result!!!!"); 50 | return; 51 | } 52 | TinkerLog.i(TAG, "receive result: %s", result.toString()); 53 | 54 | //first, we want to kill the recover process 55 | TinkerServiceInternals.killTinkerPatchServiceProcess(getApplicationContext()); 56 | TinkerServerManager.reportTinkerPatchFail(result); 57 | 58 | if (result.isSuccess) { 59 | TinkerLog.i(TAG, "patch success, please restart process"); 60 | File rawFile = new File(result.rawPatchFilePath); 61 | if (rawFile.exists()) { 62 | TinkerLog.i(TAG, "save delete raw patch file"); 63 | SharePatchFileUtil.safeDeleteFile(rawFile); 64 | } 65 | //not like TinkerResultService, I want to restart just when I am at background! 66 | //if you have not install tinker this moment, you can use TinkerApplicationHelper api 67 | if (checkIfNeedKill(result)) { 68 | if (TinkerServerUtils.isBackground()) { 69 | TinkerLog.i(TAG, "it is in background, just restart process"); 70 | restartProcess(); 71 | } else { 72 | //we can wait process at background, such as onAppBackground 73 | //or we can restart when the screen off 74 | TinkerLog.i(TAG, "tinker wait screen to restart process"); 75 | new TinkerServerUtils.ScreenState( 76 | getApplicationContext(), new TinkerServerUtils.IOnScreenOff() { 77 | @Override 78 | public void onScreenOff() { 79 | restartProcess(); 80 | } 81 | }); 82 | } 83 | } else { 84 | TinkerLog.i(TAG, "I have already install the newly patch version!"); 85 | } 86 | } else { 87 | TinkerLog.i(TAG, "patch fail, please check reason"); 88 | } 89 | 90 | //repair current patch fail, just clean! 91 | if (!result.isSuccess) { 92 | //if you have not install tinker this moment, you can use TinkerApplicationHelper api 93 | Tinker.with(getApplicationContext()).cleanPatch(); 94 | } 95 | } 96 | 97 | /** 98 | * you can restart your process through service or broadcast 99 | */ 100 | 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 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/src/main/java/com/tencent/tinker/server/TinkerServerClient.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Shengjie Sim Sun 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.tencent.tinker.server; 26 | 27 | import android.content.Context; 28 | import android.content.SharedPreferences; 29 | import android.os.Build; 30 | 31 | import com.tencent.tinker.lib.tinker.Tinker; 32 | import com.tencent.tinker.lib.util.TinkerLog; 33 | import com.tencent.tinker.loader.TinkerRuntimeException; 34 | import com.tencent.tinker.loader.shareutil.ShareTinkerInternals; 35 | import com.tencent.tinker.server.client.ConfigRequestCallback; 36 | import com.tencent.tinker.server.client.DefaultPatchRequestCallback; 37 | import com.tencent.tinker.server.client.PatchRequestCallback; 38 | import com.tencent.tinker.server.client.TinkerClientAPI; 39 | import com.tencent.tinker.server.model.DataFetcher; 40 | import com.tencent.tinker.server.utils.NetStatusUtil; 41 | 42 | public class TinkerServerClient { 43 | private static final String TAG = "Tinker.ServerClient"; 44 | 45 | public static final String SHARE_SERVER_PREFERENCE_CONFIG = "tinker_server_config"; 46 | public static final String TINKER_LAST_CHECK = "tinker_last_check"; 47 | public static final String TINKER_CONFIG_LAST_CHECK = "tinker_config_last_check"; 48 | 49 | public static final String CONDITION_WIFI = "wifi"; 50 | public static final String CONDITION_SDK = "sdk"; 51 | public static final String CONDITION_BRAND = "brand"; 52 | public static final String CONDITION_MODEL = "model"; 53 | public static final String CONDITION_CPU_ABI = "cpu"; 54 | 55 | public static final long DEFAULT_CHECK_INTERVAL = 1 * 3600 * 1000; 56 | public static final long NEVER_CHECK_UPDATE = -1; 57 | 58 | private static volatile TinkerServerClient client; 59 | private final Tinker tinker; 60 | private final Context context; 61 | private final PatchRequestCallback patchRequestCallback; 62 | 63 | private long checkInterval = DEFAULT_CHECK_INTERVAL; 64 | private long checkConfigInterval = DEFAULT_CHECK_INTERVAL; 65 | 66 | final TinkerClientAPI clientAPI; 67 | 68 | public TinkerServerClient(Context context, Tinker tinker, String appKey, 69 | String appVersion, Boolean debug, PatchRequestCallback patchRequestCallback) { 70 | this.tinker = tinker; 71 | this.context = context; 72 | this.clientAPI = TinkerClientAPI.init(context, appKey, appVersion, debug); 73 | this.patchRequestCallback = patchRequestCallback; 74 | makeDefaultConditions(); 75 | } 76 | 77 | public static TinkerServerClient get() { 78 | if (client == null) { 79 | throw new RuntimeException("Please invoke init Tinker Client first"); 80 | } 81 | return client; 82 | } 83 | 84 | /** 85 | * 初始化 TinkerPatch 的 SDK, 使用默认的 {@link DefaultPatchRequestCallback} 86 | * @param context 87 | * @param tinker 88 | * @param appKey 89 | * @param appVersion 90 | * @param debug 91 | * @return 92 | */ 93 | public static TinkerServerClient init(Context context, Tinker tinker, 94 | String appKey, String appVersion, Boolean debug) { 95 | if (client == null) { 96 | synchronized (TinkerClientAPI.class) { 97 | if (client == null) { 98 | client = new TinkerServerClient(context, tinker, appKey, 99 | appVersion, debug, new DefaultPatchRequestCallback()); 100 | } 101 | } 102 | } 103 | return client; 104 | } 105 | 106 | /** 107 | * 初始化 TinkerPatch 的 SDK, 使用自定义的 {@link PatchRequestCallback} 108 | * @param context 109 | * @param tinker 110 | * @param appKey 111 | * @param appVersion 112 | * @param debug 113 | * @param patchRequestCallback 114 | * @return 115 | */ 116 | public static TinkerServerClient init(Context context, Tinker tinker, String appKey, 117 | String appVersion, Boolean debug, PatchRequestCallback patchRequestCallback) { 118 | if (client == null) { 119 | synchronized (TinkerClientAPI.class) { 120 | if (client == null) { 121 | client = new TinkerServerClient(context, tinker, appKey, appVersion, debug, patchRequestCallback); 122 | } 123 | } 124 | } 125 | return client; 126 | } 127 | 128 | /** 129 | * 输入条件输入的数值 130 | * @param key 131 | * @param value 132 | */ 133 | public void updateTinkerCondition(String key, String value) { 134 | this.clientAPI.params(key, value); 135 | } 136 | 137 | /** 138 | * 检查服务器是否存在补丁更新 139 | * @param immediately 是否忽略时间间隔 140 | */ 141 | public void checkTinkerUpdate(boolean immediately) { 142 | if (!tinker.isTinkerEnabled() || !ShareTinkerInternals.isTinkerEnableWithSharedPreferences(context)) { 143 | TinkerLog.e(TAG, "tinker is disable, just return"); 144 | return; 145 | } 146 | 147 | SharedPreferences sp = context.getSharedPreferences( 148 | SHARE_SERVER_PREFERENCE_CONFIG, Context.MODE_PRIVATE 149 | ); 150 | long last = sp.getLong(TINKER_LAST_CHECK, 0); 151 | if (last == NEVER_CHECK_UPDATE) { 152 | TinkerLog.i(TAG, "tinker update is disabled, with never check flag!"); 153 | return; 154 | } 155 | long interval = System.currentTimeMillis() - last; 156 | if (immediately || clientAPI.isDebug() || interval >= checkInterval) { 157 | sp.edit().putLong(TINKER_LAST_CHECK, System.currentTimeMillis()).commit(); 158 | clientAPI.update(context, patchRequestCallback); 159 | } else { 160 | TinkerLog.i(TAG, "tinker sync should wait interval %ss", (checkInterval - interval) / 1000); 161 | } 162 | } 163 | 164 | 165 | /** 166 | * 获取后台在线参数信息 167 | * @param callback 回调 168 | * @param immediately 是否忽略时间间隔 169 | */ 170 | public void getDynamicConfig(final ConfigRequestCallback callback, boolean immediately) { 171 | SharedPreferences sp = context.getSharedPreferences( 172 | SHARE_SERVER_PREFERENCE_CONFIG, Context.MODE_PRIVATE 173 | ); 174 | long last = sp.getLong(TINKER_CONFIG_LAST_CHECK, 0); 175 | if (last == NEVER_CHECK_UPDATE) { 176 | TinkerLog.i(TAG, "tinker get config is disabled, with never check flag!"); 177 | return; 178 | } 179 | 180 | long interval = System.currentTimeMillis() - last; 181 | if (immediately || clientAPI.isDebug() || interval >= checkConfigInterval) { 182 | sp.edit().putLong(TINKER_CONFIG_LAST_CHECK, System.currentTimeMillis()).commit(); 183 | clientAPI.getDynamicConfig(new DataFetcher.DataCallback() { 184 | @Override 185 | public void onDataReady(String data) { 186 | if (callback != null) { 187 | callback.onSuccess(data); 188 | } 189 | } 190 | 191 | @Override 192 | public void onLoadFailed(Exception e) { 193 | if (callback != null) { 194 | callback.onFail(e); 195 | } 196 | } 197 | }); 198 | } else { 199 | TinkerLog.i(TAG, "tinker get dynamic config should wait interval %ss", 200 | (checkConfigInterval - interval) / 1000); 201 | } 202 | } 203 | 204 | /** 205 | * 设置访问TinkerPatch服务器的频率, 以小时为单位。即每隔几个小时访问TinkerPatch服务器 206 | * 207 | * @param hours 大于等于0的整数 208 | */ 209 | public void setCheckIntervalByHours(int hours) { 210 | if (hours < 0 || hours > 24) { 211 | throw new TinkerRuntimeException("hours must be between 0 and 24"); 212 | } 213 | checkInterval = (long) hours * 3600 * 1000; 214 | } 215 | 216 | public void disableTinkerUpdate() { 217 | SharedPreferences sp = context.getSharedPreferences( 218 | SHARE_SERVER_PREFERENCE_CONFIG, Context.MODE_PRIVATE 219 | ); 220 | sp.edit().putLong(TINKER_LAST_CHECK, NEVER_CHECK_UPDATE).commit(); 221 | } 222 | 223 | /** 224 | * 设置在线参数的时间间隔 225 | * @param hours 大于等于0的整数 226 | */ 227 | public void setGetConfigIntervalByHours(int hours) { 228 | if (hours < 0 || hours > 24) { 229 | throw new TinkerRuntimeException("hours must be between 0 and 24"); 230 | } 231 | checkConfigInterval = (long) hours * 3600 * 1000; 232 | } 233 | 234 | public boolean checkParameter() { 235 | return clientAPI.getAppKey() != null && clientAPI.getAppVersion() != null; 236 | } 237 | 238 | /** 239 | * 上报补丁下载成功 240 | * @param patchVersion 补丁包版本号 241 | */ 242 | public void reportPatchDownloadSuccess(Integer patchVersion) { 243 | if (!checkParameter()) { 244 | TinkerLog.e(TAG, "check parameter fail, appKey or appVersion is null, " 245 | + "reportPatchDownloadSuccess just return"); 246 | return; 247 | } 248 | TinkerLog.i(TAG, "tinker server report patch download success, patchVersion:%d", patchVersion); 249 | clientAPI.reportDownloadSuccess(patchVersion); 250 | } 251 | 252 | /** 253 | * 上报补丁应用成功 254 | * @param patchVersion 补丁包版本号 255 | */ 256 | public void reportPatchApplySuccess(Integer patchVersion) { 257 | if (!checkParameter()) { 258 | TinkerLog.e(TAG, "check parameter fail, appKey or appVersion is null, " 259 | + "reportPatchApplySuccess just return"); 260 | return; 261 | } 262 | TinkerLog.i(TAG, "tinker server report patch apply success, patchVersion:%d", patchVersion); 263 | clientAPI.reportApplySuccess(patchVersion); 264 | } 265 | 266 | /** 267 | * 上报补丁异常 268 | * @param patchVersion 补丁包版本号 269 | * @param errorCode {@link DefaultPatchRequestCallback} 270 | */ 271 | public void reportPatchFail(Integer patchVersion, int errorCode) { 272 | if (!checkParameter()) { 273 | TinkerLog.e(TAG, "check parameter fail, appKey or appVersion is null, reportPatchFail just return"); 274 | return; 275 | } 276 | TinkerLog.i(TAG, "tinker server report patch fail, patchVersion:%d, errorCode:%d", patchVersion, errorCode); 277 | clientAPI.reportFail(patchVersion, errorCode); 278 | } 279 | 280 | /** 281 | * 更新本地 Tinker 版本信息 282 | * @param newVersion 283 | * @param patchMd5 284 | */ 285 | public void updateTinkerVersion(Integer newVersion, String patchMd5) { 286 | clientAPI.updateTinkerVersion(newVersion, patchMd5); 287 | } 288 | 289 | 290 | public Tinker getTinker() { 291 | return tinker; 292 | } 293 | 294 | public Context getContext() { 295 | return context; 296 | } 297 | 298 | public String getAppKey() { 299 | return clientAPI.getAppKey(); 300 | } 301 | 302 | public String getAppVersion() { 303 | return clientAPI.getAppVersion(); 304 | } 305 | 306 | public Integer getCurrentPatchVersion() { 307 | return clientAPI.getCurrentPatchVersion(); 308 | } 309 | 310 | public String getCurrentPatchMd5() { 311 | return clientAPI.getCurrentPatchMd5(); 312 | } 313 | 314 | public boolean isDebug() { 315 | return clientAPI.isDebug(); 316 | } 317 | 318 | 319 | private void makeDefaultConditions() { 320 | this.clientAPI.params(CONDITION_WIFI, NetStatusUtil.isWifi(context) ? "1" : "0"); 321 | this.clientAPI.params(CONDITION_SDK, String.valueOf(Build.VERSION.SDK_INT)); 322 | this.clientAPI.params(CONDITION_BRAND, Build.BRAND); 323 | this.clientAPI.params(CONDITION_MODEL, Build.MODEL); 324 | this.clientAPI.params(CONDITION_CPU_ABI, Build.CPU_ABI); 325 | } 326 | } 327 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/src/main/java/com/tencent/tinker/server/client/ConfigRequestCallback.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Shengjie Sim Sun 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.tencent.tinker.server.client; 26 | 27 | public interface ConfigRequestCallback { 28 | void onSuccess(String jsonConfig); 29 | void onFail(Exception e); 30 | } 31 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/src/main/java/com/tencent/tinker/server/client/DefaultPatchRequestCallback.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Shengjie Sim Sun 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.tencent.tinker.server.client; 26 | 27 | import android.content.Context; 28 | import android.content.SharedPreferences; 29 | 30 | import com.tencent.tinker.lib.tinker.Tinker; 31 | import com.tencent.tinker.lib.tinker.TinkerInstaller; 32 | import com.tencent.tinker.lib.util.TinkerLog; 33 | import com.tencent.tinker.lib.util.TinkerServiceInternals; 34 | import com.tencent.tinker.loader.shareutil.SharePatchFileUtil; 35 | import com.tencent.tinker.loader.shareutil.ShareSecurityCheck; 36 | import com.tencent.tinker.loader.shareutil.ShareTinkerInternals; 37 | import com.tencent.tinker.server.TinkerServerClient; 38 | import com.tencent.tinker.server.utils.NetStatusUtil; 39 | import com.tencent.tinker.server.utils.ServerUtils; 40 | 41 | import java.io.File; 42 | 43 | 44 | public class DefaultPatchRequestCallback implements PatchRequestCallback { 45 | private static final String TAG = "Tinker.RequestCallback"; 46 | 47 | public static final String TINKER_DOWNLOAD_FAIL_TIMES = "tinker_download_fail"; 48 | public static final int TINKER_DOWNLOAD_FAIL_MAX_TIMES = 3; 49 | /** 50 | * 下载补丁时异常 51 | */ 52 | public static final int ERROR_DOWNLOAD_FAIL = -1; 53 | /** 54 | * 检测下载补丁的签名时异常 55 | */ 56 | public static final int ERROR_DOWNLOAD_CHECK_FAIL = -2; 57 | /** 58 | * 补丁在patchListener检测时异常 59 | */ 60 | public static final int ERROR_LISTENER_CHECK_FAIL = -3; 61 | /** 62 | * 补丁合成异常 63 | */ 64 | public static final int ERROR_PATCH_FAIL = -4; 65 | /** 66 | * 补丁加载异常 67 | */ 68 | public static final int ERROR_LOAD_FAIL = -5; 69 | 70 | @Override 71 | public boolean beforePatchRequest() { 72 | TinkerServerClient client = TinkerServerClient.get(); 73 | 74 | // check network 75 | if (!NetStatusUtil.isConnected(client.getContext())) { 76 | TinkerLog.e(TAG, "not connect to internet"); 77 | return false; 78 | } 79 | if (TinkerServiceInternals.isTinkerPatchServiceRunning(client.getContext())) { 80 | TinkerLog.e(TAG, "tinker service is running"); 81 | return false; 82 | } 83 | return true; 84 | } 85 | 86 | @Override 87 | public boolean onPatchUpgrade(File file, Integer newVersion, Integer currentVersion) { 88 | TinkerLog.i(TAG, "onPatchUpgrade, file:%s, newVersion:%d, currentVersion:%d", 89 | file.getPath(), newVersion, currentVersion); 90 | TinkerServerClient client = TinkerServerClient.get(); 91 | Context context = client.getContext(); 92 | client.reportPatchDownloadSuccess(newVersion); 93 | 94 | ShareSecurityCheck securityCheck = new ShareSecurityCheck(context); 95 | if (!securityCheck.verifyPatchMetaSignature(file)) { 96 | TinkerLog.e(TAG, "onPatchUpgrade, signature check fail. file: %s, version:%d", file.getPath(), newVersion); 97 | //treat it as download fail 98 | if (increaseDownloadError(context)) { 99 | //update tinker version also, don't request again 100 | client.updateTinkerVersion(newVersion, SharePatchFileUtil.getMD5(file)); 101 | client.reportPatchFail(newVersion, ERROR_DOWNLOAD_CHECK_FAIL); 102 | } 103 | SharePatchFileUtil.safeDeleteFile(file); 104 | return false; 105 | } 106 | tryPatchFile(file, newVersion); 107 | return true; 108 | } 109 | 110 | private void tryPatchFile(File patchFile, Integer newVersion) { 111 | TinkerServerClient client = TinkerServerClient.get(); 112 | Context context = client.getContext(); 113 | //In order to calculate the user number, just report success here 114 | String patchMd5 = SharePatchFileUtil.getMD5(patchFile); 115 | //update version 116 | client.updateTinkerVersion(newVersion, patchMd5); 117 | //delete old patch sever file 118 | File serverDir = ServerUtils.getServerDirectory(context); 119 | if (serverDir != null) { 120 | File[] files = serverDir.listFiles(); 121 | if (files != null) { 122 | String currentName = patchFile.getName(); 123 | for (File file : files) { 124 | String fileName = file.getName(); 125 | if (fileName.equals(currentName) || fileName.equals(ServerUtils.TINKER_VERSION_FILE)) { 126 | continue; 127 | } 128 | SharePatchFileUtil.safeDeleteFile(file); 129 | } 130 | } 131 | client.reportPatchApplySuccess(newVersion); 132 | //try install 133 | TinkerInstaller.onReceiveUpgradePatch(context, patchFile.getAbsolutePath()); 134 | } 135 | } 136 | 137 | @Override 138 | public void onPatchDownloadFail(Exception e, Integer newVersion, Integer currentVersion) { 139 | TinkerLog.w(TAG, "onPatchDownloadFail e:" + e); 140 | //check network 141 | TinkerServerClient client = TinkerServerClient.get(); 142 | //due to network, just return 143 | if (!NetStatusUtil.isConnected(client.getContext())) { 144 | TinkerLog.e(TAG, "onPatchDownloadFail, not connect to internet just return"); 145 | return; 146 | } 147 | Context context = client.getContext(); 148 | if (increaseDownloadError(context)) { 149 | client.reportPatchFail(newVersion, ERROR_DOWNLOAD_FAIL); 150 | } 151 | 152 | } 153 | 154 | @Override 155 | public void onPatchSyncFail(Exception e) { 156 | TinkerLog.w(TAG, "onPatchSyncFail error:" + e); 157 | TinkerLog.printErrStackTrace(TAG, e, "onPatchSyncFail stack:"); 158 | } 159 | 160 | @Override 161 | public void onPatchRollback() { 162 | TinkerLog.w(TAG, "onPatchRollback"); 163 | rollbackPatchDirectly(); 164 | } 165 | 166 | public void rollbackPatchDirectly() { 167 | TinkerServerClient client = TinkerServerClient.get(); 168 | final Context context = client.getContext(); 169 | final Tinker tinker = client.getTinker(); 170 | //restart now 171 | tinker.cleanPatch(); 172 | ShareTinkerInternals.killAllOtherProcess(context); 173 | android.os.Process.killProcess(android.os.Process.myPid()); 174 | } 175 | 176 | @Override 177 | public void updatePatchConditions() { 178 | TinkerLog.d(TAG, "updatePatchConditions"); 179 | TinkerServerClient client = TinkerServerClient.get(); 180 | // wifi condition should be updated 181 | client.updateTinkerCondition(TinkerServerClient.CONDITION_WIFI, 182 | NetStatusUtil.isWifi(client.getContext()) ? "1" : "0"); 183 | } 184 | 185 | public boolean increaseDownloadError(Context context) { 186 | SharedPreferences sp = context.getSharedPreferences( 187 | TinkerServerClient.SHARE_SERVER_PREFERENCE_CONFIG, Context.MODE_PRIVATE 188 | ); 189 | int currentCount = sp.getInt(TINKER_DOWNLOAD_FAIL_TIMES, 0); 190 | TinkerLog.i(TAG, "increaseDownloadError, current count:%d", currentCount); 191 | 192 | if (currentCount >= TINKER_DOWNLOAD_FAIL_MAX_TIMES) { 193 | sp.edit().putInt(TINKER_DOWNLOAD_FAIL_TIMES, 0).commit(); 194 | return true; 195 | } else { 196 | sp.edit().putInt(TINKER_DOWNLOAD_FAIL_TIMES, ++currentCount).commit(); 197 | } 198 | return false; 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/src/main/java/com/tencent/tinker/server/client/PatchRequestCallback.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Shengjie Sim Sun 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.tencent.tinker.server.client; 26 | 27 | import java.io.File; 28 | 29 | public interface PatchRequestCallback { 30 | /** 31 | * 在请求补丁前,我们可以在这个接口拦截请求 32 | * 33 | * @return 返回false, 即不会请求服务器 34 | */ 35 | boolean beforePatchRequest(); 36 | 37 | /** 38 | * 服务器有新的补丁,并且已经成功的下载 39 | * 40 | * @param file 下载好的补丁地址,存放在/data/data/app_name/tinker_server/ 41 | * @param newVersion 新的补丁版本 42 | * @param currentVersion 当前的补丁版本 43 | */ 44 | boolean onPatchUpgrade(File file, Integer newVersion, Integer currentVersion); 45 | 46 | /** 47 | * 向服务器请求新补丁时,下载补丁失败 48 | * 49 | * @param e 错误类型 50 | * @param newVersion 下载失败的新补丁版本 51 | * @param currentVersion 当前的补丁版本 52 | */ 53 | void onPatchDownloadFail(Exception e, Integer newVersion, Integer currentVersion); 54 | 55 | /** 56 | * 与服务器同步时失败 57 | * 58 | * @param e 失败类型 59 | */ 60 | void onPatchSyncFail(Exception e); 61 | 62 | /** 63 | * 收到服务器清除补丁的请求,如何清除本地补丁,可以自行判断 64 | */ 65 | void onPatchRollback(); 66 | 67 | /** 68 | * 若使用条件下发方式发布补丁,某些动态改变的条件,可以在这个接口更新。例如是否为wifi 69 | */ 70 | void updatePatchConditions(); 71 | } 72 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/src/main/java/com/tencent/tinker/server/model/DataFetcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Shengjie Sim Sun 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.tencent.tinker.server.model; 26 | 27 | /** 28 | * @param The type of data to be loaded (InputStream, byte[], File etc). 29 | */ 30 | public interface DataFetcher { 31 | 32 | /** 33 | * Synchronously fetch data from which a resource can be decoded. 34 | *

35 | *

This will always be called on 36 | * background thread so it is safe to perform long running tasks here. Any third party libraries 37 | * called must be thread safe since this method will be called from a thread in a {@link 38 | * java.util.concurrent.ExecutorService} that may have more than one background thread.

39 | *

40 | *

This method will only be called when the corresponding resource is not in the cache.

41 | *

42 | *

Note - this method will be run on a background thread so blocking I/O is safe.

43 | * 44 | * @see #cleanup() where the data retuned will be cleaned up 45 | */ 46 | void loadData(DataCallback callback); 47 | 48 | /** 49 | * Cleanup or recycle any resources used by this data fetcher. This method will be called in a 50 | * finally block after the data provided by 51 | *

Note - this method will be run on a background thread so blocking I/O is safe.

52 | */ 53 | void cleanup(); 54 | 55 | void cancel(); 56 | 57 | /** 58 | * Returns the class of the data this fetcher will attempt to obtain. 59 | */ 60 | Class getDataClass(); 61 | 62 | /** 63 | * Callback that should be called when data has been loaded and is available, or when the load 64 | * fails. 65 | * 66 | * @param The type of data that will be loaded. 67 | */ 68 | interface DataCallback { 69 | /** 70 | * Called with the loaded data if the load succeeded, or with {@code null} if the load failed. 71 | */ 72 | void onDataReady(T data); 73 | 74 | /** 75 | * Called when the load fails. 76 | * 77 | * @param e a non-null {@link Exception} indicating why the load failed. 78 | */ 79 | void onLoadFailed(Exception e); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/src/main/java/com/tencent/tinker/server/model/Headers.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Shengjie Sim Sun 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.tencent.tinker.server.model; 26 | 27 | import android.text.TextUtils; 28 | 29 | import java.util.Collections; 30 | import java.util.HashMap; 31 | import java.util.Map; 32 | 33 | class Headers { 34 | 35 | /** 36 | * A Headers object containing reasonable defaults that should be used when users don't want 37 | * to provide their own headers. 38 | */ 39 | public static final Headers DEFAULT = new Headers.Builder().build(); 40 | 41 | final Map headers; 42 | 43 | Headers(Map headers) { 44 | this.headers = new HashMap<>(headers); 45 | } 46 | 47 | public Map getHeaders() { 48 | return headers; 49 | } 50 | 51 | private static class Builder { 52 | private static final String USER_AGENT_HEADER = "User-Agent"; 53 | private static final String DEFAULT_USER_AGENT = System.getProperty("http.agent"); 54 | private static final String ENCODING_HEADER = "Accept-Encoding"; 55 | private static final String DEFAULT_ENCODING = "identity"; 56 | private static final Map DEFAULT_HEADERS; 57 | 58 | static { 59 | Map temp = new HashMap<>(2); 60 | if (!TextUtils.isEmpty(DEFAULT_USER_AGENT)) { 61 | temp.put(USER_AGENT_HEADER, DEFAULT_USER_AGENT); 62 | } 63 | temp.put(ENCODING_HEADER, DEFAULT_ENCODING); 64 | DEFAULT_HEADERS = Collections.unmodifiableMap(temp); 65 | } 66 | 67 | private Map headers; 68 | 69 | Builder() { 70 | // This constructor is intentionally empty. Nothing special is needed here. 71 | } 72 | 73 | public Builder setHeader(String key, String value) { 74 | if (headers == null) { 75 | headers = new HashMap<>(); 76 | } 77 | if (!TextUtils.isEmpty(key)) { 78 | if (value == null && headers.containsKey(key)) { 79 | headers.remove(key); 80 | } else { 81 | headers.put(key, value); 82 | } 83 | } 84 | return this; 85 | } 86 | 87 | public Headers build() { 88 | if (headers == null || headers.isEmpty()) { 89 | return new Headers(DEFAULT_HEADERS); 90 | } else { 91 | return new Headers(Collections.unmodifiableMap(headers)); 92 | } 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/src/main/java/com/tencent/tinker/server/model/TinkerClientUrl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Shengjie Sim Sun 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.tencent.tinker.server.model; 26 | 27 | import android.net.Uri; 28 | import android.text.TextUtils; 29 | 30 | import com.tencent.tinker.server.utils.Preconditions; 31 | 32 | import java.net.MalformedURLException; 33 | import java.net.URL; 34 | import java.util.HashMap; 35 | import java.util.Map; 36 | 37 | public class TinkerClientUrl { 38 | 39 | private static final String ALLOWED_URI_CHARS = "@#&=*+-_.,:!?()/~'%"; 40 | 41 | private final Headers headers; 42 | private final String stringUrl; 43 | private final String body; 44 | private final String method; 45 | 46 | private String safeStringUrl; 47 | private URL safeUrl; 48 | 49 | public TinkerClientUrl(String stringUrl, Headers headers, String body, String method) { 50 | this.stringUrl = Preconditions.checkNotEmpty(stringUrl); 51 | this.method = Preconditions.checkNotEmpty(method); 52 | this.headers = headers; 53 | this.body = body; 54 | } 55 | 56 | public URL toURL() throws MalformedURLException { 57 | return getSafeUrl(); 58 | } 59 | 60 | public String toStringUrl() { 61 | return getSafeStringUrl(); 62 | } 63 | 64 | public Map getHeaders() { 65 | return headers.getHeaders(); 66 | } 67 | 68 | public String getMethod() { 69 | return method; 70 | } 71 | 72 | public String getBody() { 73 | return body; 74 | } 75 | 76 | // See http://stackoverflow.com/questions/3286067/url-encoding-in-android. Although the answer 77 | // using URI would work, using it would require both decoding and encoding each string which is 78 | // more complicated, slower and generates more objects than the solution below. See also issue 79 | // #133. 80 | private URL getSafeUrl() throws MalformedURLException { 81 | if (safeUrl == null) { 82 | safeUrl = new URL(getSafeStringUrl()); 83 | } 84 | return safeUrl; 85 | } 86 | 87 | private String getSafeStringUrl() { 88 | if (TextUtils.isEmpty(safeStringUrl)) { 89 | safeStringUrl = Uri.encode(stringUrl, ALLOWED_URI_CHARS); 90 | } 91 | return safeStringUrl; 92 | } 93 | 94 | public static class Builder { 95 | private String url; 96 | private HashMap params; 97 | private String body; 98 | private String method; 99 | private Headers headers; 100 | 101 | public TinkerClientUrl.Builder url(String url) { 102 | this.url = url; 103 | return this; 104 | } 105 | 106 | public TinkerClientUrl.Builder param(String key, Object value) { 107 | if (params == null) { 108 | this.params = new HashMap<>(); 109 | } 110 | this.params.put(key, String.valueOf(value)); 111 | return this; 112 | } 113 | 114 | public TinkerClientUrl.Builder body(String body) { 115 | this.body = body; 116 | return this; 117 | } 118 | 119 | public TinkerClientUrl.Builder method(String method) { 120 | switch (method) { 121 | case "GET": 122 | case "POST": 123 | this.method = method; 124 | break; 125 | default: 126 | throw new RuntimeException("Didn't Supported Method, Please pass the correct method"); 127 | } 128 | return this; 129 | } 130 | 131 | public TinkerClientUrl.Builder headers(Headers headers) { 132 | this.headers = headers; 133 | return this; 134 | } 135 | 136 | private void makeDefault() { 137 | Uri.Builder urlBuilder = Uri.parse(this.url).buildUpon(); 138 | if (TextUtils.isEmpty(this.method)) { 139 | this.method = "GET"; 140 | } 141 | if (this.headers == null) { 142 | this.headers = Headers.DEFAULT; 143 | } 144 | if (this.params != null) { 145 | for (Map.Entry entry : params.entrySet()) { 146 | urlBuilder.appendQueryParameter(entry.getKey(), entry.getValue()); 147 | } 148 | } 149 | this.url = urlBuilder.build().toString(); 150 | } 151 | 152 | public TinkerClientUrl build() { 153 | makeDefault(); 154 | return new TinkerClientUrl(this.url, this.headers, this.body, this.method); 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/src/main/java/com/tencent/tinker/server/model/request/BaseReport.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Shengjie Sim Sun 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.tencent.tinker.server.model.request; 26 | 27 | 28 | import com.tencent.tinker.lib.util.TinkerLog; 29 | import com.tencent.tinker.server.utils.ServerUtils; 30 | 31 | import org.json.JSONException; 32 | import org.json.JSONObject; 33 | 34 | import java.net.URLEncoder; 35 | import java.util.HashMap; 36 | import java.util.Map; 37 | 38 | 39 | public class BaseReport { 40 | public static final String TAG = "Tinker.report"; 41 | 42 | public final String appKey; 43 | public final String appVersion; 44 | public final String patchVersion; 45 | public final Integer platformType; 46 | 47 | public BaseReport(String appKey, String appVersion, String patchVersion) { 48 | this.appKey = appKey; 49 | this.appVersion = appVersion; 50 | this.patchVersion = patchVersion; 51 | this.platformType = 1; 52 | } 53 | 54 | protected JSONObject toJsonObject() throws JSONException { 55 | JSONObject jsonObject = new JSONObject(); 56 | jsonObject.put("k", appKey); 57 | jsonObject.put("av", appVersion); 58 | jsonObject.put("pv", patchVersion); 59 | jsonObject.put("t", platformType); 60 | return jsonObject; 61 | } 62 | 63 | protected HashMap toEncodeObject() { 64 | HashMap values = new HashMap<>(); 65 | values.put("k", appKey); 66 | values.put("av", appVersion); 67 | values.put("pv", patchVersion); 68 | values.put("t", String.valueOf(platformType)); 69 | return values; 70 | } 71 | 72 | public String toEncodeForm() { 73 | return getPostDataString(toEncodeObject()); 74 | } 75 | 76 | private String getPostDataString(HashMap params) { 77 | StringBuilder result = new StringBuilder(); 78 | 79 | try { 80 | boolean first = true; 81 | for (Map.Entry entry : params.entrySet()) { 82 | if (first) { 83 | first = false; 84 | } else { 85 | result.append('&'); 86 | } 87 | result.append(URLEncoder.encode(entry.getKey(), ServerUtils.CHARSET)); 88 | result.append('='); 89 | result.append(URLEncoder.encode(entry.getValue(), ServerUtils.CHARSET)); 90 | } 91 | } catch (Exception e) { 92 | TinkerLog.e(TAG, "getPostDataString fail" + e.getMessage()); 93 | } 94 | 95 | return result.toString(); 96 | } 97 | 98 | public String toJson() { 99 | try { 100 | return toJsonObject().toString(); 101 | } catch (JSONException e) { 102 | e.printStackTrace(); 103 | return ""; 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/src/main/java/com/tencent/tinker/server/model/request/FailReport.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Shengjie Sim Sun 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.tencent.tinker.server.model.request; 26 | 27 | import org.json.JSONException; 28 | import org.json.JSONObject; 29 | 30 | import java.util.HashMap; 31 | 32 | 33 | public class FailReport extends BaseReport { 34 | public final Integer errCode; 35 | 36 | public FailReport(String appKey, String appVersion, String patchVersion, Integer errCode) { 37 | super(appKey, appVersion, patchVersion); 38 | this.errCode = errCode; 39 | } 40 | 41 | @Override 42 | protected JSONObject toJsonObject() throws JSONException { 43 | JSONObject jsonObject = super.toJsonObject(); 44 | jsonObject.put("code", errCode); 45 | return jsonObject; 46 | } 47 | 48 | @Override 49 | protected HashMap toEncodeObject() { 50 | HashMap map = super.toEncodeObject(); 51 | map.put("code", String.valueOf(errCode)); 52 | return map; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/src/main/java/com/tencent/tinker/server/model/request/SuccessReport.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Shengjie Sim Sun 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.tencent.tinker.server.model.request; 26 | 27 | public class SuccessReport extends BaseReport { 28 | public SuccessReport(String appKey, String appVersion, String patchVersion) { 29 | super(appKey, appVersion, patchVersion); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/src/main/java/com/tencent/tinker/server/model/response/SyncResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Shengjie Sim Sun 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.tencent.tinker.server.model.response; 26 | 27 | import org.json.JSONException; 28 | import org.json.JSONObject; 29 | 30 | 31 | public final class SyncResponse { 32 | 33 | private static final String KEY_VERSION = "v"; 34 | private static final String KEY_GRAY = "g"; 35 | private static final String KEY_CONDITIONS = "c"; 36 | private static final String KEY_PAUSE = "p"; 37 | private static final String KEY_ROLLBACK = "e"; 38 | 39 | public final String version; 40 | public final Integer grayValue; 41 | public final String conditions; 42 | public final Boolean isPaused; 43 | public final Boolean isRollback; 44 | 45 | private SyncResponse(String version, Integer grayValue, String conditions, Boolean pause, Boolean rollback) { 46 | this.version = version; 47 | this.conditions = conditions; 48 | this.isPaused = pause; 49 | this.isRollback = rollback; 50 | if (grayValue == 0) { 51 | this.grayValue = null; 52 | } else { 53 | this.grayValue = grayValue; 54 | } 55 | } 56 | 57 | public static SyncResponse fromJson(String json) { 58 | try { 59 | JSONObject jsonObject = new JSONObject(json); 60 | String version = jsonObject.optString(KEY_VERSION); 61 | String conditions = jsonObject.optString(KEY_CONDITIONS); 62 | Integer grayValue = jsonObject.optInt(KEY_GRAY); 63 | Integer pauseFlag = jsonObject.optInt(KEY_PAUSE); 64 | Integer rollbackFlag = jsonObject.optInt(KEY_ROLLBACK); 65 | 66 | return new SyncResponse(version, grayValue, conditions, pauseFlag == 1, rollbackFlag == 1); 67 | } catch (JSONException e) { 68 | e.printStackTrace(); 69 | } 70 | return null; 71 | } 72 | 73 | @Override 74 | public String toString() { 75 | return "version:" + version + "\ngrayValue:" + grayValue + "\nconditions:" + conditions 76 | + "\npause:" + isPaused + "\nrollback:" + isRollback; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/src/main/java/com/tencent/tinker/server/urlconnection/UrlConnectionStreamFetcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Shengjie Sim Sun 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.tencent.tinker.server.urlconnection; 26 | 27 | 28 | import com.tencent.tinker.lib.util.TinkerLog; 29 | import com.tencent.tinker.loader.shareutil.SharePatchFileUtil; 30 | import com.tencent.tinker.server.model.DataFetcher; 31 | import com.tencent.tinker.server.model.TinkerClientUrl; 32 | import com.tencent.tinker.server.utils.Preconditions; 33 | import com.tencent.tinker.server.utils.ServerUtils; 34 | 35 | import java.io.IOException; 36 | import java.io.InputStream; 37 | import java.io.OutputStreamWriter; 38 | import java.net.HttpURLConnection; 39 | import java.util.Map; 40 | import java.util.concurrent.Executor; 41 | 42 | 43 | public class UrlConnectionStreamFetcher implements DataFetcher { 44 | 45 | private static final String TAG = "Tinker.UrlConnectionFetcher"; 46 | private final TinkerClientUrl tkUrl; 47 | private Executor executor; 48 | 49 | public UrlConnectionStreamFetcher(Executor executor, TinkerClientUrl tkUrl) { 50 | this.tkUrl = tkUrl; 51 | this.executor = executor; 52 | } 53 | 54 | @Override 55 | public void loadData(final DataCallback callback) { 56 | ConnectionWorker worker = new ConnectionWorker(tkUrl, new DataCallback() { 57 | @Override 58 | public void onDataReady(InputStream data) { 59 | callback.onDataReady(data); 60 | } 61 | 62 | @Override 63 | public void onLoadFailed(Exception e) { 64 | callback.onLoadFailed(e); 65 | } 66 | }); 67 | if (executor != null) { 68 | executor.execute(worker); 69 | } else { 70 | TinkerLog.e(TAG, "Executor is null"); 71 | } 72 | } 73 | 74 | @Override 75 | public void cleanup() { 76 | this.executor = null; 77 | } 78 | 79 | @Override 80 | public void cancel() { 81 | // NOT IMPLEMENT 82 | } 83 | 84 | @Override 85 | public Class getDataClass() { 86 | return InputStream.class; 87 | } 88 | 89 | private static class ConnectionWorker implements Runnable { 90 | 91 | private final DataCallback callback; 92 | private final TinkerClientUrl url; 93 | 94 | ConnectionWorker(TinkerClientUrl url, DataCallback callback) { 95 | this.callback = Preconditions.checkNotNull(callback); 96 | this.url = Preconditions.checkNotNull(url); 97 | } 98 | 99 | @Override 100 | public void run() { 101 | InputStream inputStream = null; 102 | try { 103 | HttpURLConnection conn = (HttpURLConnection) url.toURL().openConnection(); 104 | conn.setRequestMethod(url.getMethod()); 105 | conn.setDoOutput(true); 106 | conn.setReadTimeout(10000 /* milliseconds */); 107 | conn.setConnectTimeout(15000 /* milliseconds */); 108 | conn.setInstanceFollowRedirects(false); 109 | conn.setUseCaches(false); 110 | for (Map.Entry entry : url.getHeaders().entrySet()) { 111 | conn.setRequestProperty(entry.getKey(), entry.getValue()); 112 | } 113 | switch (url.getMethod()) { 114 | case "GET": 115 | break; 116 | case "POST": 117 | OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream(), ServerUtils.CHARSET); 118 | writer.write(url.getBody()); 119 | writer.flush(); 120 | writer.close(); 121 | break; 122 | default: 123 | throw new RuntimeException("Unsupported request method" + url.getMethod()); 124 | } 125 | conn.connect(); 126 | TinkerLog.d(TAG, "response code " + conn.getResponseCode() + " msg: " + conn.getResponseMessage()); 127 | inputStream = conn.getInputStream(); 128 | this.callback.onDataReady(inputStream); 129 | } catch (IOException e) { 130 | e.printStackTrace(); 131 | this.callback.onLoadFailed(e); 132 | } finally { 133 | SharePatchFileUtil.closeQuietly(inputStream); 134 | } 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/src/main/java/com/tencent/tinker/server/urlconnection/UrlConnectionUrlLoader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Shengjie Sim Sun 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.tencent.tinker.server.urlconnection; 26 | 27 | import com.tencent.tinker.lib.util.TinkerLog; 28 | import com.tencent.tinker.server.model.DataFetcher; 29 | import com.tencent.tinker.server.model.TinkerClientUrl; 30 | 31 | import java.io.InputStream; 32 | import java.util.concurrent.Executor; 33 | import java.util.concurrent.Executors; 34 | 35 | public class UrlConnectionUrlLoader { 36 | 37 | public static final String TAG = "Tinker.UrlLoader"; 38 | private final Executor executor; 39 | 40 | public UrlConnectionUrlLoader() { 41 | executor = Executors.newSingleThreadExecutor(); 42 | } 43 | 44 | public DataFetcher buildLoadData(TinkerClientUrl url) { 45 | TinkerLog.i(TAG, "loadData from: %s", url.toStringUrl()); 46 | return new UrlConnectionStreamFetcher(executor, url); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/src/main/java/com/tencent/tinker/server/utils/Conditions.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Shengjie Sim Sun 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.tencent.tinker.server.utils; 26 | 27 | import android.content.Context; 28 | import android.text.TextUtils; 29 | 30 | import com.tencent.tinker.lib.util.TinkerLog; 31 | 32 | import java.io.File; 33 | import java.io.FileOutputStream; 34 | import java.io.IOException; 35 | import java.io.ObjectOutputStream; 36 | import java.util.ArrayList; 37 | import java.util.Arrays; 38 | import java.util.HashMap; 39 | import java.util.LinkedList; 40 | import java.util.List; 41 | import java.util.Map; 42 | import java.util.Stack; 43 | import java.util.regex.Pattern; 44 | 45 | import static com.tencent.tinker.server.client.TinkerClientAPI.TAG; 46 | 47 | 48 | public class Conditions { 49 | 50 | static final String FILE_NAME = "CONDITIONS_MAP"; 51 | static final Pattern INT_PATTERN = Pattern.compile("-?[0-9]+"); 52 | 53 | private final Map properties; 54 | 55 | public Conditions() { 56 | properties = new HashMap<>(); 57 | } 58 | 59 | public Boolean check(String rules) { 60 | if (TextUtils.isEmpty(rules)) { 61 | return true; 62 | } 63 | List rpList = Helper.toReversePolish(rules); 64 | try { 65 | return Helper.calcReversePolish(rpList, properties); 66 | } catch (Exception ignore) { 67 | TinkerLog.e(TAG, "parse conditions error(have you written '==' as '='?): " + rules, ignore); 68 | return false; 69 | } 70 | } 71 | 72 | /** 73 | * set the k,v to conditions map. 74 | * you should invoke {@link #saveToDisk(Context)} for saving the map to disk 75 | * 76 | * @param key the key 77 | * @param value the value 78 | * @return {@link Conditions} this 79 | */ 80 | public Conditions set(String key, String value) { 81 | properties.put(key, value); 82 | return this; 83 | } 84 | 85 | /** 86 | * Clean all properties. you should invoke {@link #saveToDisk(Context)} for saving to disk. 87 | * 88 | * @return {@link Conditions} this 89 | */ 90 | public Conditions clean() { 91 | properties.clear(); 92 | return this; 93 | } 94 | 95 | /** 96 | * saveToDisk 97 | * 98 | * @param context {@link Context} 99 | * @throws IOException 100 | */ 101 | public void saveToDisk(Context context) throws IOException { 102 | File file = new File(context.getFilesDir(), FILE_NAME); 103 | ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(file)); 104 | outputStream.writeObject(properties); 105 | outputStream.flush(); 106 | outputStream.close(); 107 | } 108 | 109 | 110 | static final class Helper { 111 | private static final String WITH_DELIMITER = "((?<=[%1$s])|(?=[%1$s]))"; 112 | private static final List TOKENS = new ArrayList<>(4); 113 | private static final HashMap TOKEN_PRIORITY = new HashMap<>(); 114 | 115 | static { 116 | TOKENS.add("&"); 117 | TOKENS.add("|"); 118 | TOKENS.add("("); 119 | TOKENS.add(")"); 120 | 121 | TOKEN_PRIORITY.put("&", 2); 122 | TOKEN_PRIORITY.put("|", 1); 123 | TOKEN_PRIORITY.put("(", 3); 124 | TOKEN_PRIORITY.put(")", 3); 125 | } 126 | 127 | private Helper() { 128 | // A Util Class 129 | } 130 | 131 | public static List toReversePolish(String input) { 132 | Stack opStack = new Stack<>(); 133 | List rpList = new LinkedList<>(); 134 | for (String word : tokenize(input)) { 135 | if (isToken(word)) { 136 | pushOp(opStack, rpList, word); 137 | } else { 138 | rpList.add(word); 139 | } 140 | } 141 | while (!opStack.isEmpty()) { 142 | rpList.add(opStack.pop()); 143 | } 144 | return rpList; 145 | } 146 | 147 | private static void pushOp(Stack stack, List rpList, String op) { 148 | if (stack.isEmpty() || "(".equals(op)) { 149 | stack.push(op); 150 | return; 151 | } 152 | 153 | if (")".equals(op)) { 154 | String tmp; 155 | while (!"(".equals(tmp = stack.pop())) { 156 | rpList.add(tmp); 157 | } 158 | return; 159 | } 160 | if ("(".equals(stack.peek())) { 161 | stack.push(op); 162 | return; 163 | } 164 | 165 | if (TOKEN_PRIORITY.get(op) > TOKEN_PRIORITY.get(stack.peek())) { 166 | stack.push(op); 167 | } else { 168 | rpList.add(stack.pop()); 169 | pushOp(stack, rpList, op); 170 | } 171 | } 172 | 173 | public static Boolean calcReversePolish(List list, Map props) { 174 | Stack stack = new Stack<>(); 175 | for (String word : list) { 176 | if (!isToken(word)) { 177 | // lazy calcExpr at pop from stack, some expr needn't calculate. 178 | // such 'true || expr' 179 | stack.push(word); 180 | } else { 181 | Boolean left, right; 182 | Object v1, v2; 183 | switch (word) { 184 | case "|": 185 | v1 = stack.pop(); 186 | v2 = stack.pop(); 187 | left = calcExpr(v1, props); 188 | if (left) { 189 | stack.push(Boolean.TRUE); 190 | continue; 191 | } 192 | right = calcExpr(v2, props); 193 | stack.push(right); 194 | break; 195 | case "&": 196 | v1 = stack.pop(); 197 | v2 = stack.pop(); 198 | left = calcExpr(v1, props); 199 | if (!left) { 200 | stack.push(Boolean.FALSE); 201 | continue; 202 | } 203 | right = calcExpr(v2, props); 204 | stack.push(right); 205 | break; 206 | default: 207 | throw new RuntimeException("Unsupported Operator: " + word); 208 | } 209 | } 210 | } 211 | return calcExpr(stack.pop(), props); 212 | } 213 | 214 | public static Boolean calcExpr(Object obj, Map props) { 215 | if (obj instanceof String) { 216 | return calcExpr((String) obj, props); 217 | } else if (obj instanceof Boolean) { 218 | return (Boolean) obj; 219 | } else { 220 | throw new RuntimeException("illegal type pass to calcExpr"); 221 | } 222 | } 223 | 224 | public static Boolean calcExpr(String expr, Map props) { 225 | boolean isInProps = false; 226 | List exprList = splitExpr(expr); 227 | String op = exprList.get(1); 228 | String left = exprList.get(0); 229 | String right = exprList.get(2); 230 | if (props.containsKey(left)) { 231 | isInProps = true; 232 | left = props.get(left); 233 | } 234 | if (props.containsKey(right)) { 235 | isInProps = true; 236 | right = props.get(right); 237 | } 238 | return isInProps && calcExpr(left, right, op); 239 | } 240 | 241 | public static Boolean calcExpr(String left, String right, String op) { 242 | switch (op) { 243 | case "==": 244 | return left.equals(right); 245 | case "!=": 246 | return !left.equals(right); 247 | case ">=": 248 | if (isInt(left)) { 249 | return Integer.parseInt(left) >= Integer.parseInt(right); 250 | } else { 251 | return left.compareToIgnoreCase(right) >= 0; 252 | } 253 | case ">": 254 | if (isInt(left)) { 255 | return Integer.parseInt(left) > Integer.parseInt(right); 256 | } else { 257 | return left.compareToIgnoreCase(right) > 0; 258 | } 259 | case "<=": 260 | if (isInt(left)) { 261 | return Integer.parseInt(left) <= Integer.parseInt(right); 262 | } else { 263 | return left.compareToIgnoreCase(right) <= 0; 264 | } 265 | case "<": 266 | if (isInt(left)) { 267 | return Integer.parseInt(left) < Integer.parseInt(right); 268 | } else { 269 | return left.compareToIgnoreCase(right) < 0; 270 | } 271 | default: 272 | throw new RuntimeException("Unsupported Operator"); 273 | } 274 | } 275 | 276 | public static List splitExpr(String expr) { 277 | String[] ops = new String[]{"==", "!=", ">=", "<=", ">", "<"}; 278 | for (String op : ops) { 279 | if (expr.contains(op)) { 280 | int pos = expr.indexOf(op); 281 | String left = expr.substring(0, pos); 282 | String right = expr.substring(pos + op.length(), expr.length()); 283 | return Arrays.asList(left, op, right); 284 | } 285 | } 286 | return new ArrayList<>(); 287 | } 288 | 289 | private static Boolean isToken(String word) { 290 | return TOKENS.contains(word); 291 | } 292 | 293 | private static List tokenize(String input) { 294 | input = input.replaceAll("\\s+", "") 295 | .replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">") 296 | .replaceAll("&&", "&").replaceAll("\\|\\|", "|"); 297 | List tokens = new ArrayList<>(TOKENS.size()); 298 | for (String token : TOKENS) { 299 | tokens.add(Pattern.quote(token)); 300 | } 301 | String splits = TextUtils.join("|", tokens); 302 | return Arrays.asList(input.split(String.format(WITH_DELIMITER, splits))); 303 | } 304 | 305 | private static Boolean isInt(String string) { 306 | return INT_PATTERN.matcher(string).matches(); 307 | } 308 | } 309 | } 310 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/src/main/java/com/tencent/tinker/server/utils/Debugger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Shengjie Sim Sun 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.tencent.tinker.server.utils; 26 | 27 | import android.content.ContentResolver; 28 | import android.content.Context; 29 | import android.database.Cursor; 30 | import android.net.Uri; 31 | import android.provider.BaseColumns; 32 | 33 | import com.tencent.tinker.lib.util.TinkerLog; 34 | 35 | import java.util.HashMap; 36 | 37 | public final class Debugger { 38 | 39 | public static final Uri CONTENT_URI = Uri.parse("content://com.tinker.debug.debugprovider/config"); 40 | public static final String KEY = "key"; 41 | public static final String TYPE = "type"; 42 | public static final String VALUE = "value"; 43 | public static final int TYPE_INT = 1; 44 | public static final int TYPE_LONG = 2; 45 | public static final int TYPE_STRING = 3; 46 | public static final int TYPE_BOOLEAN = 4; 47 | public static final int TYPE_FLOAT = 5; 48 | public static final int TYPE_DOUBLE = 6; 49 | private static final String TAG = "Tinker.Debugger"; 50 | private static Debugger sDebugger; 51 | private final String[] columns = {BaseColumns._ID, KEY, TYPE, VALUE}; 52 | private final HashMap values = new HashMap<>(); 53 | 54 | private Debugger(final Context context) { 55 | final ContentResolver cr = context.getContentResolver(); 56 | Cursor cu; 57 | try { 58 | cu = cr.query(CONTENT_URI, columns, null, null, null); 59 | } catch (Exception e) { 60 | TinkerLog.e(TAG, "Get contentProvider error", e); 61 | cu = null; 62 | } 63 | 64 | if (cu == null || cu.getCount() <= 0) { 65 | TinkerLog.w(TAG, "debugger not attached cu == null"); 66 | if (cu != null) { 67 | cu.close(); 68 | } 69 | return; 70 | } 71 | 72 | TinkerLog.i(TAG, "debugger attached"); 73 | 74 | final int keyIdx = cu.getColumnIndex("key"); 75 | final int typeIdx = cu.getColumnIndex("type"); 76 | final int valueIdx = cu.getColumnIndex("value"); 77 | 78 | while (cu.moveToNext()) { 79 | final Object obj = Resolver.resolveObj(cu.getInt(typeIdx), cu.getString(valueIdx)); 80 | values.put(cu.getString(keyIdx), obj); 81 | } 82 | cu.close(); 83 | } 84 | 85 | public static Debugger getInstance(Context context) { 86 | if (sDebugger == null) { 87 | sDebugger = new Debugger(context); 88 | } 89 | return sDebugger; 90 | } 91 | 92 | public boolean isDebug() { 93 | Boolean debug = getBoolean(".com.tinker.debugtool.debug"); 94 | if (debug == null) { 95 | return false; 96 | } 97 | return debug; 98 | } 99 | 100 | 101 | public String getString(final String key) { 102 | final Object obj = values.get(key); 103 | if (obj instanceof String) { 104 | TinkerLog.d(TAG, "getString(): key=" + key + ", value=" + obj.toString()); 105 | return (String) obj; 106 | } 107 | 108 | return null; 109 | } 110 | 111 | public Integer getInteger(final String key) { 112 | final Object obj = values.get(key); 113 | if (obj instanceof Integer) { 114 | TinkerLog.d(TAG, "getInteger(): key=" + key + ", value=" + obj.toString()); 115 | return (Integer) obj; 116 | } 117 | 118 | return null; 119 | } 120 | 121 | public Long getLong(final String key) { 122 | final Object obj = values.get(key); 123 | if (obj instanceof Long) { 124 | TinkerLog.d(TAG, "getLong(): key=" + key + ", value=" + obj.toString()); 125 | return (Long) obj; 126 | } 127 | 128 | return null; 129 | } 130 | 131 | public Boolean getBoolean(final String key) { 132 | final Object obj = values.get(key); 133 | if (obj == null) { 134 | return false; 135 | } 136 | 137 | if (obj instanceof Boolean) { 138 | TinkerLog.d(TAG, "getBoolean(): key=" + key + ", value=" + obj.toString()); 139 | return (Boolean) obj; 140 | } 141 | return false; 142 | } 143 | 144 | public static final class Resolver { 145 | private static final String TAG = "Tinker.Debugger.Resolver"; 146 | 147 | private Resolver() { 148 | 149 | } 150 | 151 | public static Object resolveObj(int type, String value) { 152 | try { 153 | switch (type) { 154 | case TYPE_INT: 155 | return Integer.valueOf(value); 156 | 157 | case TYPE_LONG: 158 | return Long.valueOf(value); 159 | 160 | case TYPE_STRING: 161 | return value; 162 | 163 | case TYPE_BOOLEAN: 164 | return Boolean.valueOf(value); 165 | 166 | case TYPE_FLOAT: 167 | return Float.valueOf(value); 168 | 169 | case TYPE_DOUBLE: 170 | return Double.valueOf(value); 171 | 172 | default: 173 | TinkerLog.e(TAG, "unknown type"); 174 | break; 175 | } 176 | 177 | } catch (Exception e) { 178 | TinkerLog.printErrStackTrace(TAG, e, ""); 179 | } 180 | return null; 181 | } 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/src/main/java/com/tencent/tinker/server/utils/NetStatusUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Shengjie Sim Sun 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.tencent.tinker.server.utils; 26 | 27 | import android.content.Context; 28 | import android.net.ConnectivityManager; 29 | import android.net.NetworkInfo; 30 | 31 | public final class NetStatusUtil { 32 | private static ConnectivityManager connectivityManager = null; 33 | 34 | private NetStatusUtil() { 35 | } 36 | 37 | public static boolean isConnected(Context context) { 38 | if (connectivityManager == null) { 39 | connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 40 | } 41 | 42 | if (connectivityManager == null) { 43 | return false; 44 | } 45 | NetworkInfo activeNetInfo = connectivityManager.getActiveNetworkInfo(); 46 | boolean connect = false; 47 | try { 48 | connect = activeNetInfo.isConnected(); 49 | } catch (Exception e) { 50 | // do noting 51 | } 52 | return connect; 53 | } 54 | 55 | public static boolean isWifi(Context context) { 56 | if (connectivityManager == null) { 57 | connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 58 | } 59 | if (connectivityManager == null) { 60 | return false; 61 | } 62 | NetworkInfo activeNetInfo = connectivityManager.getActiveNetworkInfo(); 63 | if (activeNetInfo == null) { 64 | return false; 65 | } 66 | 67 | return activeNetInfo.getType() == ConnectivityManager.TYPE_WIFI; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/src/main/java/com/tencent/tinker/server/utils/Preconditions.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Shengjie Sim Sun 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.tencent.tinker.server.utils; 26 | 27 | import android.text.TextUtils; 28 | 29 | import java.util.Collection; 30 | 31 | public final class Preconditions { 32 | 33 | private Preconditions() { 34 | // TinkerServerUtils class. 35 | } 36 | 37 | public static void checkArgument(boolean expression, String message) { 38 | if (!expression) { 39 | throw new IllegalArgumentException(message); 40 | } 41 | } 42 | 43 | public static T checkNotNull(T arg) { 44 | return checkNotNull(arg, "Argument must not be null"); 45 | } 46 | 47 | public static T checkNotNull(T arg, String message) { 48 | if (arg == null) { 49 | throw new NullPointerException(message); 50 | } 51 | return arg; 52 | } 53 | 54 | public static String checkNotEmpty(String string) { 55 | if (TextUtils.isEmpty(string)) { 56 | throw new IllegalArgumentException("Must not be null or empty"); 57 | } 58 | return string; 59 | } 60 | 61 | public static , Y> T checkNotEmpty(T collection) { 62 | if (collection.isEmpty()) { 63 | throw new IllegalArgumentException("Must not be empty."); 64 | } 65 | return collection; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/src/main/java/com/tencent/tinker/server/utils/ServerUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Shengjie Sim Sun 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.tencent.tinker.server.utils; 26 | 27 | import android.content.Context; 28 | import android.content.pm.ApplicationInfo; 29 | 30 | import java.io.ByteArrayOutputStream; 31 | import java.io.File; 32 | import java.io.FileOutputStream; 33 | import java.io.IOException; 34 | import java.io.InputStream; 35 | 36 | public final class ServerUtils { 37 | public static final String CHARSET = "UTF-8"; 38 | public static final int BUFFER_SIZE = 4096; 39 | public static final String TINKER_SERVER_DIR = "tinker_server"; 40 | public static final String TINKER_VERSION_FILE = "version.info"; 41 | 42 | 43 | private ServerUtils() { 44 | // A TinkerServerUtils Class 45 | } 46 | 47 | public static File readStreamToFile(InputStream inputStream, String filePath) throws IOException { 48 | if (inputStream == null) { 49 | return null; 50 | } 51 | 52 | File file = new File(filePath); 53 | File parent = file.getParentFile(); 54 | if (!parent.exists() && !parent.mkdirs()) { 55 | throw new IOException(String.format("Can't create folder %s", parent.getAbsolutePath())); 56 | } 57 | FileOutputStream fileOutput = new FileOutputStream(file); 58 | try { 59 | byte[] buffer = new byte[BUFFER_SIZE]; 60 | int bufferLength; 61 | while ((bufferLength = inputStream.read(buffer)) > 0) { 62 | fileOutput.write(buffer, 0, bufferLength); 63 | } 64 | } finally { 65 | try { 66 | fileOutput.close(); 67 | } catch (IOException ignored) { 68 | // ignored 69 | } 70 | } 71 | return file; 72 | } 73 | 74 | public static Integer stringToInteger(String string) { 75 | if (string == null) { 76 | return null; 77 | } 78 | return Integer.parseInt(string); 79 | } 80 | 81 | public static String readStreamToString(InputStream inputStream, String charset) { 82 | if (inputStream == null) { 83 | return null; 84 | } 85 | ByteArrayOutputStream bo = new ByteArrayOutputStream(); 86 | byte[] buffer = new byte[BUFFER_SIZE]; 87 | int bufferLength; 88 | 89 | String result; 90 | try { 91 | while ((bufferLength = inputStream.read(buffer)) > 0) { 92 | bo.write(buffer, 0, bufferLength); 93 | } 94 | result = bo.toString(charset); 95 | } catch (Throwable e) { 96 | result = null; 97 | } 98 | return result; 99 | } 100 | 101 | public static File getServerDirectory(Context context) { 102 | ApplicationInfo applicationInfo = context.getApplicationInfo(); 103 | if (applicationInfo == null) { 104 | // Looks like running on a test Context, so just return without patching. 105 | return null; 106 | } 107 | return new File(applicationInfo.dataDir, TINKER_SERVER_DIR); 108 | } 109 | 110 | public static File getServerFile(Context context, String appVersion, String currentVersion) { 111 | return new File(getServerDirectory(context), appVersion + "_" + currentVersion + ".apk"); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/src/main/java/com/tencent/tinker/server/utils/VersionUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Shengjie Sim Sun 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.tencent.tinker.server.utils; 26 | 27 | import android.content.Context; 28 | 29 | import com.tencent.tinker.lib.util.TinkerLog; 30 | import com.tencent.tinker.loader.TinkerRuntimeException; 31 | import com.tencent.tinker.loader.shareutil.SharePatchFileUtil; 32 | 33 | import java.io.File; 34 | import java.io.FileInputStream; 35 | import java.io.FileOutputStream; 36 | import java.io.IOException; 37 | import java.util.Properties; 38 | import java.util.Random; 39 | import java.util.UUID; 40 | 41 | import static com.tencent.tinker.server.client.TinkerClientAPI.TAG; 42 | 43 | public final class VersionUtils { 44 | private static final String APP_VERSION = "app"; 45 | private static final String UUID_VALUE = "uuid"; 46 | private static final String GRAY_VALUE = "gray"; 47 | private static final String CURRENT_VERSION = "version"; 48 | private static final String CURRENT_MD5 = "md5"; 49 | 50 | 51 | private final File versionFile; 52 | private String uuid; 53 | private String appVersion; 54 | private Integer grayValue; 55 | private Integer patchVersion; 56 | private String patchMd5; 57 | 58 | 59 | public VersionUtils(Context context, String appVersion) { 60 | versionFile = new File(ServerUtils.getServerDirectory(context), ServerUtils.TINKER_VERSION_FILE); 61 | readVersionProperty(); 62 | 63 | if (!versionFile.exists() || uuid == null || appVersion == null || grayValue == null || patchVersion == null) { 64 | updateVersionProperty(appVersion, 0, "", randInt(1, 10), UUID.randomUUID().toString()); 65 | } else if (!appVersion.equals(this.appVersion)) { 66 | updateVersionProperty(appVersion, 0, "", grayValue, uuid); 67 | } 68 | } 69 | 70 | public boolean isInGrayGroup(Integer gray) { 71 | boolean result = gray == null || gray >= grayValue; 72 | TinkerLog.d(TAG, "isInGrayGroup return %b, gray value:%d and my gray value is %d", result, gray, grayValue); 73 | return result; 74 | } 75 | 76 | public boolean isUpdate(Integer version, String currentAppVersion) { 77 | if (!currentAppVersion.equals(appVersion)) { 78 | TinkerLog.d(TAG, "update return true, appVersion from %s to %s", appVersion, currentAppVersion); 79 | return true; 80 | } 81 | Integer current = getPatchVersion(); 82 | if (version > current) { 83 | TinkerLog.d(TAG, "update return true, patchVersion from %s to %s", current, version); 84 | return true; 85 | } else { 86 | TinkerLog.d(TAG, "update return false, target version is not latest. current version is:" + version); 87 | return false; 88 | } 89 | } 90 | 91 | public Integer getPatchVersion() { 92 | if (patchVersion == null) { 93 | return 0; 94 | } 95 | return patchVersion; 96 | } 97 | 98 | public String getPatchMd5() { 99 | if (patchMd5 == null) { 100 | return ""; 101 | } 102 | return patchMd5; 103 | } 104 | 105 | public String id() { 106 | return uuid; 107 | } 108 | 109 | public Integer grayValue() { 110 | return grayValue; 111 | } 112 | 113 | private int randInt(int min, int max) { 114 | Random rand = new Random(); 115 | return rand.nextInt((max - min) + 1) + min; 116 | } 117 | 118 | private void readVersionProperty() { 119 | if (versionFile == null || !versionFile.exists() || versionFile.length() == 0) { 120 | return; 121 | } 122 | 123 | Properties properties = new Properties(); 124 | FileInputStream inputStream = null; 125 | try { 126 | inputStream = new FileInputStream(versionFile); 127 | properties.load(inputStream); 128 | uuid = properties.getProperty(UUID_VALUE); 129 | appVersion = properties.getProperty(APP_VERSION); 130 | grayValue = ServerUtils.stringToInteger(properties.getProperty(GRAY_VALUE)); 131 | patchVersion = ServerUtils.stringToInteger(properties.getProperty(CURRENT_VERSION)); 132 | patchMd5 = properties.getProperty(CURRENT_MD5); 133 | } catch (IOException e) { 134 | TinkerLog.e(TAG, "readVersionProperty exception:" + e); 135 | } finally { 136 | SharePatchFileUtil.closeQuietly(inputStream); 137 | } 138 | 139 | } 140 | 141 | public void updateVersionProperty(String appVersion, int currentVersion, 142 | String patchMd5, int grayValue, String uuid) { 143 | TinkerLog.d(TAG, "updateVersionProperty file path:" 144 | + versionFile.getAbsolutePath() 145 | + " , appVersion: " + appVersion 146 | + " , patchVersion:" + currentVersion 147 | + " , patchMd5:" + patchMd5 148 | + " , grayValue:" + grayValue 149 | + " , uuid:" + uuid); 150 | 151 | File parentFile = versionFile.getParentFile(); 152 | if (!parentFile.exists() && !parentFile.mkdirs()) { 153 | throw new TinkerRuntimeException("make mkdirs error: " + parentFile.getAbsolutePath()); 154 | } 155 | 156 | Properties newProperties = new Properties(); 157 | newProperties.put(CURRENT_VERSION, String.valueOf(currentVersion)); 158 | newProperties.put(CURRENT_MD5, patchMd5); 159 | 160 | newProperties.put(GRAY_VALUE, String.valueOf(grayValue)); 161 | newProperties.put(APP_VERSION, appVersion); 162 | newProperties.put(UUID_VALUE, uuid); 163 | FileOutputStream outputStream = null; 164 | try { 165 | outputStream = new FileOutputStream(versionFile, false); 166 | String comment = "from old version:" + getPatchVersion() + " to new version:" + currentVersion; 167 | newProperties.store(outputStream, comment); 168 | } catch (Exception e) { 169 | e.printStackTrace(); 170 | } finally { 171 | SharePatchFileUtil.closeQuietly(outputStream); 172 | } 173 | //update value 174 | this.appVersion = appVersion; 175 | this.patchVersion = currentVersion; 176 | this.grayValue = grayValue; 177 | this.uuid = uuid; 178 | this.patchMd5 = patchMd5; 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/src/main/java/com/tinkerpatch/sdk/TinkerPatch.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2013-2016 Shengjie Sim Sun 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.tinkerpatch.sdk; 26 | 27 | 28 | import com.tencent.tinker.lib.util.TinkerLog; 29 | import com.tencent.tinker.loader.app.ApplicationLike; 30 | import com.tinkerpatch.sdk.server.callback.ConfigRequestCallback; 31 | import com.tinkerpatch.sdk.server.callback.RollbackCallBack; 32 | import com.tinkerpatch.sdk.tinker.callback.ResultCallBack; 33 | 34 | public abstract class TinkerPatch { 35 | 36 | /** 37 | * 设置Tinker相关Log的真正实现,用于自定义日志输出 38 | * @param imp 39 | */ 40 | public static void setLogIml(TinkerLog.TinkerLogImp imp) { 41 | // nothing 42 | } 43 | 44 | /** 45 | * 用默认的构造参数初始化TinkerPatch的SDK 46 | * @param applicationLike 47 | * @return 48 | */ 49 | public static TinkerPatch init(ApplicationLike applicationLike) { 50 | return null; 51 | } 52 | 53 | /** 54 | * 自定义参数初始化TinkerPatch的SDK 55 | * @param tinkerPatch 56 | * @return 57 | */ 58 | public static TinkerPatch init(TinkerPatch tinkerPatch) { 59 | return null; 60 | } 61 | 62 | /** 63 | * 获得TinkerPatch的实例 64 | * @return 65 | */ 66 | public static TinkerPatch with() { 67 | return null; 68 | } 69 | 70 | /** 71 | * 获得ApplicationLike的实例 72 | * @return 73 | */ 74 | public abstract ApplicationLike getApplcationLike(); 75 | 76 | /** 77 | * 反射补丁的Library path, 自动加载library 78 | * 是否自动反射Library路径,无须手动加载补丁中的So文件 79 | * 注意,调用在反射接口之后才能生效,你也可以使用Tinker的方式加载Library 80 | * @return 81 | */ 82 | public TinkerPatch reflectPatchLibrary() { 83 | return null; 84 | } 85 | 86 | /** 87 | * 向后台获得动态配置,默认的访问间隔为3个小时 88 | * 若参数为true,即每次调用都会真正的访问后台配置 89 | * 90 | * @param configRequestCallback 91 | * @param immediately 是否立刻请求,忽略时间间隔限制 92 | */ 93 | public TinkerPatch fetchDynamicConfig(final ConfigRequestCallback configRequestCallback, 94 | final boolean immediately) { 95 | return null; 96 | } 97 | 98 | /** 99 | * 获得当前的补丁版本, 100 | * 在TinkerPatch sdk 1.1.4 版本添加 101 | * 102 | * @return 当前补丁版本号。(此版本号由后台管理,且单调递增) 103 | */ 104 | public Integer getPatchVersion() { 105 | return 0; 106 | } 107 | 108 | /** 109 | * 向后台获取是否有补丁包更新,默认的访问间隔为3个小时 110 | * 若参数为true,即每次调用都会真正的访问后台配置 111 | * 112 | * @param immediately 是否立刻检查,忽略时间间隔限制 113 | */ 114 | public TinkerPatch fetchPatchUpdate(final boolean immediately) { 115 | return null; 116 | } 117 | 118 | /** 119 | * 设置当前渠道号,对于某些渠道我们可能会想屏蔽补丁功能 120 | * 设置渠道后,我们就可以使用后台的条件控制渠道更新 121 | * 122 | * @param channel 123 | * @return 124 | */ 125 | public TinkerPatch setAppChannel(String channel) { 126 | return null; 127 | } 128 | 129 | /** 130 | * 屏蔽部分渠道的补丁功能 131 | * 132 | * @param channel 133 | * @return 134 | */ 135 | public TinkerPatch addIgnoreAppChannel(String channel) { 136 | return null; 137 | } 138 | 139 | /** 140 | * 设置tinkerpatch平台的条件下发参数 141 | * 默认内置的条件有[wifi, sdk, brand, model, cpu, cpu] 142 | * 若调用了setAppChannel, 能增加[channel]条件 143 | * 144 | * @param key 145 | * @param value 146 | */ 147 | public TinkerPatch setPatchCondition(String key, String value) { 148 | return null; 149 | } 150 | 151 | /** 152 | * 设置访问后台动态配置的时间间隔,默认为3个小时 153 | * @param hours 154 | * @return 155 | */ 156 | public TinkerPatch setFetchDynamicConfigIntervalByHours(int hours) { 157 | return null; 158 | } 159 | 160 | /** 161 | * 设置访问后台补丁包更新配置的时间间隔,默认为3个小时 162 | * 163 | * @param hours 164 | * @return 165 | */ 166 | public TinkerPatch setFetchPatchIntervalByHours(int hours) { 167 | return null; 168 | } 169 | 170 | /** 171 | * 设置补丁合成成功后,是否通过锁屏重启程序,这样可以加快补丁的生效时间 172 | * 默认为false, 即等待应用自身重新启动时加载 173 | * 174 | * @param restartOnScreenOff 175 | * @return 176 | */ 177 | public TinkerPatch setPatchRestartOnSrceenOff(boolean restartOnScreenOff) { 178 | return null; 179 | } 180 | 181 | /** 182 | * 我们可以通过ResultCallBack设置对合成后的回调 183 | * 例如我们也可以不锁屏,而是在这里通过弹框咨询用户等方式 184 | * 185 | * @param resultCallBack 186 | * @return 187 | */ 188 | public TinkerPatch setPatchResultCallback(ResultCallBack resultCallBack) { 189 | return null; 190 | } 191 | 192 | /** 193 | * 设置收到后台回退要求时,是否在锁屏时清除补丁 194 | * 默认为false,即等待应用下一次重新启动时才会去清除补丁 195 | * 196 | * @param rollbackOnScreenOff 197 | * @return 198 | */ 199 | public TinkerPatch setPatchRollbackOnScreenOff(boolean rollbackOnScreenOff) { 200 | return null; 201 | } 202 | 203 | /** 204 | * 我们可以通过RollbackCallBack设置对回退时的回调 205 | * 206 | * @param rollbackCallBack 207 | * @return 208 | */ 209 | public TinkerPatch setPatchRollBackCallback(RollbackCallBack rollbackCallBack) { 210 | return null; 211 | } 212 | /** 213 | * 清除补丁 214 | * @return 215 | */ 216 | public TinkerPatch cleanPatch() { 217 | return null; 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/src/main/java/com/tinkerpatch/sdk/server/callback/ConfigRequestCallback.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2013-2016 Shengjie Sim Sun 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.tinkerpatch.sdk.server.callback; 26 | 27 | import java.util.HashMap; 28 | 29 | public interface ConfigRequestCallback { 30 | void onSuccess(HashMap configs); 31 | void onFail(Exception e); 32 | } 33 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/src/main/java/com/tinkerpatch/sdk/server/callback/PatchRequestCallback.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2013-2016 Shengjie Sim Sun 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.tinkerpatch.sdk.server.callback; 26 | 27 | import java.io.File; 28 | 29 | public interface PatchRequestCallback { 30 | /** 31 | * 在请求补丁前,我们可以在这个接口拦截请求 32 | * 33 | * @return 返回false, 即不会请求服务器 34 | */ 35 | boolean beforePatchRequest(); 36 | 37 | /** 38 | * 服务器有新的补丁,并且已经成功的下载 39 | * 40 | * @param file 下载好的补丁地址,存放在/data/data/app_name/tinker_server/ 41 | * @param newVersion 新的补丁版本 42 | * @param currentVersion 当前的补丁版本 43 | */ 44 | boolean onPatchUpgrade(File file, Integer newVersion, Integer currentVersion); 45 | 46 | /** 47 | * 向服务器请求新补丁时,下载补丁失败 48 | * 49 | * @param e 错误类型 50 | * @param newVersion 下载失败的新补丁版本 51 | * @param currentVersion 当前的补丁版本 52 | */ 53 | void onPatchDownloadFail(Exception e, Integer newVersion, Integer currentVersion); 54 | 55 | /** 56 | * 与服务器同步时失败 57 | * 58 | * @param e 失败类型 59 | */ 60 | void onPatchSyncFail(Exception e); 61 | 62 | /** 63 | * 收到服务器清除补丁的请求,如何清除本地补丁,可以自行判断 64 | */ 65 | void onPatchRollback(); 66 | 67 | /** 68 | * 若使用条件下发方式发布补丁,某些动态改变的条件,可以在这个接口更新。例如是否为wifi 69 | */ 70 | void updatePatchConditions(); 71 | } 72 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/src/main/java/com/tinkerpatch/sdk/server/callback/RollbackCallBack.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2013-2016 Shengjie Sim Sun 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.tinkerpatch.sdk.server.callback; 26 | 27 | /** 28 | * Created by zhangshaowen on 16/12/23. 29 | */ 30 | 31 | 32 | public interface RollbackCallBack { 33 | void onPatchRollback(); 34 | } 35 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/src/main/java/com/tinkerpatch/sdk/tinker/callback/ResultCallBack.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2013-2016 Shengjie Sim Sun 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.tinkerpatch.sdk.tinker.callback; 26 | 27 | import com.tencent.tinker.lib.service.PatchResult; 28 | 29 | /** 30 | * Created by zhangshaowen on 16/12/23. 31 | */ 32 | 33 | public interface ResultCallBack { 34 | void onPatchResult(final PatchResult result); 35 | } 36 | 37 | -------------------------------------------------------------------------------- /tinkerpatch-sdk/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | tinkerpatch-sdk 3 | 4 | --------------------------------------------------------------------------------