02.增量更新之apk和差分包的合并

概述

在应用中做增量更新,一般都会这样做,提示用户升级(也就是下载差分包),当用户下载了差分包后,将当前版本和差分包合并成最新版本,然后提醒用户安装新版本的apk。

上一篇中通过bsdiff的win版已经可以将两个版本的apk差分出差分包,然后将差分包放到服务器中,提示用户下载即可。

下载好差分包后,需要将旧版本的apk和差分包合并起来,这就用到了bsdiff,而bsdiff依赖于bzip2。直接到网上下载bsdiff和bzip2就行。

步骤

1.将bsdiff和bzip下的文件拷贝到项目中

新建android项目,然后下载 bsdiff解压后,将其中的bspatch.c复制到项目中的jni目录下,由于bsdiff是依赖bzip2的,所以需要下载bzip2,解压并将其包下的所有的c文件和.h文件复制到jni的bzip2目录下.
这里写图片描述

2.修改bspatch.c

修改bspatch.c的头文件引用,将bzip2下的所有c文件都引用过来。这里引用的是是.c文件而不是.h文件,如果要引用.h文件也是可以的,但需要在Android.mk下将所有的c文件配置,否则编译通不过。

//将bzip2中的所有c都引入到文件中
#include "bzip2/bzlib.c"
#include "bzip2/crctable.c"
#include "bzip2/compress.c"
#include "bzip2/decompress.c"
#include "bzip2/randtable.c"
#include "bzip2/blocksort.c"
#include "bzip2/huffman.c"


#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <err.h>
#include <unistd.h>
#include <fcntl.h>

仔细阅读 bspatch.c 的源码

int bspatch_main(int argc,char * argv[])
{
    ...
    if(argc!=4) errx(1,"usage: %s oldfile newfile patchfile\n",argv[0]);
    ...
}

在bspatch.c中可以做合并的方法,就是int main方法,可以看到其中的参数,和差分时的差不多,这就可以看出,如果你上回的差分做了,这次合并做法是同样的,将main修改为bspatch_main。

3.编写native方法

根据bspatch中的方法,java中可以提供这样的native方法

public class PatchUtil {

    /**
     * 合并
     * @param oldApkPath 老apk路径
     * @param newApkPath 合并成新的apk路径
     * @param diffPath 差分包
     */
    public native static void diff(String oldApkPath,String newApkPath,String diffPath);

    //加载.so库
    static {
        System.loadLibrary("patch");
    }
}

使用javah生成相应的.h文件,并将其移动到jni目录下

在bspatch.c中引入生成的.h文件,并实现其函数

#include "com_example_ndk_03_update_PatchUtil.h"

 //diff函数的实现
 JNIEXPORT void JNICALL Java_com_example_ndk_103_1update_PatchUtil_diff
  (JNIEnv *env, jclass cls, jstring old_apk_path_str, jstring new_apk_path_str, jstring patch_path_str){
    //将jstring转成char*
    char* old_apk_path=(char*)(*env)->GetStringUTFChars(env,old_apk_path_str,NULL);
    char* new_apk_path=(char*)(*env)->GetStringUTFChars(env,new_apk_path_str,NULL);
    char* patch_path=(char*)(*env)->GetStringUTFChars(env,patch_path_str,NULL);

    //bspatch_main的参数
    int argc=4;
    char * argv[4];
    argv[0]="bspatch";
    argv[1]=old_apk_path;
    argv[2]=new_apk_path;
    argv[3]=patch_path;

    //调用bspatch的函数进行合并
    bspatch_main(argc,argv);

    //释放
    (*env)->ReleaseStringUTFChars(env,old_apk_path_str,old_apk_path);
    (*env)->ReleaseStringUTFChars(env,new_apk_path_str,new_apk_path);
    (*env)->ReleaseStringUTFChars(env,patch_path_str,patch_path);
}

4.修改Android.mk文件

如果要在bspatch.c中实现.h文件,并生成bspatch库,需要修改Androi.mk文件中的c文件为bspatch.c

扫描二维码关注公众号,回复: 2315635 查看本文章
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := patch
LOCAL_SRC_FILES := bspatch.c

include $(BUILD_SHARED_LIBRARY)

