Android 探索增量升级

一、介绍

Android 的增量升级,不同热修复和热更新,它只是通过和老的 apk 对比,识别出与新 apk 之间的二进制差异,从而生成的补丁包(差量包);
这样的好处在于,不用全部下载所有的文件,比如一个游戏 1个多G,如果每次更新,都下载1个多G,相信这个游戏基本没人下,但是使用差量包,则需要几十或者几百兆,这样对用户来说,相对能接受些。
通过这篇文章,你将看到:

  • 差量包的生成
  • cmake 实现 bsdiff 升级接口的过程
  • 生成 so 工其他工程使用

工程连接:https://github.com/LillteZheng/ZDiffUpdate

二、生成差量包

首先,我们需要借助 bsdiff工具生成差量包。win 用户下载这个:
在这里插入图片描述
接着解压,文件如下:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200610114125794.png在这里插入图片描述
接着,把老的 apk 和 新的 apk 放到相同目录下,并使用 cmd 执行以下命令,生成 patch

bsdiff_win_exe>bsdiff.exe  v1.0.apk v1.1.apk patch.patch

然后再通过 patch 与 老版本的apk对比,生成 差量包:

bspatch.exe v1.0.apk new.apk patch.patch

最后的结果如下:
在这里插入图片描述
这样,v1.1.apk 与 new.apk 是相同的,这样,我们只需要把 new.apk 和 patch 上传到服务器,Android下载更新即可

三、Android 增量更新(cmake)

bsdiff 工具为 .c 的源代码,需要配合 bszip。(有些链接已经失效,通过我的github工程加载)
导入源码:
在这里插入图片描述
然后打开 CMakeLists.txt 配置源码路径:

# 配置路径,CMAKE_SOURCE_DIR 为CMakeLists.txt 的位置
file(GLOB bzip_source ${CMAKE_SOURCE_DIR}/bzip/*.c)
add_library( # Sets the name of the library.
        native-lib

        # Sets the library as a shared library.
        SHARED
		#添加 bspatch 和 bzip 下的所有代码
        bspatch.c
        ${bzip_source}

        # Provides a relative path to your source file(s).
        native-lib.cpp)

此时编译,会出现 bspatch.c 报错的问题,我们需要改变头文件的引入路径,注释掉默认的 <bzlib.h>,修改为:
在这里插入图片描述
然后,再修改它的 main()函数名为 execute_update,方便后面我们的调用:
在这里插入图片描述

3.1 添加 Java 层调用方法

新建一个 UpdateJni.java 类,引用 jni 库,并添加方法:

public class UpdateJni {
    static {
        System.loadLibrary("native-lib");
    }

    /**
     * 升级方法
     *
     * @param oldPath    老的apk 路径
     * @param patch      对比生成的 patch 的路径
     * @param newApkPath 新 apk 的路径
     */
    public static native void diffUpdate(String oldPath, String patch, String newApkPath);
}

然后,编写 native-lib.cpp ,添加 diffUpdate() 方法,如下:


extern "C"{
//引入bspatch.c里的main方法
extern int execute_update(int argc,char * argv[]);
}

extern "C" JNIEXPORT void JNICALL
Java_com_zhengsr_zdiffupdate_UpdateJni_diffUpdate(JNIEnv *env, jclass instance, jstring oldapk_,
                                                 jstring patch_, jstring output_) {
    const char *oldapk = env->GetStringUTFChars(oldapk_, 0);
    const char *patch = env->GetStringUTFChars(patch_, 0);
    const char *output = env->GetStringUTFChars(output_, 0);


    int argc = 4;
    char *argv[4] ={"", const_cast<char *>(oldapk),const_cast<char *>(output),const_cast<char *>(patch)};
    execute_update(argc,argv);

    env->ReleaseStringUTFChars(oldapk_, oldapk);
    env->ReleaseStringUTFChars(patch_, patch);
    env->ReleaseStringUTFChars(output_, output);
}

这样,我们的 C 层就编写完了。

