APP自动增量更新 github:https://github.com/itlwy/AppSmartUpdate 
抽取的Android自动更新库,目的是几行代码引入更新功能,含服务端代码,欢迎Star,欢迎Fork,谢谢~
目录 
功能介绍 
流程图 
效果图与示例apk 
                
                示例1
              
                
                示例2
             
点击下载 smart-update.apk  
如何引入 Gradle引入 step 1 Add the JitPack repository to your build file
1 2 3 4 5 6 allprojects { 		repositories { 			... 			maven { url 'https://jitpack.io' } 		} 	} 
Step 2 Add the dependency
1 2 3 dependencies { 	          implementation 'com.github.itlwy:AppSmartUpdate:v1.0.6' 	} 
更新清单文件 该清单放置在静态服务器以供App访问,主要用于判断最新的版本,及要更新的版本资源信息等(示例见仓库根目录下的resources目录或直接访问后台代码 github ),清单由服务端程序发布apk时生成,详见后台示例:github 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 {   "minVersion" : 100 ,    "minAllowPatchVersion" : 100 ,    "newVersion" : 101 ,    "tip" : "test update" ,	   "size" : 1956631 ,	   "apkURL" : "https://raw.githubusercontent.com/itlwy/AppSmartUpdate/master/resources/app/smart-update.apk" ,    "hash" : "ea97c8efa490a2eaf7d10b37e63dab0e" ,    "patchInfo" : {       "v100" : {        "patchURL" : "v100/100to101.patch" ,        "tip" : "101 version" ,        "hash" : "ea97c8efa490a2eaf7d10b37e63dab0e" ,        "size" : 1114810       }   } } 
简单使用 1.初始化 1 2 3 4 5 6 7 8 9 10 11 12 public  class  MyApplication  extends  Application      @Override      public  void  onCreate ()           super .onCreate();                  Config config = new  Config.Builder()                 .isDebug(true )                 .build(this );         UpdateManager.getInstance().init(config);     } } 
2.调用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public  class  MainActivity  extends  AppCompatActivity  implements  View .OnClickListener   private  Button mUpdateBtn;     private  String manifestJsonUrl = "https://raw.githubusercontent.com/itlwy/AppSmartUpdate/master/resources/UpdateManifest.json" ;     private  IUpdateCallback mCallback;     @Override      protected  void  onCreate (Bundle savedInstanceState)           super .onCreate(savedInstanceState);         setContentView(R.layout.activity_main);         mUpdateBtn = (Button) findViewById(R.id.update_btn);         mUpdateBtn.setOnClickListener(this );     }     @Override      public  void  onClick (View v)           switch  (v.getId()) {             case  R.id.update_btn:                 UpdateManager.getInstance().update(this , manifestJsonUrl, null );                 break ;         }     } } 
详细说明 注册通知回调 
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 public  void  register (IUpdateCallback callback)  public  void  unRegister (IUpdateCallback callback)  public  interface  IUpdateCallback           void  noNewApp ()           void  beforeUpdate ()           void  onProgress (int  percent, long  totalLength, int  patchIndex, int  patchCount)           void  onCompleted ()           void  onError (String error)           void  onCancelUpdate ()           void  onBackgroundTrigger ()  } 
网络框架注入 默认使用okhttp,也可由外部注入,只需实现如下的IHttpManager接口,然后通过new Config.Builder().httpManager(new OkhttpManager())注入即可
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 public  interface  IHttpManager      IResponse syncGet (@NonNull String url, @NonNull Map<String, String> params)  throws  IOException ;          void  asyncGet (@NonNull String url, @NonNull Map<String, String> params, @NonNull Callback callBack)           void  asyncPost (@NonNull String url, @NonNull Map<String, String> params, @NonNull Callback callBack)           void  download (@NonNull String url, @NonNull String path, @NonNull String fileName, @NonNull FileCallback callback)  } 
定制更新交互界面 每个应用的风格都可能是不一样的,因此这里也支持自定义弹出的提示框和进度框,详细见如下代码示例:
初始化config时需要将内部默认的弹框屏蔽掉
1 2 3 4 5 6 7 8 9 10 11  public  class  MyApplication  extends  Application       @Override      public  void  onCreate ()           super .onCreate();          Config config = new  Config.Builder()                 .isShowInternalDialog(false )                 .build(this );         UpdateManager.getInstance().init(config);     } } 
自定义对话框,如下(详细代码在MainActivity.java里):
 
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 54 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 public  void  registerUpdateCallbak ()         mCallback = new  IUpdateCallback() {            @Override             public  void  noNewApp ()                  Toast.makeText(MainActivity.this , "当前已是最新版本!" , Toast.LENGTH_LONG).show();            }            @Override             public  void  hasNewApp (AppUpdateModel appUpdateModel, UpdateManager updateManager, final  int  updateMethod)                  AlertDialog.Builder builder = new  AlertDialog.Builder(MainActivity.this );                mDialog = builder.setTitle("自动更新提示" )                        .setMessage(appUpdateModel.getTip())                        .setPositiveButton("更新" , new  DialogInterface.OnClickListener() {                            @Override                             public  void  onClick (DialogInterface dialog, int  which)                                  UpdateManager.getInstance().startUpdate(updateMethod);                            }                        })                        .setNegativeButton("取消" , new  DialogInterface.OnClickListener() {                            @Override                             public  void  onClick (DialogInterface dialog, int  which)                              }                        }).create();                mDialog.show();            }            @Override             public  void  beforeUpdate ()                                  mProgressDialog = new  ProgressDialog(MainActivity.this );                mProgressDialog.setTitle("更新中..." );                mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);                mProgressDialog.setMessage("正在玩命更新中..." );                mProgressDialog.setMax(100 );                mProgressDialog.setProgress(0 );                mProgressDialog.setOnCancelListener(new  DialogInterface.OnCancelListener() {                    @Override                     public  void  onCancel (DialogInterface dialog)                                                  if  (UpdateManager.getInstance().isRunning()) {                            UpdateManager.getInstance().onBackgroundTrigger();                        }                    }                });                mProgressDialog.show();            }            @Override             public  void  onProgress (int  percent, long  totalLength, int  patchIndex, int  patchCount)                  String tip;                if  (patchCount > 0 ) {                    tip = String.format("正在下载补丁%d/%d" , patchIndex, patchCount);                } else  {                    tip = "正在下载更新中..." ;                }                mProgressDialog.setProgress(percent);                mProgressDialog.setMessage(tip);            }            @Override             public  void  onCompleted ()                  mProgressDialog.dismiss();            }            @Override             public  void  onError (String error)                  Toast.makeText(MainActivity.this , error, Toast.LENGTH_LONG).show();                mProgressDialog.dismiss();            }            @Override             public  void  onCancelUpdate ()              }            @Override             public  void  onBackgroundTrigger ()                  Toast.makeText(MainActivity.this , "转为后台更新,进度由通知栏提示!" , Toast.LENGTH_LONG).show();            }        };        UpdateManager.getInstance().register(mCallback);    } 
差分包合成(jni)     此部分采用的差分工具为开源bsdiff ,用于生成.patch补丁文件,采用jni方式封装一个.so库供java调用,详见”smartupdate”库里的main/cpp目录源码,过程比较简单,就是写个jni的方法来直接调用bsdiff库,目录结构如下:
main
-cpp
    -bzip2
    -CMakeLists.txt
    -patchUtils.c
    -patchUtils.h
    -update-lib.cpp
    因为bsdiff还依赖了bzip2,所以这里涉及多个源文件编译链接问题,需要在CMakeLists.txt稍作修改:
