NDK开发(三):增量更新

效果:

版本1:

NDK开发(三):增量更新_第1张图片

增量更新后的版本

NDK开发(三):增量更新_第2张图片

用到的C库

bsdiff::https://github.com/mendsley/bsdiff

bzip2: https://sourceforge.net/projects/bzip2/

差分包的生成(windows)

  • 将需要的开源文件导入vs

NDK开发(三):增量更新_第3张图片

其中jni.h和jni_md.h头文件从系统jdk中获得,这里会出现各种报错请查看【The POSIX name for this item is deprecated. Instead,】【使用了可能未初始化的本地指针变量“xxx”】


创建java项目

  • 创建jni调用java类
package bsdiff;

public class BsDiff {
    /**
     * 差分
     * @param oldfile
     * @param newfile
     * @param patchfile
     */
    public native static void diff(String oldfile, String newfile, String patchfile);
    static {
        System.loadLibrary("bsdiff");
    }
}

  • 生成.h头文件放进vs项目中
    NDK开发(三):增量更新_第4张图片
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class bsdiff_BsDiff */

#ifndef _Included_bsdiff_BsDiff
#define _Included_bsdiff_BsDiff
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     bsdiff_BsDiff
 * Method:    diff
 * Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_bsdiff_BsDiff_diff
  (JNIEnv *, jclass, jstring, jstring, jstring);

#ifdef __cplusplus
}
#endif
#endif

  • 在bsdiff.cpp文件中调用实现jni方法

    将main方法更改为bsdiff_main方法直接使用
    NDK开发(三):增量更新_第5张图片

//JNI调用
JNIEXPORT void JNICALL Java_bsdiff_BsDiff_diff
(JNIEnv *env, jclass jcls, jstring oldfile_jstr, jstring newfile_jstr, jstring patchfile_jstr) {
	int argc = 4;

	char* oldfile = (char*)env->GetStringUTFChars(oldfile_jstr,NULL);
	char* newfile = (char*)env->GetStringUTFChars(newfile_jstr, NULL);
	char* patchfile = (char*)env->GetStringUTFChars(patchfile_jstr, NULL);

	//参数(第一个参数无效)
	char *argv[4];
	argv[0] = (char*)"bsdiff";
	argv[1] = oldfile;
	argv[2] = newfile;
	argv[3] = patchfile;

	bsdiff_main(argc, argv);
	env->ReleaseStringUTFChars(oldfile_jstr, oldfile);
	env->ReleaseStringUTFChars(newfile_jstr, newfile);
	env->ReleaseStringUTFChars(patchfile_jstr, patchfile);
}
  • 生成解决方案(.dll)放到java项目跟目录下方便使用

NDK开发(三):增量更新_第6张图片

  • 调用jni生成差分包
public class ConstantsWin {
	
	//路径不能包含中文
	public static final String OLD_APK_PATH = "C:/Users/Administrator/Desktop/bsdiff/old.apk";
	
	public static final String NEW_APK_PATH = "C:/Users/Administrator/Desktop/bsdiff/new.apk";
	
	public static final String PATCH_PATH = "C:/Users/Administrator/Desktop/bsdiff/apk.patch";
}

public class Main {

    public static void main(String[] args) {
        //得到差分包
        BsDiff.diff(ConstantsWin.OLD_APK_PATH,ConstantsWin.NEW_APK_PATH,ConstantsWin.PATCH_PATH);

    }
}

NDK开发(三):增量更新_第7张图片
NDK开发(三):增量更新_第8张图片

这里就将两个版本的差分包生成出来了!

差分包的合并

  • 创建android项目
  • 将需要的c文件考入项目中

NDK开发(三):增量更新_第9张图片

  • 创建native方法
package com.qufu.mergeupdates.utils.jni;

public class BsPatch {
    /**
     * 合并
     * @param oldfile
     * @param newfile
     * @param patchfile
     */
    public native static void patch(String oldfile,String newfile,String patchfile);

