apk增量更新的初步实现

在市场下我们经常见到省流量更新,特别是游戏类的apk,假如一个游戏apk有100M,那我下次版本有110M,我是不是要下载这110M的apk呢?这显然在用户的角度是不合理的,增量更新就是只要用户下载这10M的差分包就可以了。

原理就是:1:在服务器端通过old.apk与新的new.apk进行比较,生成一个.patch文件的差分包。来共用户下载。

    2:用户收到版本升级的提示框,就从服务器下载这个.patch差分包文件,在与当前安装的apk文件进行合成。生成一个新的apk文件,即:old.apk+.patch

    3:用户在来替换安装这个新的apk文件,即old.apk+.patch


注意:如果已经上线运营的apk有1.1   1.2   1.3 三个版本了。那么需要升级1.4的时候,在服务器分别要用1.4与1.1  1.2   1.3做比较,生成3个差分包。

这里提供一个生成差分包的测试工具:http://download.csdn.net/detail/qq_17387361/9663516

1:首先新建一个old工程。即ver:1.0

package com.demo;

import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;

import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

import com.example.incrementalupdates.R;
import com.nothome.delta.Delta;
import com.nothome.delta.DiffWriter;
import com.nothome.delta.GDiffWriter;

/**
 * 增量更新
 * 
 * 
 * */