1 2 3 4 5 6 7 8 9 10 11 12 13 # 将当前 "./src/main/cpp" 目录下的所有源文件保存到 "NATIVE_SRC" 中,然后在 add_library 方法调用。 aux_source_directory( . NATIVE_SRC ) # 将 "./src/main/cpp/bzip2" 目录下的子目录bzip2保存到 "BZIP2_BASE" 中,然后在 add_library 方法调用。 aux_source_directory( ./bzip2 BZIP2_BASE ) # 将 BZIP2_BASE 增加到 NATIVE_SRC 中,这样目录的源文件也加入了编译列表中,当然也可以不加到 NATIVE_SRC,直接调用add_library。 list(APPEND NATIVE_SRC ${BZIP2_BASE}) add_library( # Sets the name of the library.         update-lib         # Sets the library as a shared library.         SHARED         # Provides a relative path to your source file(s).         ${NATIVE_SRC}) 
差分包生成     服务端见github  ,使用时将manifestJsonUrl改成部署的服务器地址即可,如下示例代码片段的注释处
1 2 3 4 5 public  class  MainActivity  extends  AppCompatActivity      private  String manifestJsonUrl = "https://raw.githubusercontent.com/itlwy/AppSmartUpdate/master/resources/UpdateManifest.json" ;     ... } 
依赖 
okhttp : com.squareup.okhttp3:okhttp:3.11.0 
gson : com.google.code.gson:gson:2.8.0 
numberprogressbar : com.daimajia.numberprogressbar:library:1.4@aar 
 
License    Copyright 2018 lwy
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
   http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.