    static {
        System.loadLibrary("bspatch");
    }
}

  • 生成头文件
    NDK开发(三):增量更新_第10张图片
/* DO NOT EDIT THIS FILE - it is machine generated */
#include 
/* Header for class com_qufu_mergeupdates_utils_jni_BsPatch */

#ifndef _Included_com_qufu_mergeupdates_utils_jni_BsPatch
#define _Included_com_qufu_mergeupdates_utils_jni_BsPatch
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_qufu_mergeupdates_utils_jni_BsPatch
 * Method:    patch
 * Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_com_qufu_mergeupdates_utils_jni_BsPatch_patch
  (JNIEnv *, jclass, jstring, jstring, jstring);

#ifdef __cplusplus
}
#endif
#endif

  • 在bapatch.c文件中实现jni方法,同样将main方法改为bspatch_main方法,方便调用
/**
 * 合并
 */
JNIEXPORT void JNICALL Java_com_qufu_mergeupdates_utils_jni_BsPatch_patch
        (JNIEnv *env, jclass jcls, jstring oldfile_jstr, jstring newfile_jstr,
         jstring patchfile_jstr) {
    int argc = 4;
    char *oldfile = (*env)->GetStringUTFChars(env, oldfile_jstr, NULL);
    char *newfile = (*env)->GetStringUTFChars(env, newfile_jstr, NULL);
    char *patchfile = (*env)->GetStringUTFChars(env, patchfile_jstr, NULL);
    //参数(第一个参数无效)
    char *argv[4];
    argv[0]="bspatch";
    argv[1]=oldfile;
    argv[2]=newfile;
    argv[3]=patchfile;
    bspatch_main(argc,argv);
    (*env)->ReleaseStringUTFChars(env,oldfile_jstr,oldfile);
    (*env)->ReleaseStringUTFChars(env,newfile_jstr,newfile);
    (*env)->ReleaseStringUTFChars(env,patchfile_jstr,patchfile);
}
  • 权限的申请
 <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"
        tools:ignore="ProtectedPermissions" />
  
  • 下载并合并
  class ApkUpdataTask extends AsyncTask<Void,Void,Boolean>{

        @Override
        protected Boolean doInBackground(Void... voids) {
            try {
                //下载差分包
                File patchFile = DownloadUtils.download(Constants.URL_PATCH_DOWNLOAD);

                //获取当前apk文件  /data/app/my.apk
                String oldfile=ApkUtils.getSourceApkPath(MainActivity.this,getPackageName());
                String newfile=Constants.NEW_APK_PATH;
                String patchfile=patchFile.getAbsolutePath();
                //合并得到最新版本的APK文件
                BsPatch.patch(oldfile,newfile,patchfile);
                Log.e("huangxiaoguo","oldfile===>"+oldfile);
                Log.e("huangxiaoguo","newfile===>"+newfile);
                Log.e("huangxiaoguo","patchfile===>"+patchfile);
            }catch (Exception e){
                e.printStackTrace();
                return false;
            }
            return true;
        }

        @Override
        protected void onPostExecute(Boolean result) {
            super.onPostExecute(result);
            //安装
            if (result){
                Toast.makeText(MainActivity.this,"安装开始",Toast.LENGTH_LONG).show();
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    boolean hasInstallPermission = MainActivity.this.getPackageManager().canRequestPackageInstalls();
                    if (!hasInstallPermission) {
                        //跳转至“安装未知应用”权限界面,引导用户开启权限
                        Uri selfPackageUri = Uri.parse("package:" + MainActivity.this.getPackageName());
                        Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, selfPackageUri);
                        startActivityForResult(intent, REQUEST_CODE_UNKNOWN_APP);
                        return;
                    }else {
                        ApkUtils.installApk(MainActivity.this,Constants.NEW_APK_PATH);
                    }
                }else {
                    ApkUtils.installApk(MainActivity.this,Constants.NEW_APK_PATH);
                }

            }
        }
    }

