感谢博主的分享:http://blog.csdn.net/brok1n/article/details/50406774
GitHub项目:https://github.com/JiangAndroidwork/UpdateApk/tree/master
增量更新的原理就不过多说了网上很多,就是利用二进制对比新旧apk文件产生差异包,通过下载差异包再与旧的apk合并成新的一个apk,使用的工具就是bsdiff二进制工具。
我特此记录一下整个流程以便其他人参考,自己亲测是可以。工具文件我单独存在了百度云上面可以去上面下载http://pan.baidu.com/s/1eSDl9eE,里面有bsdiff的生成差分包和合并的工具,还有项目中用到的so文件和jar包。
1.首先利用bsdiff工具生成差分包,将bsdiff.exe、旧的apk和新的apk放在一个文件夹里面。
2.运行cmd,cd到“更新测试”的文件夹下,输入 bsdiff.exe old.apk new.apk update.patch命令回车就会生成patch差分包在文件夹中。
可以看到文件夹中生成了差分包
之后就需要我们把差分包放在服务器上面下载到手机特定文件夹下面与旧的apk合并成新的apk。
建立工程,把从百度云下载的libs下的文件放在工程的libs目录下。
具体代码如下:
package cn.com.bjhj.demo.ui.activity.work;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.skywds.android.bsdiffpatch.JniApi;
import java.io.File;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import cn.com.bjhj.baseframework.ui.activity.BaseActivity;
import cn.com.bjhj.demo.R;
/**
* Created by Jiang on 2016/11/29.
*/
public class ManageCanlendarActivity extends BaseActivity {
@BindView(R.id.bt_geng)
Button btGeng;
private String mLocalApkPath;//旧apk位置
@Override
public void setRootView() {
}
@Override
public void initWidget() {
}
@Override
public void widgetClick(View var1) {
}
@Override
public void onNetWorkChange(boolean available) {
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// TODO: add setContentView(...) invocation
setContentView(R.layout.activity_managecalendar);
ButterKnife.bind(this);
mLocalApkPath = Environment.getExternalStorageDirectory() + "/bjhj/oldapk/old.apk";
}
@OnClick(R.id.bt_geng)
public void onClick() {//更新按钮
new Thread(new BsPatchRunnable()).start();
}
/**
* 增量更新库。需要放到异步线程中使用 !!!
* */
public class BsPatchRunnable implements Runnable
{
@Override
public void run() {
if( !Environment.MEDIA_MOUNTED.equals( Environment.getExternalStorageState() ))
{
toast("未检测到外部存储设备,无法导出文件");
return ;
}
File file = new File(mLocalApkPath);
if( file.exists() && file.canRead() )
{
toast("文件可以读取");
}
else
{
toast("文件不存在或 文件无法读取");
return;
}
//旧版apk存放文件
String oldFile = file.getAbsolutePath();
//新版本apk存放文件
String newFile = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "bjhj/bsdiff" + File.separator + "new.apk";
//将增量更新patch文件放在 这里
String updateFile = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "bjhj/bsdiff" + File.separator + "update.patch";
//创建新版本apk所在文件夹
new File( newFile ).getParentFile().mkdirs();
File patchFile = new File( updateFile );
if( !patchFile.exists() )
{
toast("增量更新文件不存在");
return ;
}
//增量更新生成新版apk安装包
int bspatch = JniApi.bspatch(oldFile, newFile, updateFile);
toast("增量更新文件处理完成:" + bspatch);
if( bspatch == 0 )
{
toast("补丁包处理完毕, 开始安装新版本");
}
else
{
toast("补丁包处理失败");
}
//安装新版apk文件
installNewApk( newFile );
}
}
/**
* 安装apk
* */
public void installNewApk( String filePath )
{
// 核心是下面几句代码
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File(filePath)),
"application/vnd.android.package-archive");
startActivity(intent);
}
public void toast( String str )
{
Message msg = Message.obtain();
msg.what = 0x123;
msg.obj = str;
mHandler.sendMessage(msg);
}
Handler mHandler = new Handler()
{
@Override
public void handleMessage(Message msg) {
if( msg.what == 0x123 )
Toast.makeText(mContext, "" + msg.obj.toString(), Toast.LENGTH_SHORT).show();
}
};
}
到此完成了增量更新
本来到这结束了,过了几天发现人问我关于旧版本apk如何获取放到指定文件夹的问题,我特此在这添加一点内容,当然就是只针对程序运行所在的应用实现增量更新了,对其他的安卓包不起作用。修改后的代码如下:
package cn.com.bjhj.demo.ui.activity.work;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.skywds.android.bsdiffpatch.JniApi;
import java.io.File;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import cn.com.bjhj.baseframework.ui.activity.BaseActivity;
import cn.com.bjhj.demo.R;
/**
* Created by Jiang on 2016/11/29.
*/
public class ManageCanlendarActivity extends BaseActivity {
@BindView(R.id.bt_geng)
Button btGeng;
private String mLocalApkPath;//旧apk位置
@Override
public void setRootView() {
}
@Override
public void initWidget() {
}
@Override
public void widgetClick(View var1) {
}
@Override
public void onNetWorkChange(boolean available) {
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// TODO: add setContentView(...) invocation
setContentView(R.layout.activity_managecalendar);
ButterKnife.bind(this);
mLocalApkPath = Environment.getExternalStorageDirectory() + "/bjhj/oldapk/old.apk";
}
@OnClick(R.id.bt_geng)
public void onClick() {//更新按钮
new Thread(new BsPatchRunnable()).start();
}
/**
* 增量更新库。需要放到异步线程中使用 !!!
* */
public class BsPatchRunnable implements Runnable
{
@Override
public void run() {
if( !Environment.MEDIA_MOUNTED.equals( Environment.getExternalStorageState() ))
{
toast("未检测到外部存储设备,无法导出文件");
return ;
}
// File file = new File(mLocalApkPath);
//
//
// if( file.exists() && file.canRead() )
// {
// toast("文件可以读取");
// }
// else
// {
// toast("文件不存在或 文件无法读取");
// return;
// }
//旧版apk存放文件
// String oldFile = file.getAbsolutePath();
String oldVersionPath = getOldVersionPath(this);
//新版本apk存放文件
String newFile = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "bjhj/bsdiff" + File.separator + "new.apk";
//将增量更新patch文件放在 这里
String updateFile = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "bjhj/bsdiff" + File.separator + "update.patch";
//创建新版本apk所在文件夹
new File( newFile ).getParentFile().mkdirs();
File patchFile = new File( updateFile );
if( !patchFile.exists() )
{
toast("增量更新文件不存在");
return ;
}
//增量更新生成新版apk安装包
int bspatch = JniApi.bspatch(oldVersionPath, newFile, updateFile);
toast("增量更新文件处理完成:" + bspatch);
if( bspatch == 0 )
{
toast("补丁包处理完毕, 开始安装新版本");
}
else
{
toast("补丁包处理失败");
}
//安装新版apk文件
installNewApk( newFile );
}
}
/**
* 安装apk
* */
public void installNewApk( String filePath )
{
// 核心是下面几句代码
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File(filePath)),
"application/vnd.android.package-archive");
startActivity(intent);
}
public void toast( String str )
{
Message msg = Message.obtain();
msg.what = 0x123;
msg.obj = str;
mHandler.sendMessage(msg);
}
Handler mHandler = new Handler()
{
@Override
public void handleMessage(Message msg) {
if( msg.what == 0x123 )
Toast.makeText(mContext, "" + msg.obj.toString(), Toast.LENGTH_SHORT).show();
}
};
/**
*获取原始版本路径
* @return
*/
public String getOldVersionPath(Context context) {
context = context.getApplicationContext();
ApplicationInfo applicationInfo = context.getApplicationInfo();
String apkPath = applicationInfo.sourceDir;
return apkPath;
}}
只是增加了一个方法用来获取本应用的资源路径,这样就可以不用往手机里面放apk安装包 直接将差分包放在指定文件夹 就可以更新了。
安卓7.0以上的安装请参考:点击打开链接