上面的LOCAL_SRC_FILES 的值只写了bspatch.c,这是由于在bspatch.c中直接引用了bzip2中的c文件,如果引用的是.h文件则在这后面追加相应的.c文件。

5.在MainActiviyt中实现差分包的下载和合并

public class MainActivity extends Activity {

    private Handler mHandle = new Handler() {
        public void handleMessage(android.os.Message msg) {
            // 3.提示安装新apk
            Toast.makeText(MainActivity.this, "您正在进行无流量更新", Toast.LENGTH_SHORT).show();
            ApkUtil.installAPK(MainActivity.this,new File(Constant.new_apk_path));
        };
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    /**
     * 更新版本
     */
    public void update(View view) {
        Toast.makeText(MainActivity.this, "开始更新", Toast.LENGTH_SHORT).show();
        // 1.下载差分包
        downDiffPatch(Constant.diff_patch_url);
    }

    private void downDiffPatch(String url) {
        Request request = new Request.Builder().get().url(url).build();
        OkHttpClient client = new OkHttpClient();
        Call call = client.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onResponse(Call arg0, Response response) throws IOException {
                InputStream inputStream = response.body().byteStream();
                FileOutputStream fileOutputStream = null;
                try {
                    fileOutputStream = new FileOutputStream(new File(Constant.dif_patch_path));
                    byte[] buffer = new byte[2048];
                    int len = 0;
                    while ((len = inputStream.read(buffer)) != -1) {
                        fileOutputStream.write(buffer, 0, len);
                    }
                    fileOutputStream.flush();
                } catch (IOException e) {
                    Log.i("gxl", "IOException");
                    e.printStackTrace();
                }
                System.out.println("下载完成");
                // 2.合并差分包
                File patchFile = new File(Constant.dif_patch_path);
                // apk文件
                String oldApkPath = ApkUtil.getSourceApkPath(MainActivity.this, getPackageName());
                if (patchFile.exists()) {
                    PatchUtil.patch(oldApkPath, Constant.new_apk_path, Constant.dif_patch_path);
                    System.out.println("合并完成");
                } else {
                    System.out.println("文件不存在");
                }
                mHandle.sendEmptyMessage(0);
            }

            @Override
            public void onFailure(Call arg0, IOException arg1) {
                Log.i("gxl", "下载失败");
            }
        });
    }
}

ApkUtil

public class ApkUtil {
    /**
     * 获取已安装Apk文件的源Apk文件
     * 如:/data/app/my.apk
     * @param context
     * @param packageName
     * @return
     */
    public static String getSourceApkPath(Context context, String packageName) {
        if (TextUtils.isEmpty(packageName))
            return null;

        try {
            ApplicationInfo appInfo = context.getPackageManager()
                    .getApplicationInfo(packageName, 0);
            return appInfo.sourceDir;
        } catch (NameNotFoundException e) {
            e.printStackTrace();
        }

        return null;
    }
    /**
     * 安装apk
     * @param context 上下文
     * @param file apk文件
     */
     public static void installAPK(Context context, File file) {
            Intent intent = new Intent();
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            intent.setAction(Intent.ACTION_VIEW);
            intent.setDataAndType(Uri.fromFile(file),
                    "application/vnd.android.package-archive");
            context.startActivity(intent);
        }

}

Constant

public class Constant {
    //查分包的下载路径
    public static final String diff_patch_url="http://192.168.200.101:8080/apk.patch";
    //sd卡路径
    public static final String SD_PATH=Environment.getExternalStorageDirectory().getAbsolutePath();
    //差分 包路径
    public static final String dif_patch_path=SD_PATH+File.separator+"apk.patch";
    //合成后的apk路径
    public static final String new_apk_path=SD_PATH+File.separator+"new.apk";
}

PatchUtil

public class PatchUtil {

    /**
     * 合并
     * @param oldApkPath 老apk路径
     * @param newApkPath 合并成新的apk路径
     * @param diffPath 差分包
     */
    public native static void patch(String oldApkPath,String newApkPath,String diffPath);

    //加载.so库
    static {
        System.loadLibrary("patch");
    }
}

猜你喜欢

转载自blog.csdn.net/A_thousand_miles/article/details/81141783
今日推荐