android增量更新中差分包生成以及合成

网上看了很多的帖子,单对于第一次接触增量更新的朋友,会碰到各种坑,浪费大量时间。

说到增量更新并非热修复,增量更新具体实现逻辑是:根据新旧包之间的差异生成对应的二进制差异包文件,然后将此差异文件合成到老的 apk中使之含有新版本的包的代码来达到更新效果。

下面是关于个人查阅资料总结的分差包生成以及合成的具体步骤

一、首先需要下载对应的差分包生成合成的jni文件(bsdiff-4.3、bzip2-1.0.6)

bsdiff-4.3具体文件内容如图:

bzip2-1.0.6具体文件内容如图:

二、要确定的你android studio版本是否是大于2.2,大于2.2的版本可以直接创建带native项目的工程,创建项目时直接勾选 include c++ support选项如图

三、当然AS还要支持ndk编译,如果不支持就需要去安装一些插件主要勾选 CMAKE 、LLDB、NDK安装,如图:

四、前提工作已经准备就绪,按流程创建一个勾选了include C++ support项目。

五、将前面下载下来的工具包里面的c(库文件)导入到cpp文件中,具体需要导入的文件可以参考下图其中bs.h和bs.c是我们需要自己编写的文件:


bs.h头文件:

//
// Created by Administrator on 2017/8/15 0015.
//

#ifndef BSDIFFPATCH_BS_H
#define BSDIFFPATCH_BS_H
#endif //BSDIFFPATCH_BS_H
#include <malloc.h>
#include <jni.h>
JNIEXPORT jint JNICALL
Java_com_example_administrator_applicationc_MainActivity_patch
        (JNIEnv *env, jobject instance, jstring oldpath_, jstring newpath_,jstring patch_);

JNIEXPORT jint JNICALL
Java_com_example_administrator_applicationc_MainActivity_diff
        (JNIEnv *env, jobject instance, jstring oldpath_, jstring newpath_, jstring patch_);
bs.c类文件:

//
// Created by Administrator on 2017/8/15 0015.
//


#include "bs.h"
#include "bsdiff.c"
#include "bspatch.c"


JNIEXPORT jint JNICALL
               Java_com_example_administrator_applicationc_MainActivity_patch
                       (JNIEnv *env, jobject instance, jstring oldpath_, jstring newpath_,jstring patch_) {
    const char* argv[4];
    argv[0] = "bspatch";
    argv[1] = (*env)->GetStringUTFChars(env,oldpath_, 0);
    argv[2] = (*env)->GetStringUTFChars(env,newpath_, 0);
    argv[3] = (*env)->GetStringUTFChars(env, patch_, 0);
    //该函数用于合并差分包
    mergeDiffApk(4,argv);
    (*env)->ReleaseStringUTFChars(env,oldpath_, argv[1]);
    (*env)->ReleaseStringUTFChars(env,newpath_, argv[2]);
    (*env)->ReleaseStringUTFChars(env,patch_,argv[3]);
    free(argv);
    return 0;
}

JNIEXPORT jint JNICALL
               Java_com_example_administrator_applicationc_MainActivity_diff
                       (JNIEnv *env, jobject instance, jstring oldpath_, jstring newpath_, jstring patch_) {
    int argc = 4;
    char *argv[argc];
    argv[0] = (char *) "bspatch";
    argv[1] = (char *) (*env)->GetStringUTFChars(env, oldpath_, 0);
    argv[2] = (char *) (*env)->GetStringUTFChars(env, newpath_, 0);
    argv[3] = (char *) (*env)->GetStringUTFChars(env, patch_, 0);
    jint result = generateDiffApk(argc, argv);
    (*env)->ReleaseStringUTFChars(env, oldpath_, argv[1]);
    (*env)->ReleaseStringUTFChars(env, newpath_, argv[2]);
    (*env)->ReleaseStringUTFChars(env, patch_, argv[3]);
    return result;

}

其中的mergeDiffApk方法在bspatch.c中,将bspatch.c中的main方法名称改成mergeDiffApk.
其中的generateDiffApk方法在bsdiff.c中,将bsdiff.c中的mian方法名改成generateDiffApk.
六、jni部分已经准备就绪,下面是关于MainActivity的调用以及新、旧包的准备。
先准备好需要生成差异包的apk,将此新旧apk放到sdk卡的根目录中如图:

放好包后就是执行ManiActivity中的差分包执行方法,MainActivity具体测试代码如下:
package com.example.administrator.applicationc;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Environment;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import java.io.File;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    //旧版本
    String old =getsdpath()+"hello.apk";
    //新版本
    String newp = getsdpath()+"hehehe.apk";
    //差分包
    String patch = getsdpath()+"patch.patch";
    //旧版apk和差分包合并生成的新版apk
    String tmp = getsdpath()+"new.apk";


    private final  String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        findViewById(R.id.bt_diff).setOnClickListener(this);
        findViewById(R.id.bt_patch).setOnClickListener(this);


        findViewById(R.id.bt_one).setOnClickListener(this);
        findViewById(R.id.bt_two).setOnClickListener(this);

        int REQUEST_EXTERNAL_STORAGE = 1;
        String[] PERMISSIONS_STORAGE = {
                Manifest.permission.READ_EXTERNAL_STORAGE,
                Manifest.permission.WRITE_EXTERNAL_STORAGE
        };
        int permission = ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE);

        if (permission != PackageManager.PERMISSION_GRANTED) {
            // We don't have permission so prompt the user
            ActivityCompat.requestPermissions(
                    MainActivity.this,
                    PERMISSIONS_STORAGE,
                    REQUEST_EXTERNAL_STORAGE
            );
        }

    }



    private String getsdpath(){
        return Environment.getExternalStorageDirectory().getPath()+ File.separator;
    }
    //生成差分包
    public native int diff(String oldpath,String newpath,String patch);
    //旧apk和差分包合并
    public native int patch(String oldpath,String newpath,String patch);
    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.bt_diff:
                Log.e(TAG,"patch_old:"+old);
                Log.e(TAG,"patch_newp:"+newp);
                Log.e(TAG,"patch_patch:"+patch);
                long s = System.currentTimeMillis();

                diff(old,newp,patch);
                long s1 = System.currentTimeMillis();
                Toast.makeText(MainActivity.this,"生成差分包成功,用时:"+(s1-s)+"ms",Toast.LENGTH_SHORT).show();
                break;
            case R.id.bt_patch:
                long s2 = System.currentTimeMillis();
                Log.e(TAG,"patch_old:"+old);
                Log.e(TAG,"patch_newp:"+tmp);
                Log.e(TAG,"patch_patch:"+patch);
                        patch(old,tmp,patch);
                long s3 = System.currentTimeMillis();
                Toast.makeText(this,"差分包合并成功,用时:"+(s3-s2)+"ms",Toast.LENGTH_SHORT).show();
                break;
            case R.id.bt_one:
                Toast.makeText(this,"按钮1",Toast.LENGTH_SHORT).show();
                break;
            case R.id.bt_two:
                Toast.makeText(this,"按钮2",Toast.LENGTH_SHORT).show();
                break;
        }
    }
}

当然必要的权限不能少
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章