一、先看看效果图
二、简述实现过程
因为我这里是自己在编译,不是服务器给编译生成差分包,所以我这里是在本地生成差分包,然后在把差分包传到服务器然后下载即可
三、编译环境
win7系统 Android studio 2.3 jdk1.8 Eclipse(生成差分包) 、vs2013(编译bsdiff源码生成dll)
四、实现步骤
1、创建Android项目,并创建native方法
public class BsPatch {
//oldfile newfile patchfile
static{
System.loadLibrary("bspatch");
}
/**
* 合并
* @param oldfile
* @param newfile
* @param patchfile
*/
public native static void patch(String oldfile,String newfile,String patchfile);
}
2、因为合并是依赖开源项目bzip2的,所以导入他的c文件和头文件
3、引入bspatch.c的文件并修改头文件,和main方法
#include <jni.h>
#include "com_xiaofan_testdiff_utils_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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <err.h>
#include <unistd.h>
#include <fcntl.h>
int bspatch_main(int argc,char * argv[])
4、实现native方法
JNIEXPORT void JNICALL Java_com_xiaofan_testdiff_utils_BsPatch_patch
(JNIEnv * env, jclass jcls, jstring oldfile_jstr,
jstring newfile_jstr, jstring patchfile_jstr){
int argc = 4;
//参数
char* oldfile = (char*)(*env)->GetStringUTFChars(env,oldfile_jstr, JNI_FALSE);
char* newfile = (char*)(*env)->GetStringUTFChars(env,newfile_jstr, JNI_FALSE);
char* patchfile = (char*)(*env)->GetStringUTFChars(env,patchfile_jstr, JNI_FALSE);
char* argv[4];
argv[0] = "bspatch";
argv[1] = oldfile;
argv[2] = newfile;
argv[3] = patchfile;
bspatch_main(argc, argv);
(*env)->ReleaseStringUTFChars(env,oldfile_jstr, argv[1]);
(*env)->ReleaseStringUTFChars(env,newfile_jstr, argv[2]);
(*env)->ReleaseStringUTFChars(env,patchfile_jstr, argv[3]);
}
5、在Eclipse下创建java项目,创建native方法
6、然后使用javah命令生成头文件,然后把头文件引入到vs2013中
7、在vs中创建项目,导入bsdiff的源码,因为我们是要生成差分包,所以去掉了bspatch.cpp
8、修改bsdiff.cpp的方法
1、修改头部,解决非安全和函数过时问题
//解决非安全的函数
#define _CRT_SECURE_NO_WARNINGS
//解决过时函数
#define _CRT_NONSTDC_NO_DEPRECATE
#include "com_xiaofan_BsDiff.h"
#include <stdlib.h>
#include "bzlib.h"
#include <stdio.h>
#include <string.h>
//#include <err.h>
//#include <unistd.h>
#include <io.h>
#include <fcntl.h>
//#include <sys/wait.h>
2、修改cpp的入口文件,因为我们是当做dll调用
//int main(int argc,char *argv[])
int bsdiff_main(int argc, char *argv[])
9、实现natvie方法
JNIEXPORT void JNICALL Java_com_xiaofan_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, JNI_FALSE);
char* newfile = (char*)env->GetStringUTFChars(newfile_jstr, JNI_FALSE);
char* patchfile = (char*)env->GetStringUTFChars(patchfile_jstr, JNI_FALSE);
char* argv[4];
argv[0] = "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);
}
10、然后在main方法中调用
public class BsDiffTest {
static String oldfile="D:/TestDiff_old.apk";
static String newfile="D:/TestDiff_new.apk";
static String patchfile="D:/diff.patch";
public static void main(String[] args) {
//得到差分包
BsDiff.diff(oldfile, newfile, patchfile);
}
}
注意:
为什么是三个参数,因为在bsdiff.cpp的(if(argc!=4) errx(1,"usage: %s oldfile newfile patchfile\n",argv[0]);)这句话中得到的
11、在Activity中实现文件patch的下载并且调用native方法,实现文件的合并,并且生成新的包,然后去安装
我这里是直接搁在我的linux服务器的apache的
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.button1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new ApkUpdateTask().execute();
}
});
}
class ApkUpdateTask extends AsyncTask<Void, Void, Boolean> {
@Override
protected Boolean doInBackground(Void... params) {
try {
Log.i("wxf", "开始下载");
// 1、下载差分包
File patchFile = DownloadUtils
.download(Constants.URL_PATCH_DOWNLOAD);
// 2、合并得到最新版本的APK文件
/**
* oldfile 获取当前应用的apk文件(data/app)
*/
String oldfile = ApkUtils.getSourceApkPath(MainActivity.this,
Constants.PACKAGE_NAME);
String newfile = Constants.NEW_APK_PATH;
String patchfile = patchFile.getAbsolutePath();
Log.i("wxf", "oldfile:" + oldfile);
Log.i("wxf", "newfile:" + newfile);
Log.i("wxf", "patchfile:" + patchfile);
BsPatch.patch(oldfile, newfile, patchfile);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
// 3、安装
if(result){
Toast.makeText(MainActivity.this, "您正在进行无流量更新", Toast.LENGTH_LONG).show();
ApkUtils.installApk(MainActivity.this, Constants.NEW_APK_PATH);
}
}
}
}
DownloadUtils
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;
}
}
Constants
public class Constants {
public static final String PATCH_FILE = "diff.patch";
//服务器文件路径
public static final String URL_PATCH_DOWNLOAD = "http://*.**.*/" + PATCH_FILE;
public static final String PACKAGE_NAME = "com.xiaofan.testdiff";
public static final String SD_CARD = Environment.getExternalStorageDirectory() + File.separator;
//新版本apk的目录
public static final String NEW_APK_PATH = SD_CARD + "test_apk_new.apk";
public static final String PATCH_FILE_PATH = SD_CARD + PATCH_FILE;
}
ApkUtils
public class ApkUtils {
/**
* 获取已安装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 apkPath
*/
public static void installApk(Context context, String apkPath) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse("file://" + apkPath),
"application/vnd.android.package-archive");
context.startActivity(intent);
}
}
我会在我的下载里边放入我用的所有编译文件