回到 activity,在点击事件中,更新差量包:

   public void test(View view) {
        /**
         * 使用请参考以下步骤
         * 1、请先安装 v1.0.apk 看看效果,然后不要点击它的button,adb 命令参考:adb install -r v1.0.apk
         * 2、接着把new.apk 和 patch.patch push 到 sdcard,adb 命令参考:adb  push patch.patch /sdcard/.
         * 3、运行软件,点击更新,即可看到 v1.0.apk 的背景被替换了
         */
        new UpdateTask().execute();
    }

    class UpdateTask extends AsyncTask<Void,Void,File> {

        @Override
        protected File doInBackground(Void... voids) {
            //自己的apk 可以用这个
           // String sourceDir = getApplicationInfo().sourceDir;
            String sourceDir = "/data/app/com.zhengsr.diffupdate-1/base.apk";

            String patch = Environment.getExternalStorageDirectory().getAbsolutePath()+"/patch.patch";

            String newApk = Environment.getExternalStorageDirectory().getAbsolutePath()+"/new.apk";

            File file1 = new File(patch);
            File file2 = new File(newApk);

           
            long time = System.currentTimeMillis();
 			//差分包建议在子线程中运行,防止阻塞主线程
            UpdateJni.diffUpdate(sourceDir,patch,newApk);


            return new File(newApk);
        }

        @Override
        protected void onPostExecute(File file) {
            super.onPostExecute(file);
            //2、安装
            Intent i = new Intent(Intent.ACTION_VIEW);
            if(Build.VERSION.SDK_INT<Build.VERSION_CODES.N){
                i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                i.setDataAndType(Uri.fromFile(file),
                        "application/vnd.android.package-archive");
            }else {
                i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                String packageName = getApplication().getPackageName();
                Uri contentUri = FileProvider.getUriForFile(MainActivity.this, packageName+ ".fileProvider", file);
                i.setDataAndType(contentUri,"application/vnd.android.package-archive");
            }
            startActivity(i);
        }
    }

由于解析差量包的过程是好事的,所以,我们放在 asynctask 中去更新。 效果如下:
在这里插入图片描述

四、生成 so 工其他工程使用

既然功能已经完成了,下个工程还得重新搞一遍有点得不偿失,所以,从 debug 中,拿到已经有完整功能的 so:
在这里插入图片描述
新建一个cmake或ndk工程,把它放到 libs 中,并把 libnative-lib.so 命名成自己喜欢的,比如 libdiffUpdate.so:
在这里插入图片描述
然后再 build.gradle 中配置so库的位置:
在这里插入图片描述
接着,新建一个和 so 相同的包名,并把 UpdateJni 复制过去:
在这里插入图片描述
然后在 activity 中调用即可:

    class UpdateTask extends AsyncTask<Void,Void, File> {

        @Override
        protected File doInBackground(Void... voids) {
            //自己的apk 可以用这个
            // String sourceDir = getApplicationInfo().sourceDir;
            String sourceDir = "/data/app/com.zhengsr.diffupdate-1/base.apk";

            String patch = Environment.getExternalStorageDirectory().getAbsolutePath()+"/patch.patch";

            String newApk = Environment.getExternalStorageDirectory().getAbsolutePath()+"/new.apk";

            File file1 = new File(patch);
            File file2 = new File(newApk);

            //差分包建议在子线程中运行,防止阻塞主线程,这里是测试,所以没关系
            long time = System.currentTimeMillis();

            UpdateJni.diffUpdate(sourceDir,patch,newApk);

            return new File(newApk);
        }

        @Override
        protected void onPostExecute(File file) {
            super.onPostExecute(file);
            //2、安装
            Intent i = new Intent(Intent.ACTION_VIEW);
            if(Build.VERSION.SDK_INT<Build.VERSION_CODES.N){
                i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                i.setDataAndType(Uri.fromFile(file),
                        "application/vnd.android.package-archive");
            }else {
                i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                String packageName = getApplication().getPackageName();
                Uri contentUri = FileProvider.getUriForFile(MainActivity.this, packageName+ ".fileProvider", file);
                i.setDataAndType(contentUri,"application/vnd.android.package-archive");
            }
            startActivity(i);
        }
    }