android整体代码

  • 工具类
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.os.Build;
import android.support.v4.content.FileProvider;
import android.text.TextUtils;

import java.io.File;


public class ApkUtils {

	public static boolean isInstalled(Context context, String packageName) {
		PackageManager pm = context.getPackageManager();
		boolean installed = false;
		try {
			pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES);
			installed = true;
		} catch (Exception e) {
			e.printStackTrace();
		}

		return installed;
	}

	/**
	 * 获取已安装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 filePath
	 */
	public static void installApk(Context context,String filePath) {
		File apkFile = new File(filePath);
		Intent intent = new Intent(Intent.ACTION_VIEW);
		intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
		intent.addCategory(Intent.CATEGORY_DEFAULT);
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
			//7.0
			intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
			Uri contentUri = FileProvider.getUriForFile(context, "com.qufu.mergeupdates.fileProvider", apkFile);
			intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
		} else {
			/**
			 * 正常安装
			 */
			intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
		}
		context.startActivity(intent);
	}
}
import java.io.File;

import android.os.Environment;

public class Constants {
	public static final String PATCH_FILE = "apk.patch";
	public static final String URL_PATCH_DOWNLOAD = "http://192.168.6.17:8080/ndk/bsdiff/"+PATCH_FILE;
	public static final String SD_CARD = Environment.getExternalStorageDirectory() + File.separator;
	//新版本apk的目录
	public static final String NEW_APK_PATH = SD_CARD+"dn_apk_new.apk";

}


import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

import android.os.Environment;

public class DownloadUtils {

