Android NDK实现增量更新

增量跟新,这个词相信大家都听说过,有些人也会增量跟新理解为是热跟新,其实不是,增量跟新比热跟新还要重量级一点,需要用户安装,只是下载不是高版本app的资源包,而是下载高版本app跟现版本的app的差分包,一般大小在几百K到几M之间。

增量跟新与全量跟新具有哪些优势?

主要优势:减少用户的跟新下载流量,达到轻量级跟新效果,想一想如果王者荣耀没跟新一点点就下载一个这个大的包,得多浪费流量啊,用户体验也不好。

先讲一下这个大概的流程:

客户端—–>向服务器索取升级信息——>服务器生成差分包——>客户端获取差分包—>合成新的app

这里写图片描述

服务器:

这里写图片描述

这里可以调用bspatch里面的main函数就可以生成一个.patch文件,这个就是差分包

在源代码里面bspatch的main函数有2个参数,一个int的argc,一个char*指针数组,如果你直接双击就会退出
所以我们要用cmd执行

这里写图片描述

输入以上就可以生成差分包,如果是服务器的话,需要下载bspatch的so库,然后用jni调用生成,这里暂时只讲安卓端的,服务器的先略过了哈

现在我们假如apk.patch的路径为http://192.168.1.100/apk.patch

客户端:

Android端:我们拿到bspatch的源码在visuaStudio小小修改一下,把重要的几个C文件copy到app目录当中
这里就不讲这个过程了


MainActivity:

 private static String TAG = "Activity";

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

        if (ApkUtils.getVersionCode(this ,getPackageName()) < 2.0){
            Log.i(TAG , "不是最新的版本号! 开始更新!");
            new ApkUpdateTask().execute();
        } else {
            Log.i(TAG , "最新版本号 无需更新!");
        }

    }

    class ApkUpdateTask extends AsyncTask<Void ,Void ,Boolean>{

        @Override
        protected Boolean doInBackground(Void... voids) {
            Log.i(TAG , "开始下载...");
            File patchFile = DownLoadUtils.download(Constants.URL_PATCH_DOWNLOAD);
            Log.i(TAG , "下载完成......");

            String oldFile = ApkUtils.getSourceApkPath(MainActivity.this , getPackageName());

            String newFile = Constants.NEW_APK_PATH;

            String patchFileString = patchFile.getAbsolutePath();

            Log.i(TAG , "开始合并");
            int ret = BsPatch.patch(oldFile , newFile , patchFileString);
            Log.i(TAG , "合并完成......");

            if(ret == 0) {
                return true;
            } else {
                return false;
            }
        }

        @Override
        protected void onPostExecute(Boolean aBoolean) {
            super.onPostExecute(aBoolean);
            if (aBoolean){
                Log.i(TAG , "合并成功 开始安装新APK");
                ApkUtils.installApk(MainActivity.this , Constants.NEW_APK_PATH);
            }
        }
    }

BsPatch:

    /**
     * 合并
     * @param oldFlie
     * @param newFile
     * @param patchFlie
     * @return
     */
    public native static int patch(String oldFlie , String newFile ,String patchFlie);

    static {
        System.loadLibrary("JasonBsPatch");
    }

ApkUtils:

 //获取APK版本号 在公司实际开发中 是根据 key uuid判断(渠道 版本)
    public static int getVersionCode (Context context, String packageName) {
        PackageManager pm = context.getPackageManager();
        try {
            PackageInfo info = pm.getPackageInfo(packageName, 0);
            return info.versionCode;
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 获取已安装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 (PackageManager.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);
    }

Constants:


public class Constants {

    public static final String PATCH_FILE = "apk.patch";

    public static final String URL_PATCH_DOWNLOAD = "http://000.000.0.000:8080/" +PATCH_FILE;    //这里换成自己的服务器路径

    public static final String SD_CARD = Environment.getExternalStorageDirectory() + "/";

    //新版本apk的目录
    public static final String NEW_APK_PATH = SD_CARD + "dn_apk_new.apk";

    public static final String PATCH_FILE_PATH = SD_CARD + PATCH_FILE;
}

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[1*1024];
            int len = 0;
            while((len = is.read(buffer)) != -1){
                Log.d("Tim", String.valueOf(len));
                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;
    }
}



我们先copy这几个类。

copy C文件到cpp目录

这里写图片描述

把bspatch.c的main方法修改一下名字,因为一个应用程序只能有一个main方法,

这里写图片描述

在bspatch.c的最下面添加一个自己的jni方法,给自己的应用程序调用,把方法路径修改成自己的或者自己可以在BsPatch.java的native里自己生成

/*
 * Class:     路径修改成自己的
 * Method:    patch
 * Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I
 */
JNIEXPORT jint JNICALL Java_com_112_BsPatch_patch 
        (JNIEnv *env, jclass jazz, jstring oldPath_jstr, jstring newPath_jstr, jstring patchPath_jstr){
    LOGI("jni patch begin...");
    int ret = -1;


    char *oldPath = (*env) ->GetStringUTFChars(env , oldPath_jstr ,JNI_FALSE);
    char *newPath = (*env) ->GetStringUTFChars(env , newPath_jstr ,JNI_FALSE);
    char *patchPath = (*env) ->GetStringUTFChars(env , patchPath_jstr ,JNI_FALSE);

    int argc = 4;
    char *argv[4];

    argv[0] = "JasonDsPatch";
    argv[1] = oldPath;
    argv[2] = newPath;
    argv[3] = patchPath;

    //如果成功的话,ret等于0
    ret = bspatch_main(argc ,argv);


    (*env) ->ReleaseStringUTFChars(env , oldPath_jstr , oldPath);
    (*env) ->ReleaseStringUTFChars(env , newPath_jstr , newPath);
    (*env) ->ReleaseStringUTFChars(env , patchPath_jstr , patchPath);
    return ret;
}

在CmakeList里面添加所有的C文件
这里写图片描述

把自己的c++文件删掉,不删也行,不过CmakeList要自己添加一下

然后自己动手可以试一下,本人亲测可用!


博主偷个懒,直接发个demo吧,demo地址

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值