参考:Android增量更新

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
框架内部支持中/英文(其他语言只需要在对应的string.xml中取相同的名字即可)内部对话框背景图片、按钮支持自定义了查看版本中的Log只需要过滤AppUpdate开头的Tag重点:&nbsp;如果没有设置downloadPath则默认为getExternalCacheDir()目录,同时不会申请[存储]权限!目录编译问题效果图功能介绍DownloadManagerUpdateConfiguration使用步骤Demo下载体验版本更新记录结语编译问题因为适配了Android O的通知栏,所以依赖的v7版本比较高appcompat-v7:26.1.0使用的gradle版本为gradle-4.1-all,所以建议使用Android Studio 3.0及以上的版本打开此项目效果图 &nbsp; &nbsp; 功能介绍&nbsp;支持断点下载&nbsp;支持后台下载&nbsp;支持自定义下载过程&nbsp;支持 设备 >= Android M 动态权限的申请&nbsp;支持通知栏进度条展示(或者自定义显示进度)&nbsp;支持Android N&nbsp;支持Android O&nbsp;支持中/英文双语&nbsp;支持自定内置对话框的样式&nbsp;使用HttpURLConnection下载,未集成其他第三方框架更加详细的文档参阅此处《AppUpdate API文档》DownloadManager:配置文档初始化使用DownloadManager.getInstance(this)属性描述默认值是否必须设置context上下文nulltrueapkUrlapk的下载地址nulltrueapkNameapk下载好的名字nulltruedownloadPathapk下载的位置getExternalCacheDir()falseshowNewerToast是否提示用户 "当前已是最新版本"falsefalsesmallIcon通知栏的图标(资源id)-1trueconfiguration这个库的额外配置nullfalseapkVersionCode更新apk的versionCode&nbsp;(如果设置了那么库中将会进行版本判断下面的属性也就需要设置了)1falseapkVersionName更新apk的versionNamenullfalseapkDescription更新描述nullfalseapkSize新版本的安装大小(单位M)nullfalseauthorities兼容Android N uri授权应用名falseUpdateConfiguration:配置文档属性描述默认值notifyId通知栏消息id1011notificationChannel适配Android O的渠道通知详情查阅源码httpManager设置自己的下载过程nullbreakpointDownload是否需要支持断点下载trueenableLog是否需要日志输出trueonDownloadListener下载过程的回调nulljumpInstallPage下载完成是否自动弹出安装页面trueshowNotification是否显示通知栏进度(后台下载提示)trueforcedUpgrade是否强制升级falseonButtonClickListener按钮点击事件回调nulldialogImage对话框背景图片资源(图片规范参考demo)-1dialogButtonColor对话框按钮的颜色-1dialogButtonTextColor对话框按钮的文字颜色-1所有版本:点击查看使用步骤第一步:&nbsp;app/build.gradle进行依赖implementation&nbsp;'com.azhon:appupdate:1.7.3'第二步:创建DownloadManager,更多用法请查看这里示例代码DownloadManager&nbsp;manager&nbsp;=&nbsp;DownloadManager.getInstance(this); manager.setApkName("appupdate.apk") &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.setApkUrl("https://raw.githubusercontent.com/azhon/AppUpdate/master/apk/appupdate.apk") &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.setSmallIcon(R.mipmap.ic_launcher) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//可设置,可不设置 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.setConfiguration(configuration) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.download();第三步:兼容Android N 及以上版本,在你应用的Manifest.xml添加如下代码<--!&nbsp;android:authorities="${applicationId}"&nbsp; 这个值必须与DownloadManager中的authorities一致(不设置则为应用名)--> <provider &nbsp;&nbsp;&nbsp;&nbsp;android:name="android.support.v4.content.FileProvider" &nbsp;&nbsp;&nbsp;&nbsp;android:authorities="${applicationId}" &nbsp;&nbsp;&nbsp;&nbsp;android:exported="false" &nbsp;&nbsp;&nbsp;&nbsp;android:grantUriPermissions="true"> &nbsp;&nbsp;&nbsp;&nbsp;<meta-data &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;android:name="android.support.FILE_PROVIDER_PATHS" &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;android:resource="@xml/file_paths_public"&nbsp;/> </provider>第四步:资源文件res/xml/file_paths_public.xml内容<?xml&nbsp;version="1.0"&nbsp;encoding="utf-8"?> <paths> &nbsp;&nbsp;&nbsp;&nbsp;<external-path &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name="app_update_external" &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;path="/"&nbsp;/> &nbsp;&nbsp;&nbsp;&nbsp;<external-cache-path &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name="app_update_cache" &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;path="/"&nbsp;/> </paths>兼容Android O及以上版本,需要设置NotificationChannel(通知渠道);库中已经写好可以前往查阅NotificationUtil.java温馨提示:升级对话框中的内容是可以上下滑动的哦!如果需要实现自己一套下载过程,只需要继承BaseHttpDownloadManager&nbsp;并使用listener更新进度public&nbsp;class&nbsp;MyDownload&nbsp;extends&nbsp;BaseHttpDownloadManager&nbsp;{}

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值