	/**
	 * 下载差分包
	 * @param url
	 * @return
	 * @throws Exception
	 */
	public static File download(String url){
		File file = null;
		InputStream is = null;
		FileOutputStream os = null;
		try {
			file = new File(Environment.getExternalStorageDirectory(),Constants.PATCH_FILE);
			if (file.exists()) {
				file.delete();
			}
			HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
			conn.setDoInput(true);
			is = conn.getInputStream();
			os = new FileOutputStream(file);
			byte[] buffer = new byte[1024];
			int len = 0;
			while((len = is.read(buffer)) != -1){
				os.write(buffer, 0, len);
			}
		} catch(Exception e){
			e.printStackTrace();
		}finally{
			try {
				os.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			try {
				is.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return file;
	}
	
}

  • jni

public class BsPatch {
    /**
     * 合并
     * @param oldfile
     * @param newfile
     * @param patchfile
     */
    public native static void patch(String oldfile,String newfile,String patchfile);

    static {
        System.loadLibrary("bspatch");
    }
}


import android.Manifest;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;

import com.qufu.mergeupdates.utils.ApkUtils;
import com.qufu.mergeupdates.utils.Constants;
import com.qufu.mergeupdates.utils.DownloadUtils;
import com.qufu.mergeupdates.utils.jni.BsPatch;

import java.io.File;
import java.io.StringReader;

import tsou.cn.lib_primissions.HxgPermissionFail;
import tsou.cn.lib_primissions.HxgPermissionHelper;
import tsou.cn.lib_primissions.HxgPermissionSuccess;

public class MainActivity extends AppCompatActivity {

    private static final int REQUESE_CODE = 102;
    private int REQUEST_CODE_UNKNOWN_APP=110;

    /**
     * 使用时放在BaseActivity中或BaseFragment中
     * @param requestCode
     * @param permissions
     * @param grantResults
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        HxgPermissionHelper.requestPermissionsResult(this, requestCode, permissions);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_CODE_UNKNOWN_APP) {
            ApkUtils.installApk(MainActivity.this,Constants.NEW_APK_PATH);
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initPermission();
    }
    @HxgPermissionSuccess(requestCode = REQUESE_CODE)
    private void success() {
        Toast.makeText(this, "权限授权了", Toast.LENGTH_SHORT).show();
        new ApkUpdataTask().execute();
    }

    @HxgPermissionFail(requestCode = REQUESE_CODE)
    private void fail() {
        Toast.makeText(this, "失败了", Toast.LENGTH_SHORT).show();
    }
    private void initPermission() {
        HxgPermissionHelper.with(this)
                .requestCode(REQUESE_CODE)
                .requestPermission( Manifest.permission.WRITE_EXTERNAL_STORAGE,
                        Manifest.permission.READ_EXTERNAL_STORAGE)
                .request();
    }

    class ApkUpdataTask extends AsyncTask<Void,Void,Boolean>{

        @Override
        protected Boolean doInBackground(Void... voids) {
            try {
                //下载差分包
                File patchFile = DownloadUtils.download(Constants.URL_PATCH_DOWNLOAD);

                //获取当前apk文件  /data/app/my.apk
                String oldfile=ApkUtils.getSourceApkPath(MainActivity.this,getPackageName());
                String newfile=Constants.NEW_APK_PATH;
                String patchfile=patchFile.getAbsolutePath();
                //合并得到最新版本的APK文件
                BsPatch.patch(oldfile,newfile,patchfile);
                Log.e("huangxiaoguo","oldfile===>"+oldfile);
                Log.e("huangxiaoguo","newfile===>"+newfile);
                Log.e("huangxiaoguo","patchfile===>"+patchfile);
            }catch (Exception e){
                e.printStackTrace();
                return false;
            }
            return true;
        }

        @Override
        protected void onPostExecute(Boolean result) {
            super.onPostExecute(result);
            //安装
            if (result){
                Toast.makeText(MainActivity.this,"安装开始",Toast.LENGTH_LONG).show();
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    boolean hasInstallPermission = MainActivity.this.getPackageManager().canRequestPackageInstalls();
                    if (!hasInstallPermission) {
                        //跳转至“安装未知应用”权限界面,引导用户开启权限
                        Uri selfPackageUri = Uri.parse("package:" + MainActivity.this.getPackageName());
                        Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, selfPackageUri);
                        startActivityForResult(intent, REQUEST_CODE_UNKNOWN_APP);
                        return;
                    }else {
                        ApkUtils.installApk(MainActivity.this,Constants.NEW_APK_PATH);
                    }
                }else {
                    ApkUtils.installApk(MainActivity.this,Constants.NEW_APK_PATH);
                }

            }
        }
    }
}

  • .h头文件
/* DO NOT EDIT THIS FILE - it is machine generated */
#include 
/* Header for class com_qufu_mergeupdates_utils_jni_BsPatch */

#ifndef _Included_com_qufu_mergeupdates_utils_jni_BsPatch
#define _Included_com_qufu_mergeupdates_utils_jni_BsPatch
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_qufu_mergeupdates_utils_jni_BsPatch
 * Method:    patch
 * Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_com_qufu_mergeupdates_utils_jni_BsPatch_patch
  (JNIEnv *, jclass, jstring, jstring, jstring);

#ifdef __cplusplus
}
#endif
#endif

  • 合并c代码
/*-
 * Copyright 2003-2005 Colin Percival
 * Copyright 2012 Matthew Endsley
 * All rights reserved
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted providing that the following conditions 
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "com_qufu_mergeupdates_utils_jni_BsPatch.h"
#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 
#include 
#include 
#include 
#include 
#include 

static off_t offtin(u_char *buf)
{
    off_t y;

    y=buf[7]&0x7F;
    y=y*256;y+=buf[6];
    y=y*256;y+=buf[5];
    y=y*256;y+=buf[4];
    y=y*256;y+=buf[3];
    y=y*256;y+=buf[2];
    y=y*256;y+=buf[1];
    y=y*256;y+=buf[0];

    if(buf[7]&0x80) y=-y;

    return y;
}

int bspatch_main(int argc,char * argv[])
{
    FILE * f, * cpf, * dpf, * epf;
    BZFILE * cpfbz2, * dpfbz2, * epfbz2;
    int cbz2err, dbz2err, ebz2err;
    int fd;
    ssize_t oldsize,newsize;
    ssize_t bzctrllen,bzdatalen;
    u_char header[32],buf[8];
    u_char *old, *new;
    off_t oldpos,newpos;
    off_t ctrl[3];
    off_t lenread;
    off_t i;

    if(argc!=4) errx(1,"usage: %s oldfile newfile patchfile\n",argv[0]);

    /* Open patch file */
    if ((f = fopen(argv[3], "r")) == NULL)
        err(1, "fopen(%s)", argv[3]);

    /*
    File format:
        0	8	"BSDIFF40"
        8	8	X
        16	8	Y
        24	8	sizeof(newfile)
        32	X	bzip2(control block)
        32+X	Y	bzip2(diff block)
        32+X+Y	???	bzip2(extra block)
    with control block a set of triples (x,y,z) meaning "add x bytes
    from oldfile to x bytes from the diff block; copy y bytes from the
    extra block; seek forwards in oldfile by z bytes".
    */

    /* Read header */
    if (fread(header, 1, 32, f) < 32) {
        if (feof(f))
            errx(1, "Corrupt patch\n");
        err(1, "fread(%s)", argv[3]);
    }

    /* Check for appropriate magic */
    if (memcmp(header, "BSDIFF40", 8) != 0)
        errx(1, "Corrupt patch\n");

    /* Read lengths from header */
    bzctrllen=offtin(header+8);
    bzdatalen=offtin(header+16);
    newsize=offtin(header+24);
    if((bzctrllen<0) || (bzdatalen<0) || (newsize<0))
        errx(1,"Corrupt patch\n");

    /* Close patch file and re-open it via libbzip2 at the right places */
    if (fclose(f))
        err(1, "fclose(%s)", argv[3]);
    if ((cpf = fopen(argv[3], "r")) == NULL)
        err(1, "fopen(%s)", argv[3]);
    if (fseeko(cpf, 32, SEEK_SET))
        err(1, "fseeko(%s, %lld)", argv[3],
            (long long)32);
    if ((cpfbz2 = BZ2_bzReadOpen(&cbz2err, cpf, 0, 0, NULL, 0)) == NULL)
        errx(1, "BZ2_bzReadOpen, bz2err = %d", cbz2err);
    if ((dpf = fopen(argv[3], "r")) == NULL)
        err(1, "fopen(%s)", argv[3]);
    if (fseeko(dpf, 32 + bzctrllen, SEEK_SET))
        err(1, "fseeko(%s, %lld)", argv[3],
            (long long)(32 + bzctrllen));
    if ((dpfbz2 = BZ2_bzReadOpen(&dbz2err, dpf, 0, 0, NULL, 0)) == NULL)
        errx(1, "BZ2_bzReadOpen, bz2err = %d", dbz2err);
    if ((epf = fopen(argv[3], "r")) == NULL)
        err(1, "fopen(%s)", argv[3]);
    if (fseeko(epf, 32 + bzctrllen + bzdatalen, SEEK_SET))
        err(1, "fseeko(%s, %lld)", argv[3],
            (long long)(32 + bzctrllen + bzdatalen));
    if ((epfbz2 = BZ2_bzReadOpen(&ebz2err, epf, 0, 0, NULL, 0)) == NULL)
        errx(1, "BZ2_bzReadOpen, bz2err = %d", ebz2err);

    if(((fd=open(argv[1],O_RDONLY,0))<0) ||
       ((oldsize=lseek(fd,0,SEEK_END))==-1) ||
       ((old=malloc(oldsize+1))==NULL) ||
       (lseek(fd,0,SEEK_SET)!=0) ||
       (read(fd,old,oldsize)!=oldsize) ||
       (close(fd)==-1)) err(1,"%s",argv[1]);
    if((new=malloc(newsize+1))==NULL) err(1,NULL);

    oldpos=0;newpos=0;
    while(newpos<newsize) {
        /* Read control data */
        for(i=0;i<=2;i++) {
            lenread = BZ2_bzRead(&cbz2err, cpfbz2, buf, 8);
            if ((lenread < 8) || ((cbz2err != BZ_OK) &&
                                  (cbz2err != BZ_STREAM_END)))
                errx(1, "Corrupt patch\n");
            ctrl[i]=offtin(buf);
        };

        /* Sanity-check */
        if(newpos+ctrl[0]>newsize)
            errx(1,"Corrupt patch\n");

        /* Read diff string */
        lenread = BZ2_bzRead(&dbz2err, dpfbz2, new + newpos, ctrl[0]);
        if ((lenread < ctrl[0]) ||
            ((dbz2err != BZ_OK) && (dbz2err != BZ_STREAM_END)))
            errx(1, "Corrupt patch\n");

        /* Add old data to diff string */
        for(i=0;i<ctrl[0];i++)
            if((oldpos+i>=0) && (oldpos+i<oldsize))
                new[newpos+i]+=old[oldpos+i];

        /* Adjust pointers */
        newpos+=ctrl[0];
        oldpos+=ctrl[0];

        /* Sanity-check */
        if(newpos+ctrl[1]>newsize)
            errx(1,"Corrupt patch\n");

        /* Read extra string */
        lenread = BZ2_bzRead(&ebz2err, epfbz2, new + newpos, ctrl[1]);
        if ((lenread < ctrl[1]) ||
            ((ebz2err != BZ_OK) && (ebz2err != BZ_STREAM_END)))
            errx(1, "Corrupt patch\n");

        /* Adjust pointers */
        newpos+=ctrl[1];
        oldpos+=ctrl[2];
    };

    /* Clean up the bzip2 reads */
    BZ2_bzReadClose(&cbz2err, cpfbz2);
    BZ2_bzReadClose(&dbz2err, dpfbz2);
    BZ2_bzReadClose(&ebz2err, epfbz2);
    if (fclose(cpf) || fclose(dpf) || fclose(epf))
        err(1, "fclose(%s)", argv[3]);

    /* Write the new file */
    if(((fd=open(argv[2],O_CREAT|O_TRUNC|O_WRONLY,0666))<0) ||
       (write(fd,new,newsize)!=newsize) || (close(fd)==-1))
        err(1,"%s",argv[2]);

    free(new);
    free(old);

    return 0;
}

/**
 * 合并
 */
JNIEXPORT void JNICALL Java_com_qufu_mergeupdates_utils_jni_BsPatch_patch
        (JNIEnv *env, jclass jcls, jstring oldfile_jstr, jstring newfile_jstr,
         jstring patchfile_jstr) {
    int argc = 4;
    char *oldfile = (*env)->GetStringUTFChars(env, oldfile_jstr, NULL);
    char *newfile = (*env)->GetStringUTFChars(env, newfile_jstr, NULL);
    char *patchfile = (*env)->GetStringUTFChars(env, patchfile_jstr, NULL);
    //参数(第一个参数无效)
    char *argv[4];
    argv[0]="bspatch";
    argv[1]=oldfile;
    argv[2]=newfile;
    argv[3]=patchfile;
    bspatch_main(argc,argv);
    (*env)->ReleaseStringUTFChars(env,oldfile_jstr,oldfile);
    (*env)->ReleaseStringUTFChars(env,newfile_jstr,newfile);
    (*env)->ReleaseStringUTFChars(env,patchfile_jstr,patchfile);
}

源码地址:https://pan.baidu.com/s/1PDFmQU2gNxebqAQPPdvnuA或者:https://download.csdn.net/download/huangxiaoguo1/11594580

你可能感兴趣的:(NDK学习)