├── .gitignore ├── .idea ├── .name ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── andfixtest │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── images │ │ ├── apatch.png │ │ ├── tihuan.png │ │ ├── wenjian.png │ │ └── yuanli.png │ ├── java │ │ └── com │ │ │ └── andfixtest │ │ │ ├── MainActivity.java │ │ │ ├── MainApplication.java │ │ │ └── PatchDownloadIntentService.java │ ├── res │ │ ├── layout │ │ │ └── activity_main.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-w820dp │ │ │ └── dimens.xml │ │ └── values │ │ │ ├── colors.xml │ │ │ ├── dimens.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ └── tools │ │ └── apkpatch-1.0.3.zip │ └── test │ └── java │ └── com │ └── andfixtest │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | AndFixTest -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | 49 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AndFixTest 2 | AndFix 热修复 3 | 在我们发布的应用中都可能存在bug,而修复bug我们需要重新发包,但是重新发包会影响用户的体验,并且浪费用户的流量,重新发包用户需要下载整个包进行安装,这时我们可以考虑不需要重新发包,只需要向用户推送一个补丁进行修复就可以了。 4 | 5 | 在这里我将向大家推荐AndFix 热修复,AndFix是阿里开源的一个热修复框架,AndFix它的原理简单而纯粹。我们可以通过apkpatch工具生成一个差量文件(.apatch)文件。 6 | 7 | AndFix原理 8 | 9 | 10 | 方法替换过程 11 | 12 | 13 | # .apatch文件生成 14 | 在cmd下输入 apkpatch.bat -f new.apk -t old.apk -o xxx -k xxx -p xxx -a ckb -e xxx 15 | 16 | -f :新版本 17 | 18 | -t : 旧版本 19 | 20 | -o : 输出目录 21 | 22 | -k : 打包所用的keystore的路径 23 | 24 | -p : keystore的密码 25 | 26 | -a : keystore 用户别名 27 | 28 | -e : keystore 用户别名密码 29 | 30 | 31 | 32 | 执行完之后会生成一个.apatch 文件 33 | 34 | 35 | 36 | 在本实例中可以将文件重命名out.apatch,拷贝至sd卡根目录下进行测试 37 | 38 | 在android studio 中build.gradle 需要添加 compile 'com.alipay.euler:andfix:0.3.1@aar' 39 | 40 | 混淆需要添加下列混淆语句 41 | 42 | -keep class * extends java.lang.annotation.Annotation 43 | 44 | -keepclasseswithmembernames class * { native ; } 45 | 46 | -keep class com.alipay.euler.andfix.** { *; } 47 | 48 | 注意:AndFix只支持修复代码部分,资源文件不支持,并且下载的补丁不能删除,在版本更新时,请删除sd卡补丁 49 | 50 | 框架源码地址:https://github.com/alibaba/AndFix 51 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.3" 6 | 7 | defaultConfig { 8 | applicationId "com.andfixtest" 9 | minSdkVersion 9 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | testCompile 'junit:junit:4.12' 25 | compile 'com.android.support:appcompat-v7:23.2.1' 26 | compile 'com.alipay.euler:andfix:0.3.1@aar' 27 | } 28 | -------------------------------------------------------------------------------- /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 E:\soft\adt-bundle-windows-x86_64-20140321\adt-bundle-windows-x86_64-20140321\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/andfixtest/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.andfixtest; 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 | 8 | 9 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/images/apatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wukaide/AndFixTest/2c554f9304beb9872e70443acfd6e9e91139c557/app/src/main/images/apatch.png -------------------------------------------------------------------------------- /app/src/main/images/tihuan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wukaide/AndFixTest/2c554f9304beb9872e70443acfd6e9e91139c557/app/src/main/images/tihuan.png -------------------------------------------------------------------------------- /app/src/main/images/wenjian.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wukaide/AndFixTest/2c554f9304beb9872e70443acfd6e9e91139c557/app/src/main/images/wenjian.png -------------------------------------------------------------------------------- /app/src/main/images/yuanli.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wukaide/AndFixTest/2c554f9304beb9872e70443acfd6e9e91139c557/app/src/main/images/yuanli.png -------------------------------------------------------------------------------- /app/src/main/java/com/andfixtest/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.andfixtest; 2 | 3 | import android.content.Intent; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.os.Bundle; 6 | import android.view.View; 7 | import android.widget.Button; 8 | import android.widget.TextView; 9 | 10 | public class MainActivity extends AppCompatActivity { 11 | private Intent patchDownloadIntent; 12 | private TextView textView; 13 | private Button testBtn; 14 | 15 | @Override 16 | protected void onCreate(Bundle savedInstanceState) { 17 | super.onCreate(savedInstanceState); 18 | setContentView(R.layout.activity_main); 19 | textView = (TextView)findViewById(R.id.test_tv); 20 | textView.setText("Repair Success!\n 修复成功!!!!"); 21 | testBtn = (Button)findViewById(R.id.test_btn); 22 | testBtn.setOnClickListener(new View.OnClickListener() { 23 | @Override 24 | public void onClick(View v) { 25 | patchDownloadIntent = new Intent("com.andfixtest.PatchDownloadIntentService"); 26 | patchDownloadIntent.putExtra("url", "http://172.16.24.159:8080/Json/Test/out.apatch"); 27 | startService(patchDownloadIntent); 28 | } 29 | }); 30 | 31 | } 32 | 33 | @Override 34 | protected void onDestroy() { 35 | super.onDestroy(); 36 | if(patchDownloadIntent != null){ 37 | stopService(patchDownloadIntent); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/andfixtest/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.andfixtest; 2 | 3 | import android.app.Application; 4 | import android.os.Environment; 5 | import android.util.Log; 6 | 7 | import com.alipay.euler.andfix.patch.PatchManager; 8 | 9 | import java.io.IOException; 10 | 11 | /** 12 | * Created by wukd on 2016/6/12. 13 | */ 14 | public class MainApplication extends Application{ 15 | private static final String TAG = "euler"; 16 | private static final String APATCH_PATH = "/out.apatch"; 17 | private PatchManager mPatchManager; 18 | 19 | @Override 20 | public void onCreate() { 21 | super.onCreate(); 22 | mPatchManager = new PatchManager(this); 23 | mPatchManager.init("1.0"); 24 | Log.d(TAG, "inited."); 25 | 26 | mPatchManager.loadPatch(); 27 | Log.d(TAG, "apatch loaded."); 28 | try{ 29 | String patchFileString = Environment.getExternalStorageDirectory().getAbsolutePath() + APATCH_PATH; 30 | mPatchManager.addPatch(patchFileString); 31 | //mPatchManager.removeAllPatch(); 32 | Log.d(TAG, "apatch:" + patchFileString + "added."); 33 | }catch (IOException e){ 34 | Log.e(TAG,"",e); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/java/com/andfixtest/PatchDownloadIntentService.java: -------------------------------------------------------------------------------- 1 | package com.andfixtest; 2 | 3 | import android.app.IntentService; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.os.Environment; 7 | import android.text.TextUtils; 8 | import android.util.Log; 9 | 10 | import java.io.File; 11 | import java.io.FileNotFoundException; 12 | import java.io.FileOutputStream; 13 | import java.io.IOException; 14 | import java.io.InputStream; 15 | import java.net.HttpURLConnection; 16 | import java.net.URL; 17 | 18 | /** 19 | * Created by wukd on 2016/6/12. 20 | */ 21 | public class PatchDownloadIntentService extends IntentService{ 22 | private int fileLength,downloadLength; 23 | 24 | 25 | public PatchDownloadIntentService(){ 26 | super("PatchDownloadIntentService"); 27 | } 28 | /** 29 | * This method is invoked on the worker thread with a request to process. 30 | * Only one Intent is processed at a time, but the processing happens on a 31 | * worker thread that runs independently from other application logic. 32 | * So, if this code takes a long time, it will hold up other requests to 33 | * the same IntentService, but it will not hold up anything else. 34 | * When all requests have been handled, the IntentService stops itself, 35 | * so you should not call {@link #stopSelf}. 36 | * 37 | * @param intent The value passed to {@link 38 | * Context#startService(Intent)}. 39 | */ 40 | @Override 41 | protected void onHandleIntent(Intent intent) { 42 | 43 | if (intent != null){ 44 | String downloadUrl = intent.getStringExtra("url"); 45 | if (downloadUrl !=null && !TextUtils.isEmpty(downloadUrl)) { 46 | downloadPatch(downloadUrl); 47 | } 48 | 49 | } 50 | } 51 | 52 | private void downloadPatch(String downloadUrl){ 53 | File dir = new File(Environment.getExternalStorageDirectory().getAbsolutePath()); 54 | if (!dir.exists()){ 55 | dir.mkdir(); 56 | } 57 | File patchFile = new File(dir,"out.apatch"); 58 | downloadFile(downloadUrl, patchFile); 59 | } 60 | private void downloadFile(String downloadUrl, File file){ 61 | FileOutputStream fos = null; 62 | try { 63 | fos = new FileOutputStream(file); 64 | } catch (FileNotFoundException e) { 65 | e.printStackTrace(); 66 | } 67 | InputStream ips = null; 68 | try { 69 | URL url = new URL(downloadUrl); 70 | HttpURLConnection huc = (HttpURLConnection) url.openConnection(); 71 | huc.setRequestMethod("GET"); 72 | huc.setReadTimeout(10000); 73 | huc.setConnectTimeout(3000); 74 | fileLength = Integer.valueOf(huc.getHeaderField("Content-Length")); 75 | ips = huc.getInputStream(); 76 | int hand = huc.getResponseCode(); 77 | if (hand == 200) { 78 | byte[] buffer = new byte[8192]; 79 | int len = 0; 80 | while ((len = ips.read(buffer)) != -1) { 81 | if (fos != null) { 82 | fos.write(buffer, 0, len); 83 | } 84 | downloadLength = downloadLength + len; 85 | } 86 | } else { 87 | //Log.e("response code: " + hand); 88 | } 89 | 90 | } catch (IOException e) { 91 | e.printStackTrace(); 92 | } catch (Exception e){ 93 | e.printStackTrace(); 94 | } 95 | finally { 96 | try { 97 | if (fos != null) { 98 | fos.close(); 99 | } 100 | if (ips != null) { 101 | ips.close(); 102 | } 103 | } catch (IOException e) { 104 | e.printStackTrace(); 105 | } 106 | } 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 16 |