public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		setContentView(R.layout.main);

		Button btnCreate = (Button) findViewById(R.id.btnCreate);
		Button btnMix = (Button) findViewById(R.id.btnMix);
		Button btnInstall = (Button) findViewById(R.id.btnInstall);

		btnCreate.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				createPatch();
			}
		});
		btnMix.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				mixPatch();
			}
		});
		btnInstall.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				installAPK();
			}
		});
	}

	/**
	 * 生成差分包:old_new.patch = diff(old.apk, old.apk)
	 * 
	 * */
	private void createPatch() {
		try {
			String sd = Environment.getExternalStorageDirectory().getPath();

			String oldFile = sd + "/henry/old.apk";
			String newFile = sd + "/henry/new.apk";
			String patchFile = sd + "/henry/old_new.patch";

			DiffWriter output = null;
			File sourceFile = null;
			File targetFile = null;

			sourceFile = new File(oldFile);
			targetFile = new File(newFile);
			output = new GDiffWriter(new DataOutputStream(
					new BufferedOutputStream(new FileOutputStream(new File(
							patchFile)))));

			if (sourceFile.length() > Integer.MAX_VALUE
					|| targetFile.length() > Integer.MAX_VALUE) {
				System.err
						.println("source or target is too large, max length is "
								+ Integer.MAX_VALUE);
				System.err.println("aborting..");

			}

			Delta d = new Delta();
			d.compute(sourceFile, targetFile, output);

			Toast.makeText(getApplicationContext(), "生成完成!", Toast.LENGTH_LONG)
					.show();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 合成差分包:new.apk = old.apk + old_new.patch
	 * */
	private void mixPatch() {
		try {

			String sd = Environment.getExternalStorageDirectory()
					.getAbsolutePath();
			String serviceFile = sd + "/henry/new.apk";
			String source = sd + "/henry/old.apk";
			String patch = sd + "/henry/old_new.patch";

			String target = sd + "/henry/mix.apk";

			String newMD5 = DiffTool.getMD5(new File(serviceFile));

			DiffTool.mergeApk(source, patch, target, newMD5);

			Toast.makeText(getApplicationContext(), "合成完成!", Toast.LENGTH_LONG)
					.show();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 安装apk。 这边路径已经写死,实际应用中,apk路径需要当参数传入
	 * */
	private void installAPK() {
		File apkfile = new File(Environment.getExternalStorageDirectory()
				.getAbsolutePath() + "/henry/mix.apk");
		if (!apkfile.exists()) {
			return;
		}
		Intent i = new Intent(Intent.ACTION_VIEW);
		i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
		i.setDataAndType(Uri.parse("file://" + apkfile.toString()),
				"application/vnd.android.package-archive");
		MainActivity.this.startActivity(i);

	}

}
下面是检验MD5的工具类

package com.demo;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.MessageDigest;

import com.nothome.delta.GDiffPatcher;

public class DiffTool {

	private final static char[] hexChar = { '0', '1', '2', '3', '4', '5', '6',
			'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };

	private static String toHexString(byte[] b) {
		StringBuilder sb = new StringBuilder(b.length * 2);
		for (int i = 0; i < b.length; i++) {
			sb.append(hexChar[((b[i] & 0xF0) >>> 4)]);
			sb.append(hexChar[(b[i] & 0xF)]);
		}
		return sb.toString();
	}

	public static String getMD5(File file) {
		InputStream fis = null;
		String str = null;
		try {
			fis = new FileInputStream(file);
			byte[] buffer = new byte[1024];
			MessageDigest md5 = MessageDigest.getInstance("MD5");
			int numRead = 0;
			while ((numRead = fis.read(buffer)) > 0) {
				md5.update(buffer, 0, numRead);
			}

			str = toHexString(md5.digest());
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (fis != null) {
				try {
					fis.close();
				} catch (Exception e) {

				}
			}
		}

		return str;
	}

	private static File mergeFile(final String source, final String patch,
			String target) throws Exception {
		GDiffPatcher patcher = new GDiffPatcher();
		File deffFile = new File(patch);
		File updatedFile = new File(target);
		patcher.patch(new File(source), deffFile, updatedFile);
		return updatedFile;
	}

	public static File mergeApk(final String source, final String patch,
			final String target, String newApkMd5) throws Exception {
		File updateFile = mergeFile(source, patch, target);
		String ufpMd5 = getMD5(updateFile);
		System.out
				.println("服务端下发的md5:" + newApkMd5 + ",新合并后的apk MD5:" + ufpMd5);
		if (ufpMd5 == null || !newApkMd5.equalsIgnoreCase(ufpMd5)) {
			if (updateFile.exists()) {
				updateFile.delete();
			}
			throw new Exception("MD5错误,不能成功合并!");
		}

		return updateFile;
	}

	public static void main(String args[]) throws Exception {

		try {
			System.out.println("old Md5:"
					+ DiffTool.getMD5(new File("d:/diff/appstore2024.apk")));
			File sourceFile = new File("d:/diff/appstore2017.apk");
			File patchFile = new File("d:/diff/appstore.patch");
			File outputFile = new File("d:/diff/appstore2025.apk");

			if (sourceFile.length() > Integer.MAX_VALUE
					|| patchFile.length() > Integer.MAX_VALUE) {
				System.err
						.println("source or patch is too large, max length is "
								+ Integer.MAX_VALUE);
				System.err.println("aborting..");
				return;
			}
			GDiffPatcher patcher = new GDiffPatcher();
			patcher.patch(sourceFile, patchFile, outputFile);

			System.out.println("finished patching file");

		} catch (Exception ioe) { // gls031504a
			System.err.println("error while patching: " + ioe);
		}

		System.out.println("new Md5:"
				+ DiffTool.getMD5(new File("d:/diff/appstore2025.apk")));

	}

}

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.incrementalupdates"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="18" />

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.demo.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

2:由于这里是服务器的步骤省略了,都放在客户端进行的,所以,先在手机内存卡中建一个henry文件夹用于存放差分包和当前安装的apk文件

这是old.apk的界面:




然后我们在在上面工程文件中界面改改,当做升级的新版本new.apk  记住versioncode和versionname要改一下.界面如下:



同样的,运行之后把new.apk和old.apk和差分包.patch文件一同放入henry文件夹下面。现在我们可以开始做差分包了。

差分包工具博文开始处有下载地址。里面的使用写的很清楚了。



现在我们运行old.APK文件,这里可以点击生成差分包按钮,也可以不点击,因为我们用工具已经生成了差分包。



那个mix.apk就是差分包和old.apk合成的新的apk,最后直接替换安装即可。


最后供上我在网上找的一个jar包,此项目中所需要的:http://download.csdn.net/detail/qq_17387361/9663633



  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值