├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── LICENSE ├── README-zh.md ├── README.md ├── app ├── .gitignore ├── build.gradle ├── findbugs_exclude.xml ├── findbugs_include.xml ├── keystore.jks ├── proguard-rules.pro ├── robust.xml ├── robust │ ├── app-release.apk │ ├── baksmali-2.1.2.jar │ ├── dx.jar │ ├── methodsMap.robust │ ├── patch.jar │ ├── robust.apkhash │ └── smali-2.1.2.jar └── src │ ├── androidTest │ └── java │ │ └── meituan │ │ └── instantrun │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── meituan │ │ │ └── sample │ │ │ ├── MainActivity.java │ │ │ ├── PatchManipulateImp.java │ │ │ ├── PermissionUtils.java │ │ │ ├── RobustCallBackSample.java │ │ │ └── SecondActivity.java │ └── res │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── activity_main2.xml │ │ └── content_main2.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-v21 │ │ └── styles.xml │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── meituan │ └── instantrun │ └── ExampleUnitTest.java ├── auto-patch-plugin ├── build.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── src │ └── main │ ├── groovy │ ├── com │ │ └── meituan │ │ │ └── robust │ │ │ └── autopatch │ │ │ ├── InlineClassFactory.groovy │ │ │ ├── PatchesAssistFactory.groovy │ │ │ ├── PatchesFactory.groovy │ │ │ ├── ReadAnnotation.groovy │ │ │ ├── ReadXML.groovy │ │ │ └── ReflectUtils.groovy │ └── robust │ │ └── gradle │ │ └── plugin │ │ └── AutoPatchTransform.groovy │ ├── java │ └── com │ │ └── meituan │ │ └── robust │ │ ├── autopatch │ │ ├── ClassMapping.java │ │ ├── Config.java │ │ ├── NameManger.java │ │ ├── PatchesControlFactory.java │ │ ├── PatchesInfoFactory.java │ │ └── ReadMapping.java │ │ └── utils │ │ ├── JavaUtils.java │ │ └── SmaliTool.java │ └── resources │ ├── META-INF │ └── gradle-plugins │ │ └── auto-patch-plugin.properties │ └── libs │ ├── baksmali-2.1.2.jar │ ├── dx.jar │ └── smali-2.1.2.jar ├── autopatchbase ├── build.gradle └── src │ └── main │ └── java │ └── com │ └── meituan │ └── robust │ ├── ChangeQuickRedirect.java │ ├── Constants.java │ ├── RobustArguments.java │ ├── RobustExtension.java │ ├── patch │ ├── RobustModify.java │ └── annotaion │ │ ├── Add.java │ │ └── Modify.java │ └── utils │ ├── EnhancedRobustUtils.java │ └── PatchTemplate.java ├── build.gradle ├── gradle-plugin ├── build.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── src │ └── main │ ├── groovy │ └── robust │ │ └── gradle │ │ └── plugin │ │ ├── ConvertUtils.groovy │ │ ├── InsertcodeStrategy.java │ │ ├── RobustApkHashAction.groovy │ │ ├── RobustApkHashZipUtils.groovy │ │ ├── RobustTransform.groovy │ │ ├── asm │ │ ├── AsmInsertImpl.java │ │ └── RobustAsmUtils.java │ │ └── javaassist │ │ └── JavaAssistInsertImpl.java │ └── resources │ └── META-INF │ └── gradle-plugins │ └── robust.properties ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle_mvn_push.gradle ├── gradlew ├── gradlew.bat ├── images ├── Robust结构图.png ├── patchsuccess_cn.png ├── patchsuccess_en.png └── 补丁代码结构.png ├── patch ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── meituan │ └── robust │ ├── Patch.java │ ├── PatchExecutor.java │ ├── PatchManipulate.java │ ├── PatchProxy.java │ ├── PatchProxyResult.java │ ├── PatchedClassInfo.java │ ├── PatchesInfo.java │ ├── RobustApkHashUtils.java │ └── RobustCallBack.java └── settings.gradle /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 在提交issue之前,我们应该先查询是否已经有相关的issue和ReadMe中的注意事项,比如说Robust在0.3.3版本才支持windows开发。提交issue时,我们需要写明issue的原因,最好可以携带编译或运行过程的日志或者截图。issue最好以下面的格式提出: 2 | 3 | 异常类型:app运行时异常/编译异常 4 | 5 | 手机型号:如:Nexus 5(如是编译异常,则可以不填) 6 | 7 | 手机系统版本:如:Android 5.0 (如是编译异常,则可以不填) 8 | 9 | Robust版本:如:0.4.99 10 | 11 | Gradle版本:如:2.10 12 | 13 | 系统:如:Windows 14 | 15 | 堆栈/日志: 16 | 17 | 如是编译异常,请在执行gradle命令时,加上--stacktrace,并把结果重定向,例如在demo中重定向命令如下:./gradlew clean assembleRelease --stacktrace --no-daemon >log.txt ,结果重定向到当前的目录下的log.txt文件; 18 | 日志中我们需要过滤"robust"关键字,可以初步查找问题的大概原因; 19 | Robust提供了sample样例与我们的源码,大家在使用前可以先将样例跑通,如遇任何疑问也欢迎大家提出,更鼓励大家给我们提pr,谢谢大家的支持. -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Android template 3 | # Built application files 4 | 5 | *.ap_ 6 | 7 | # Files for the ART/Dalvik VM 8 | 9 | 10 | # Java class files 11 | *.class 12 | 13 | # Generated files 14 | bin/ 15 | gen/ 16 | out/ 17 | 18 | # Gradle files 19 | .gradle/ 20 | build/ 21 | 22 | # Local configuration file (sdk path, etc) 23 | local.properties 24 | 25 | # Proguard folder generated by Eclipse 26 | proguard/ 27 | 28 | # Log Files 29 | *.log 30 | 31 | # Android Studio Navigation editor temp files 32 | .navigation/ 33 | 34 | # Android Studio captures folder 35 | captures/ 36 | 37 | # Intellij 38 | *.iml 39 | .idea/workspace.xml 40 | 41 | # Keystore files 42 | 43 | .idea/ 44 | 45 | *.DS_Store -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README-zh.md: -------------------------------------------------------------------------------- 1 | 2 | # Robust 3 | [![Download](https://api.bintray.com/packages/meituan/maven/com.meituan.robust%3Apatch/images/download.svg?version=0.4.99) ](https://bintray.com/meituan/maven/com.meituan.robust%3Apatch/0.4.99/link) 4 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/Meituan-Dianping/Robust/pulls) 5 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://raw.githubusercontent.com/Meituan-Dianping/Robust/master/LICENSE) 6 | 7 | 8 | Robust是新一代热更新系统,无差别兼容Android2.3-10版本;无需重启补丁实时生效,快速修复线上问题,补丁修补成功率高达99.9%。 9 | 10 | [English Introduction](README.md) 11 | 12 | 关于如何自定义以及常见问题的解决,请参看 [Wiki](https://github.com/Meituan-Dianping/Robust/wiki) 13 | 14 | # 环境 15 | 16 | * Mac Linux Windows 17 | * Gradle 2.10+ , include 3.0 18 | * Java 1.7 + 19 | 20 | # 使用方法 21 | 22 | 1. 在App的build.gradle,加入如下依赖 23 | 24 | ```java 25 | apply plugin: 'com.android.application' 26 | //制作补丁时将这个打开,auto-patch-plugin紧跟着com.android.application 27 | //apply plugin: 'auto-patch-plugin' 28 | apply plugin: 'robust' 29 | 30 | 31 | compile 'com.meituan.robust:robust:0.4.99' 32 | 33 | ``` 34 | 2. 在整个项目的build.gradle加入classpath 35 | 36 | ```java 37 | buildscript { 38 | repositories { 39 | jcenter() 40 | } 41 | dependencies { 42 | classpath 'com.meituan.robust:gradle-plugin:0.4.99' 43 | classpath 'com.meituan.robust:auto-patch-plugin:0.4.99' 44 | } 45 | } 46 | ``` 47 | 3. 在项目的src同级目录下配置robust.xml文件,具体项请参考DEMO**app/robust.xml** 48 | 49 | # 优势 50 | 51 | * 支持Android2.3-10版本 52 | * 高兼容性、高稳定性,修复成功率高达99.9% 53 | * 补丁实时生效,不需要重新启动 54 | * 支持方法级别的修复,包括静态方法 55 | * 支持增加方法和类 56 | * 支持ProGuard的混淆、内联、优化等操作 57 | 58 | 需要保存打包时生成的mapping文件以及**build/outputs/robust/methodsMap.robust**文件 59 | # 注意 gradle 3.6及以上版本默认启用R8,会将插入的ChangeQuickRedirect变量优化掉,需要在混淆文件proguard-rules.pro中加入以下代码。 60 | 61 | -keepclassmembers class **{ 62 | public static com.meituan.robust.ChangeQuickRedirect *; 63 | } 64 | 65 | # AutoPatch 66 | 67 | 68 | Robust补丁自动化,为Robust自动生成补丁,使用者只需要提交修改完bug后的代码,运行和线上apk打包同样的gradle命令即可,会在项目的app/build/outputs/robust目录下生成补丁。更多自动化补丁信息请参考:[Android热更新方案Robust开源,新增自动化补丁工具](http://tech.meituan.com/android_autopatch.html) 。 69 | 70 | # 使用方法 71 | 72 | 1. 使用插件时,需要把auto-patch-plugin放置在com.android.application插件之后,其余插件之前。 73 | 74 | ```java 75 | apply plugin: 'com.android.application' 76 | apply plugin: 'auto-patch-plugin' 77 | ``` 78 | 2. 将保存下来的mapping文件和methodsMap.robust文件放在app/robust/文件夹下。 79 | 80 | 3. 修改代码,在改动的方法上面添加```@Modify```注解,对于Lambda表达式请在修改的方法里面调用RobustModify.modify()方法 81 | 82 | ```java 83 | @Modify 84 | protected void onCreate(Bundle savedInstanceState) { 85 | super.onCreate(savedInstanceState); 86 | } 87 | //或者是被修改的方法里面调用RobustModify.modify()方法 88 | protected void onCreate(Bundle savedInstanceState) { 89 | RobustModify.modify() 90 | super.onCreate(savedInstanceState); 91 | } 92 | 93 | ``` 94 | 95 | 新增的方法和字段使用`@Add`注解 96 | 97 | ```java 98 | //增加方法 99 | @Add 100 | public String getString() { 101 | return "Robust"; 102 | } 103 | //增加类 104 | @Add 105 | public class NewAddCLass { 106 | public static String get() { 107 | return "robust"; 108 | } 109 | } 110 | ``` 111 | 4. 运行和生成线上apk同样的命令,即可生成补丁,补丁目录app/build/outputs/robust/patch.jar 112 | 5. 补丁制作成功后会停止构建apk,出现类似于如下的提示,表示补丁生成成功 113 | ![补丁制作成功图片](images/patchsuccess_cn.png) 114 | 115 | # 样例使用: 116 | 1. 生成样例apk,执行gradle命令: 117 | 118 | ```java 119 | ./gradlew clean assembleRelease --stacktrace --no-daemon 120 | ``` 121 | 2. 安装样例apk。保存mapping.txt文件以及app/build/outputs/robust/methodsMap.robust文件 122 | 3. 修改代码之后,加上**@Modify**注解或者调用RobustModify.modify()方法 123 | 4. 把保存的**mapping.txt**和**methodsMap.robust**放到app/robust目录下 124 | 5. 执行与生成样式apk相同的gradle命令: 125 | 126 | ```java 127 | ./gradlew clean assembleRelease --stacktrace --no-daemon 128 | ``` 129 | 5. 补丁制作成功后会停止构建apk,出现类似于如下的提示,表示补丁生成成功 130 | ![补丁制作成功图片](images/patchsuccess_cn.png) 131 | 7. 将补丁文件copy到手机目录/sdcard/robust下 132 | 133 | ```java 134 | adb push ~/Desktop/code/robust/app/build/outputs/robust/patch.jar /sdcard/robust/patch.jar 135 | ``` 136 | 补丁的路径/sdcard/robust是`PatchManipulateImp`中指定的 137 | 8. 打开App,点击Patch按钮就会加载补丁。 138 | 9. 也可以加载app/robust的样例补丁,修改了Jump_second_Activity跳转Activity的显示文字。 139 | 10. 在样例中我们给类```SecondActivity```的方法```getTextInfo(String meituan)```制作补丁,你可以自行定制。 140 | 141 | # 注意事项 142 | 143 | 1. 内部类的构造方法是private(private会生成一个匿名的构造函数)时,需要在制作补丁过程中手动修改构造方法的访问域为public 144 | 2. 对于方法的返回值是this的情况现在支持不好,比如builder模式,但在制作补丁代码时,可以通过如下方式来解决,增加一个类来包装一下(如下面的B类), 145 | 146 | ```java 147 | method a(){ 148 | return this; 149 | } 150 | ``` 151 | 改为 152 | 153 | ```java 154 | method a(){ 155 | return new B().setThis(this).getThis(); 156 | } 157 | ``` 158 | 3. 字段增加能力内测中,不过暂时可以通过增加新类,把字段放到新类中的方式来实现字段增加能力 159 | 4. 新增的类支持包括静态内部类和非内部类 160 | 5. 对于只有字段访问的函数无法直接修复,可通过调用处间接修复 161 | 6. 构造方法的修复内测中 162 | 7. 资源和so的修复内测中 163 | 8. 更多的信息,请访问我们的[Wiki](https://github.com/Meituan-Dianping/Robust/wiki) 164 | 165 | ## License 166 | 167 | Copyright 2017 Meituan-Dianping 168 | 169 | Licensed under the Apache License, Version 2.0 (the "License"); 170 | you may not use this file except in compliance with the License. 171 | You may obtain a copy of the License at 172 | 173 | http://www.apache.org/licenses/LICENSE-2.0 174 | 175 | Unless required by applicable law or agreed to in writing, software 176 | distributed under the License is distributed on an "AS IS" BASIS, 177 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 178 | See the License for the specific language governing permissions and 179 | limitations under the License. 180 | 181 | 182 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Robust 3 | 4 | [![Download](https://api.bintray.com/packages/meituan/maven/com.meituan.robust%3Apatch/images/download.svg?version=0.4.99) ](https://bintray.com/meituan/maven/com.meituan.robust%3Apatch/0.4.99/link) 5 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/Meituan-Dianping/Robust/pulls) 6 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://raw.githubusercontent.com/Meituan-Dianping/Robust/master/LICENSE) 7 | 8 | Robust is an Android HotFix solution with high compatibility and high stability. Robust can fix bugs immediately without publishing apk. 9 | 10 | [中文说明](README-zh.md) 11 | 12 | More help on [Wiki](https://github.com/Meituan-Dianping/Robust/wiki) 13 | 14 | # Environment 15 | 16 | * Mac Linux and Windows 17 | * Gradle 2.10+ , include 3.0 18 | * Java 1.7 + 19 | 20 | # Usage 21 | 22 | 1. Add below codes in the module's build.gradle. 23 | 24 | ```java 25 | apply plugin: 'com.android.application' 26 | //please uncomment fellow line before you build a patch 27 | //apply plugin: 'auto-patch-plugin' 28 | apply plugin: 'robust' 29 | 30 | compile 'com.meituan.robust:robust:0.4.99' 31 | 32 | 2. Add below codes in the outest project's build.gradle file. 33 | 34 | ```java 35 | buildscript { 36 | repositories { 37 | jcenter() 38 | } 39 | dependencies { 40 | classpath 'com.meituan.robust:gradle-plugin:0.4.99' 41 | classpath 'com.meituan.robust:auto-patch-plugin:0.4.99' 42 | } 43 | } 44 | ``` 45 | 3. There are some configure items in **app/robust.xml**,such as classes which Robust will insert code,this may diff from projects to projects.Please copy this file to your project. 46 | 47 | # Advantages 48 | 49 | * Support 2.3 to 10 Android OS 50 | * Perfect compatibility 51 | * Patch takes effect without a reboot 52 | * Support fixing at method level,including static methods 53 | * Support add classes and methods 54 | * Support ProGuard,including inline methods or changing methods' signature 55 | 56 | 57 | 58 | When you build APK,you may need to save "mapping.txt" and the files in directory "build/outputs/robust/". 59 | 60 | # AutoPatch 61 | 62 | 63 | AutoPatch will generate patch for Robust automatically. You just need to fellow below steps to genrate patches. For more details please visit website http://tech.meituan.com/android_autopatch.html 64 | 65 | # Steps 66 | 67 | 1. Put **'auto-patch-plugin'** just behind **'com.android.application'**,but in the front of others plugins。like this: 68 | 69 | ```java 70 | apply plugin: 'com.android.application' 71 | apply plugin: 'auto-patch-plugin' 72 | ``` 73 | 2. Put **mapping.txt** and **methodsMap.robust** which are generated when you build the apks in diretory **app/robust/**,if not exists ,create it! 74 | 3. After modifying the code ,please put annotation `@Modify` on the modified methods or invoke `RobustModify.modify()` (designed for Lambda Expression )in the modified methods: 75 | 76 | ```java 77 | @Modify 78 | protected void onCreate(Bundle savedInstanceState) { 79 | super.onCreate(savedInstanceState); 80 | } 81 | // 82 | protected void onCreate(Bundle savedInstanceState) { 83 | RobustModify.modify() 84 | super.onCreate(savedInstanceState); 85 | } 86 | 87 | ``` 88 | Use annotation `@Add` when you neeed to add methods or classes. 89 | 90 | ```java 91 | //add method 92 | @Add 93 | public String getString() { 94 | return "Robust"; 95 | } 96 | 97 | //add class 98 | 99 | @Add 100 | public class NewAddCLass { 101 | public static String get() { 102 | return "robust"; 103 | } 104 | } 105 | ``` 106 | 4. After those steps,you need to run the same gradle command as you build the apk,then you will get patches in directory **app/build/outputs/robust/patch.jar**. 107 | 5. Generating patches always end like this,which means patches is done 108 | ![Success in generating patch](images/patchsuccess_en.png) 109 | 110 | # Demo Usage 111 | 1. Excute fellow command to build apk: 112 | 113 | ```java 114 | ./gradlew clean assembleRelease --stacktrace --no-daemon 115 | ``` 116 | 2. After install apk on your phone,you need to save **mapping.txt** and **app/build/outputs/robust/methodsMap.robust** 117 | 3. Put mapping.txt and methodsMap.robust which are generated when you build the apks into diretory **app/robust/**,if directory not exists ,create it! 118 | 4. After modifying the code ,please put annotation `@Modify` on the modified methods or invoke `RobustModify.modify()` (designed for Lambda Expression )in the modified methods. 119 | 5. Run the same gradle command as you build the apk: 120 | 121 | ```java 122 | ./gradlew clean assembleRelease --stacktrace --no-daemon 123 | ``` 124 | 6. Generating patches always end like this,which means patches is done 125 | ![Success in generating patch](images/patchsuccess_en.png) 126 | 7. Copy patch to your phone: 127 | 128 | ```java 129 | adb push ~/Desktop/code/robust/app/build/outputs/robust/patch.jar /sdcard/robust/patch.jar 130 | ``` 131 | patch directory can be configured in ``PatchManipulateImp``. 132 | 8. Open app,and click **Patch** button,patch is used. 133 | 9. Also you can use our sample patch in **app/robust/sample_patch.jar** ,this dex change text after you click **Jump_second_Activity** Button. 134 | 10. In the demo ,we change the text showed on the second activity which is configured in the method ```getTextInfo(String meituan)``` in class ```SecondActivity``` 135 | 136 | # Attentions 137 | 138 | 1. You should modify inner classes' private constructors to public modifier. 139 | 2. AutoPatch cannot handle situations which method returns **this**,you may need to wrap it like belows: 140 | 141 | ```java 142 | method a(){ 143 | return this; 144 | } 145 | ``` 146 | changed to 147 | 148 | ```java 149 | method a(){ 150 | return new B().setThis(this).getThis(); 151 | } 152 | ``` 153 | 3. Not Support add fields,but you can add classes currently, this feature is under testing. 154 | 4. Classes added in patch should be static nested classes or non-inner classes,and all fields and methods in added class should be public. 155 | 5. Support to fix bugs in constructors currently is under testing. 156 | 6. Not support methods which only use fields,without method call or new expression. 157 | 7. Support to resources and so file is under testing. 158 | 8. For more help, please visit [Wiki](https://github.com/Meituan-Dianping/Robust/wiki) 159 | ## License 160 | 161 | Copyright 2017 Meituan-Dianping 162 | 163 | Licensed under the Apache License, Version 2.0 (the "License"); 164 | you may not use this file except in compliance with the License. 165 | You may obtain a copy of the License at 166 | 167 | http://www.apache.org/licenses/LICENSE-2.0 168 | 169 | Unless required by applicable law or agreed to in writing, software 170 | distributed under the License is distributed on an "AS IS" BASIS, 171 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 172 | See the License for the specific language governing permissions and 173 | limitations under the License. 174 | 175 | 176 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | *.txt 3 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'me.tatarka.retrolambda' 3 | //apply plugin: 'auto-patch-plugin' 4 | apply plugin: 'robust' 5 | 6 | android { 7 | compileSdkVersion 25 8 | buildToolsVersion '25.0.2' 9 | defaultConfig { 10 | applicationId "com.meituan.robust.sample" 11 | minSdkVersion 9 12 | targetSdkVersion 25 13 | versionCode 1 14 | versionName "1.0" 15 | } 16 | lintOptions { 17 | checkReleaseBuilds false 18 | // Or, if you prefer, you can continue to check for errors in release builds, 19 | // but continue the build even when errors are found: 20 | abortOnError false 21 | } 22 | 23 | signingConfigs { 24 | meituan { 25 | storeFile file("keystore.jks") 26 | storePassword "robust" 27 | keyAlias "robust" 28 | keyPassword "robust" 29 | } 30 | } 31 | buildTypes { 32 | release { 33 | // minifyEnabled false 34 | minifyEnabled true 35 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 36 | signingConfig signingConfigs.meituan 37 | } 38 | debug { 39 | minifyEnabled true 40 | // minifyEnabled false 41 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 42 | } 43 | } 44 | 45 | 46 | compileOptions { 47 | sourceCompatibility JavaVersion.VERSION_1_8 48 | targetCompatibility JavaVersion.VERSION_1_8 49 | 50 | } 51 | packagingOptions { 52 | exclude 'META-INF/maven/com.google.guava/guava/pom.properties' 53 | exclude 'META-INF/maven/com.google.guava/guava/pom.xml' 54 | exclude 'META-INF/maven/commons-cli/commons-cli/pom.properties' 55 | exclude 'META-INF/maven/commons-cli/commons-cli/pom.xml' 56 | } 57 | } 58 | configurations { 59 | all*.exclude group: 'com.google.guava', module: 'guava' 60 | } 61 | configurations.all { 62 | resolutionStrategy.cacheChangingModulesFor 0, 'seconds' 63 | } 64 | dependencies { 65 | compile 'com.google.code.gson:gson:2.7' 66 | compile 'com.squareup.picasso:picasso:2.5.2' 67 | compile fileTree(dir: 'libs', include: ['*.jar']) 68 | testCompile 'junit:junit:4.12' 69 | compile 'com.android.support:appcompat-v7:25.3.1' 70 | compile 'com.squareup.okhttp:okhttp:2.5.0' 71 | compile 'com.google.code.gson:gson:2.3.1' 72 | compile 'com.android.support:design:25.4.0' 73 | compile 'org.javassist:javassist:3.20.0-GA' 74 | compile project(path: ':patch') 75 | } -------------------------------------------------------------------------------- /app/findbugs_exclude.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 | -------------------------------------------------------------------------------- /app/findbugs_include.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/keystore.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Meituan-Dianping/Robust/955adcc21e4fbcb52054a8f7f4bbb11f462aeb2f/app/keystore.jks -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/wukun/android-sdk-mac_x86/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 | -dontwarn 19 | -keepattributes Signature,SourceFile,LineNumberTable 20 | -keepattributes *Annotation* 21 | -keeppackagenames 22 | -ignorewarnings 23 | -dontwarn android.support.v4.**,**CompatHoneycomb,com.tenpay.android.** 24 | -optimizations !class/unboxing/enum,!code/simplification/arithmetic 25 | 26 | -------------------------------------------------------------------------------- /app/robust.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | true 8 | 9 | 10 | 11 | 12 | 13 | false 14 | 15 | 16 | 17 | 18 | false 19 | 20 | 21 | true 22 | 23 | 24 | 25 | 26 | false 27 | 28 | 29 | true 30 | 31 | 32 | 33 | true 34 | 35 | 36 | 37 | true 38 | 39 | 40 | 41 | 42 | 44 | 45 | com.meituan 46 | com.sankuai 47 | com.dianping 48 | 49 | 50 | 51 | 52 | com.meituan.robust 53 | com.meituan.sample.extension 54 | 55 | 56 | 58 | 59 | com.meituan.robust.patch 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /app/robust/app-release.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Meituan-Dianping/Robust/955adcc21e4fbcb52054a8f7f4bbb11f462aeb2f/app/robust/app-release.apk -------------------------------------------------------------------------------- /app/robust/baksmali-2.1.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Meituan-Dianping/Robust/955adcc21e4fbcb52054a8f7f4bbb11f462aeb2f/app/robust/baksmali-2.1.2.jar -------------------------------------------------------------------------------- /app/robust/dx.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Meituan-Dianping/Robust/955adcc21e4fbcb52054a8f7f4bbb11f462aeb2f/app/robust/dx.jar -------------------------------------------------------------------------------- /app/robust/methodsMap.robust: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Meituan-Dianping/Robust/955adcc21e4fbcb52054a8f7f4bbb11f462aeb2f/app/robust/methodsMap.robust -------------------------------------------------------------------------------- /app/robust/patch.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Meituan-Dianping/Robust/955adcc21e4fbcb52054a8f7f4bbb11f462aeb2f/app/robust/patch.jar -------------------------------------------------------------------------------- /app/robust/robust.apkhash: -------------------------------------------------------------------------------- 1 | 3be3e1e0520d6de6b90edab1b7c2fd7c -------------------------------------------------------------------------------- /app/robust/smali-2.1.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Meituan-Dianping/Robust/955adcc21e4fbcb52054a8f7f4bbb11f462aeb2f/app/robust/smali-2.1.2.jar -------------------------------------------------------------------------------- /app/src/androidTest/java/meituan/instantrun/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package meituan.instantrun; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/meituan/sample/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.meituan.sample; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.annotation.NonNull; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.view.View; 8 | import android.widget.Button; 9 | import android.widget.TextView; 10 | import android.widget.Toast; 11 | 12 | import com.meituan.robust.PatchExecutor; 13 | /** 14 | * For users of Robust you may only to use MainActivity or SecondActivity,other classes are used for test.
15 | *
16 | * If you just want to use Robust ,we recommend you just focus on MainActivity SecondActivity and PatchManipulateImp.Especially three buttons in MainActivity
17 | *
18 | * in the MainActivity have three buttons; "SHOW TEXT " Button will change the text in the MainActivity,you can patch the show text.
19 | *
20 | * "PATCH" button will load the patch ,the patch path can be configured in PatchManipulateImp.
21 | *
22 | * "JUMP_SECOND_ACTIVITY" button will jump to the second ACTIVITY,so you can patch a Activity.
23 | *
24 | * Attention to this ,We recommend that one patch is just for one built apk ,because every built apk has its unique mapping.txt and resource id
25 | * 26 | * @author mivanzhang 27 | */ 28 | 29 | public class MainActivity extends AppCompatActivity { 30 | 31 | TextView textView; 32 | Button button; 33 | 34 | @Override 35 | protected void onCreate(Bundle savedInstanceState) { 36 | super.onCreate(savedInstanceState); 37 | setContentView(R.layout.activity_main); 38 | 39 | button = (Button) findViewById(R.id.button); 40 | textView = (TextView) findViewById(R.id.textView); 41 | Button patch = (Button) findViewById(R.id.patch); 42 | //beigin to patch 43 | patch.setOnClickListener(new View.OnClickListener() { 44 | @Override 45 | public void onClick(View v) { 46 | if (isGrantSDCardReadPermission()) { 47 | runRobust(); 48 | } else { 49 | requestPermission(); 50 | } 51 | } 52 | }); 53 | 54 | findViewById(R.id.jump_second_activity).setOnClickListener(new View.OnClickListener() { 55 | @Override 56 | public void onClick(View v) { 57 | Intent intent = new Intent(MainActivity.this, SecondActivity.class); 58 | startActivity(intent); 59 | } 60 | }); 61 | 62 | button.setOnClickListener(new View.OnClickListener() { 63 | @Override 64 | public void onClick(View v) { 65 | Toast.makeText(MainActivity.this, "arrived in ", Toast.LENGTH_SHORT).show(); 66 | } 67 | }); 68 | 69 | } 70 | 71 | private boolean isGrantSDCardReadPermission() { 72 | return PermissionUtils.isGrantSDCardReadPermission(this); 73 | } 74 | 75 | private void requestPermission() { 76 | PermissionUtils.requestSDCardReadPermission(this, REQUEST_CODE_SDCARD_READ); 77 | } 78 | 79 | private static final int REQUEST_CODE_SDCARD_READ = 1; 80 | 81 | @Override 82 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { 83 | switch (requestCode) { 84 | case REQUEST_CODE_SDCARD_READ: 85 | handlePermissionResult(); 86 | break; 87 | 88 | default: 89 | break; 90 | } 91 | } 92 | 93 | private void handlePermissionResult() { 94 | if (isGrantSDCardReadPermission()) { 95 | runRobust(); 96 | } else { 97 | Toast.makeText(this, "failure because without sd card read permission", Toast.LENGTH_SHORT).show(); 98 | } 99 | 100 | } 101 | 102 | private void runRobust() { 103 | new PatchExecutor(getApplicationContext(), new PatchManipulateImp(), new RobustCallBackSample()).start(); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /app/src/main/java/com/meituan/sample/PatchManipulateImp.java: -------------------------------------------------------------------------------- 1 | package com.meituan.sample; 2 | 3 | import android.content.Context; 4 | import android.os.Environment; 5 | import android.util.Log; 6 | 7 | import com.meituan.robust.Patch; 8 | import com.meituan.robust.PatchManipulate; 9 | import com.meituan.robust.RobustApkHashUtils; 10 | 11 | import java.io.File; 12 | import java.io.FileInputStream; 13 | import java.io.FileOutputStream; 14 | import java.io.IOException; 15 | import java.io.InputStream; 16 | import java.io.OutputStream; 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | 20 | /** 21 | * Created by mivanzhang on 17/2/27. 22 | * 23 | * We recommend you rewrite your own PatchManipulate class ,adding your special patch Strategy,in the demo we just load the patch directly 24 | * 25 | *
26 | * Pay attention to the difference of patch's LocalPath and patch's TempPath 27 | * 28 | *
29 | * We recommend LocalPath store the origin patch.jar which may be encrypted,while TempPath is the true runnable jar 30 | *
31 | *
32 | * 我们推荐继承PatchManipulate实现你们App独特的A补丁加载策略,其中setLocalPath设置补丁的原始路径,这个路径存储的补丁是加密过得,setTempPath存储解密之后的补丁,是可以执行的jar文件 33 | *
34 | * setTempPath设置的补丁加载完毕即刻删除,如果不需要加密和解密补丁,两者没有啥区别 35 | */ 36 | 37 | public class PatchManipulateImp extends PatchManipulate { 38 | /*** 39 | * connect to the network ,get the latest patches 40 | * l联网获取最新的补丁 41 | * @param context 42 | * 43 | * @return 44 | */ 45 | @Override 46 | protected List fetchPatchList(Context context) { 47 | //将app自己的robustApkHash上报给服务端,服务端根据robustApkHash来区分每一次apk build来给app下发补丁 48 | //apkhash is the unique identifier for apk,so you cannnot patch wrong apk. 49 | String robustApkHash = RobustApkHashUtils.readRobustApkHash(context); 50 | Log.w("robust","robustApkHash :" + robustApkHash); 51 | //connect to network to get patch list on servers 52 | //在这里去联网获取补丁列表 53 | Patch patch = new Patch(); 54 | patch.setName("123"); 55 | //we recommend LocalPath store the origin patch.jar which may be encrypted,while TempPath is the true runnable jar 56 | //LocalPath是存储原始的补丁文件,这个文件应该是加密过的,TempPath是加密之后的,TempPath下的补丁加载完毕就删除,保证安全性 57 | //这里面需要设置一些补丁的信息,主要是联网的获取的补丁信息。重要的如MD5,进行原始补丁文件的简单校验,以及补丁存储的位置,这边推荐把补丁的储存位置放置到应用的私有目录下,保证安全性 58 | patch.setLocalPath(Environment.getExternalStorageDirectory().getPath()+ File.separator+"robust"+File.separator + "patch"); 59 | 60 | //setPatchesInfoImplClassFullName 设置项各个App可以独立定制,需要确保的是setPatchesInfoImplClassFullName设置的包名是和xml配置项patchPackname保持一致,而且类名必须是:PatchesInfoImpl 61 | //请注意这里的设置 62 | patch.setPatchesInfoImplClassFullName("com.meituan.robust.patch.PatchesInfoImpl"); 63 | List patches = new ArrayList(); 64 | patches.add(patch); 65 | return patches; 66 | } 67 | 68 | /** 69 | * 70 | * @param context 71 | * @param patch 72 | * @return 73 | * 74 | * you can verify your patches here 75 | */ 76 | @Override 77 | 78 | protected boolean verifyPatch(Context context, Patch patch) { 79 | //do your verification, put the real patch to patch 80 | //放到app的私有目录 81 | patch.setTempPath(context.getCacheDir()+ File.separator+"robust"+File.separator + "patch"); 82 | //in the sample we just copy the file 83 | try { 84 | copy(patch.getLocalPath(), patch.getTempPath()); 85 | }catch (Exception e){ 86 | e.printStackTrace(); 87 | throw new RuntimeException("copy source patch to local patch error, no patch execute in path "+patch.getTempPath()); 88 | } 89 | 90 | return true; 91 | } 92 | public void copy(String srcPath,String dstPath) throws IOException { 93 | File src=new File(srcPath); 94 | if(!src.exists()){ 95 | throw new RuntimeException("source patch does not exist "); 96 | } 97 | File dst=new File(dstPath); 98 | if(!dst.getParentFile().exists()){ 99 | dst.getParentFile().mkdirs(); 100 | } 101 | InputStream in = new FileInputStream(src); 102 | try { 103 | OutputStream out = new FileOutputStream(dst); 104 | try { 105 | // Transfer bytes from in to out 106 | byte[] buf = new byte[1024]; 107 | int len; 108 | while ((len = in.read(buf)) > 0) { 109 | out.write(buf, 0, len); 110 | } 111 | } finally { 112 | out.close(); 113 | } 114 | } finally { 115 | in.close(); 116 | } 117 | } 118 | /** 119 | * 120 | * @param patch 121 | * @return 122 | * 123 | * you may download your patches here, you can check whether patch is in the phone 124 | */ 125 | @Override 126 | protected boolean ensurePatchExist(Patch patch) { 127 | return true; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /app/src/main/java/com/meituan/sample/PermissionUtils.java: -------------------------------------------------------------------------------- 1 | package com.meituan.sample; 2 | 3 | import android.Manifest; 4 | import android.app.Activity; 5 | import android.content.Context; 6 | import android.content.pm.PackageManager; 7 | import android.support.v4.app.ActivityCompat; 8 | import android.support.v4.content.ContextCompat; 9 | 10 | /** 11 | * Created by hedingxu on 17/3/11. 12 | */ 13 | public class PermissionUtils { 14 | /** 15 | * 是否有权限 16 | * 17 | * @param context 18 | * @return 19 | */ 20 | public static boolean checkSelfPermission(Context context, String permission) { 21 | if (null == context) { 22 | return false; 23 | } 24 | int per = ContextCompat.checkSelfPermission(context, permission); 25 | return per == PackageManager.PERMISSION_GRANTED; 26 | } 27 | 28 | /** 29 | * Check that all given permissions have been granted by verifying that each entry in the 30 | * given array is of the value {@link PackageManager#PERMISSION_GRANTED}. 31 | * 32 | * @see Activity#onRequestPermissionsResult(int, String[], int[]) 33 | */ 34 | public static boolean verifyPermissions(int[] grantResults) { 35 | // At least one result must be checked. 36 | if (null == grantResults || grantResults.length < 1) { 37 | return false; 38 | } 39 | 40 | // Verify that each required permission has been granted, otherwise return false. 41 | for (int result : grantResults) { 42 | if (result != PackageManager.PERMISSION_GRANTED) { 43 | return false; 44 | } 45 | } 46 | return true; 47 | } 48 | 49 | public static boolean isGrantSDCardReadPermission(Context context) { 50 | return checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE); 51 | } 52 | 53 | public static void requestSDCardReadPermission(Activity activity, int requestCode) { 54 | ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, requestCode); 55 | } 56 | } -------------------------------------------------------------------------------- /app/src/main/java/com/meituan/sample/RobustCallBackSample.java: -------------------------------------------------------------------------------- 1 | package com.meituan.sample; 2 | 3 | import android.util.Log; 4 | 5 | import com.meituan.robust.Patch; 6 | import com.meituan.robust.RobustCallBack; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Created by hedingxu on 17/11/26. 12 | */ 13 | public class RobustCallBackSample implements RobustCallBack { 14 | 15 | @Override 16 | public void onPatchListFetched(boolean result, boolean isNet, List patches) { 17 | Log.d("RobustCallBack", "onPatchListFetched result: " + result); 18 | Log.d("RobustCallBack", "onPatchListFetched isNet: " + isNet); 19 | for (Patch patch : patches) { 20 | Log.d("RobustCallBack", "onPatchListFetched patch: " + patch.getName()); 21 | } 22 | } 23 | 24 | @Override 25 | public void onPatchFetched(boolean result, boolean isNet, Patch patch) { 26 | Log.d("RobustCallBack", "onPatchFetched result: " + result); 27 | Log.d("RobustCallBack", "onPatchFetched isNet: " + isNet); 28 | Log.d("RobustCallBack", "onPatchFetched patch: " + patch.getName()); 29 | } 30 | 31 | @Override 32 | public void onPatchApplied(boolean result, Patch patch) { 33 | Log.d("RobustCallBack", "onPatchApplied result: " + result); 34 | Log.d("RobustCallBack", "onPatchApplied patch: " + patch.getName()); 35 | 36 | } 37 | 38 | @Override 39 | public void logNotify(String log, String where) { 40 | Log.d("RobustCallBack", "logNotify log: " + log); 41 | Log.d("RobustCallBack", "logNotify where: " + where); 42 | } 43 | 44 | @Override 45 | public void exceptionNotify(Throwable throwable, String where) { 46 | Log.e("RobustCallBack", "exceptionNotify where: " + where, throwable); 47 | } 48 | } -------------------------------------------------------------------------------- /app/src/main/java/com/meituan/sample/SecondActivity.java: -------------------------------------------------------------------------------- 1 | package com.meituan.sample; 2 | 3 | import android.content.Context; 4 | import android.os.Bundle; 5 | import android.os.Handler; 6 | import android.support.annotation.NonNull; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.util.AttributeSet; 9 | import android.util.Log; 10 | import android.view.View; 11 | import android.widget.ArrayAdapter; 12 | import android.widget.BaseAdapter; 13 | import android.widget.ListView; 14 | import android.widget.TextView; 15 | import android.widget.Toast; 16 | 17 | import com.meituan.robust.patch.annotaion.Add; 18 | import com.meituan.robust.patch.annotaion.Modify; 19 | 20 | import java.lang.reflect.Field; 21 | 22 | public class SecondActivity extends AppCompatActivity implements View.OnClickListener { 23 | 24 | protected static String name = "SecondActivity"; 25 | private ListView listView; 26 | private String[] multiArr = {"列表1", "列表2", "列表3", "列表4"}; 27 | @Override 28 | protected void onCreate(Bundle savedInstanceState) { 29 | super.onCreate(savedInstanceState); 30 | setContentView(R.layout.activity_main2); 31 | 32 | listView = (ListView) findViewById(R.id.listview); 33 | TextView textView = (TextView) findViewById(R.id.secondtext); 34 | textView.setOnClickListener(v -> { 35 | // RobustModify.modify(); 36 | Log.d("robust", " onclick in Listener"); 37 | } 38 | ); 39 | //change text on the SecondActivity 40 | textView.setText(getTextInfo()); 41 | 42 | //test array 43 | BaseAdapter adapter = new ArrayAdapter<>(this, android.R.layout.simple_expandable_list_item_1, multiArr); 44 | listView.setAdapter(adapter); 45 | printLog("robust", new String[][]{new String[]{"1", "2", "3"}, new String[]{"4", "5", "6"}}); 46 | } 47 | 48 | // @Modify 49 | public String getTextInfo() { 50 | getArray(); 51 | return "error occur " ; 52 | // return "error fixed"; 53 | } 54 | 55 | @Add 56 | public String[] getArray() { 57 | return new String[]{"hello","world"}; 58 | } 59 | 60 | @Override 61 | public View onCreateView(String name, Context context, AttributeSet attrs) { 62 | 63 | return super.onCreateView(name, context, attrs); 64 | } 65 | 66 | @Override 67 | public void onClick(View v) { 68 | Toast.makeText(SecondActivity.this, "from implements onclick ", Toast.LENGTH_SHORT).show(); 69 | 70 | } 71 | 72 | public static Field getReflectField(String name, Object instance) throws NoSuchFieldException { 73 | for (Class clazz = instance.getClass(); clazz != null; clazz = clazz.getSuperclass()) { 74 | try { 75 | Field field = clazz.getDeclaredField(name); 76 | if (!field.isAccessible()) { 77 | field.setAccessible(true); 78 | } 79 | return field; 80 | } catch (NoSuchFieldException e) { 81 | // ignore and search next 82 | } 83 | } 84 | throw new NoSuchFieldException("Field " + name + " not found in " + instance.getClass()); 85 | } 86 | 87 | public static Object getFieldValue(String name, Object instance) { 88 | try { 89 | return getReflectField(name, instance).get(instance); 90 | } catch (Exception e) { 91 | Log.d("robust", "getField error " + name + " target " + instance); 92 | e.printStackTrace(); 93 | } 94 | return null; 95 | } 96 | 97 | private void printLog(@NonNull String tag, @NonNull String[][] args) { 98 | int i = 0; 99 | int j = 0; 100 | for (String[] array : args) { 101 | for (String arg : array) { 102 | Log.d(tag, "args[" + i + "][" + j + "] is: " + arg); 103 | j++; 104 | } 105 | i++; 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 17 | 18 |