效果:
用到的C库
bsdiff::https://github.com/mendsley/bsdiff
bzip2: https://sourceforge.net/projects/bzip2/
其中jni.h和jni_md.h头文件从系统jdk中获得,这里会出现各种报错请查看【The POSIX name for this item is deprecated. Instead,】【使用了可能未初始化的本地指针变量“xxx”】
创建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");
}
}
/* 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
//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);
}
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);
}
}
这里就将两个版本的差分包生成出来了!
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");
}
}
/* 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
/**
* 合并
*/
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);
}
}
}
}
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;
}
}
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);
}
}
}
}
}
/* 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
/*-
* 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