Android使用kotlin实现增量更新详细全过程

本文详细实现增量更新流程

涉及知识点

1.kotlin基本使用
2.NDK的使用
3.ubuntu使用
4.基本命令操作

准备工作

1.下载资源文件
bzip2源码下载地址 –> http://www.bzip.org/
Binary diff/patch utility下载地址 — > http://www.daemonology.net/bsdiff/
2.需要ubuntu环境进行差分 点击下面文章查看如何进行差分
http://blog.csdn.net/xie397361457/article/details/77895113

下面正式进入代码编写

1.demo需要的Contats配置

package com.manager.textbasec
import android.os.Environment
import java.io.File
/**
 * 增量更新需要测试值
 */
class Contats {
    companion object{
        val PATCH_FILE = "apk.patch"
        //差分文件下载地址,可根据实际项目接口返回
        val URL_PATCH_DOWLOAD = "http://192.168.56.1:8080/" + PATCH_FILE
        //项目包名
        val PACKAGE_NAME = "com.manager.textbasec"
        //外置卡路径(这里是方便测试使用)---->实际开发时应该将文件getCacheDir()目录下
        val SD_CARD = Environment.getExternalStorageDirectory().absolutePath+File.separator
        //给新APK一个存放路径
        val NEW_APK_PATH = SD_CARD + "apk_new.apk"
    }
}

2.layout代码

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" 
    android:layout_width="match_parent"
    android:layout_height="match_parent">
<Button
    android:id="@+id/tv"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textColor="@color/colorAccent"
    android:textSize="16dp"
    android:text="Hello_World_VERSION1"/>
</LinearLayout>

3.Native代码

package com.manager.framesource.incremental;

/**
 * 本地方法存放类,目前推荐使用java类,不建议kotlin进行配置此类
 * 一键生成c/c++头文件
 *         1.打开Android studio 命令工具Terminal
 *         2.cd app/src/main/java  -->进入到java目录下面
 *         3.javah 本类的package + 类名   eg:  javah com.manager.framesource.incremental.BsPatchNative
 *                                        注意:如果遇见-->错误: 编码GBK的不可映射字符此类错误,是由于jdk是国际版引起的
 *                                        可以使用这个命令 javah -encoding UTF-8 com.manager.framesource.incremental.BsPatchNative
 *         4.将生成的头文件移动到cpp文件夹下面,方便管理和使用
 * kotlin 1.配置static 需要加上@JvmStatic注解
 *        2.使用kotlin,不能使用javah 进行生成c/c++头文件
 *        3.使用kotlin,不能一键链接到指定的c/c++头文件函数方法
 *
 */

public class BsPatchNative {

    public static native int bspatch(String oldPath,String newPath,String patchPath);
    static {
        System.loadLibrary("BsPatch");
    }
}

4.kotlin扩展代码

package com.manager.textbasec

import android.content.Context
import android.content.Intent
import android.net.Uri
import android.util.Log

/**
 * 使用kotlin给context添加扩展方法
 * 参数格式说明
 *     需要扩展的类.方法名(参数) : 返回值类型{ }
 */
//获取当前app版本号
public fun Context.getVersionCode():Int{
    val packageManager = this.packageManager
    val packageInfo = packageManager.getPackageInfo(this.packageName,0)
    Log.d("versionCode = ",packageInfo.versionCode.toString())
    return packageInfo.versionCode
}

//获取已安装APK文件目录
public fun Context.getSourceApkPath(packageName :String) :String{
    val appInfo = this.packageManager.getApplicationInfo(packageName,0)
    return appInfo.sourceDir
}
//安装APK
public fun Context.installApk(apkPath : String){
    val intent = Intent(Intent.ACTION_VIEW)
    intent.setDataAndType(Uri.parse("file://"+apkPath),"application/vnd.android.package-archive")
    this.startActivity(intent)
}

5.jni/c代码

这是导入C代码的截图

这是导入c代码的截图

在bspatch.c加入方法

JNIEXPORT jint JNICALL Java_com_manager_textbasec_BsPatchNative_bspatch
        (JNIEnv *env, jclass clazz, jstring oldPath, jstring newPath, jstring patchPath){
    int result = -1;

    int argc = 4;//上面main函数需要传的值
    char *argv[4];
    argv[0] = "BsPatch";
    LOGD("JNI bspatch begin");

    const char* oldPath_c = (*env)->GetStringUTFChars(env,oldPath,JNI_FALSE);
    const char* newPath_c = (*env)->GetStringUTFChars(env,newPath,JNI_FALSE);
    const char* patchPath_c = (*env)->GetStringUTFChars(env,patchPath,JNI_FALSE);

    argv[1] = oldPath_c;
    argv[2] = newPath_c;
    argv[3] = patchPath_c;
    //调用bapatch,开始合并
    result = bspatchMain(argc,argv);

    if (result < 0){
        LOGD("JNI Bspatch fail");
    }

    //释放内存
    (*env)->ReleaseStringUTFChars(env,oldPath,oldPath_c);
    (*env)->ReleaseStringUTFChars(env,newPath,newPath_c);
    (*env)->ReleaseStringUTFChars(env,patchPath,patchPath_c);
    free(argv);
    return result;
}

6.编辑CMakeList.txt


cmake_minimum_required(VERSION 3.4.1)

set(distribution_DIR ../../../../src/main/jniLibs/${ANDROID_ABI})

#增量更新 需要用到的c代码
#bzip2源码下载地址   -->   http://www.bzip.org/
#(Binary diff/patch utility)bsPatch下载地址 --- > http://www.daemonology.net/bsdiff/
#(Binary diff/patch utility)bsPatch下载包里面还有个bsdiff此C文件是进行差分算法
#说明:上面两个包里面的C文件,凡是有main 函数,需要自己更改函数名字
add_library(
              BsPatch
              SHARED
              src/main/cpp/incremental/bzip2/blocksort.c
              src/main/cpp/incremental/bzip2/bzip2.c
              src/main/cpp/incremental/bzip2/bzip2recover.c
              src/main/cpp/incremental/bzip2/bzlib.c
              src/main/cpp/incremental/bzip2/compress.c
              src/main/cpp/incremental/bzip2/crctable.c
              src/main/cpp/incremental/bzip2/decompress.c
              src/main/cpp/incremental/bzip2/dlltest.c
              src/main/cpp/incremental/bzip2/huffman.c
              src/main/cpp/incremental/bzip2/mk251.c
              src/main/cpp/incremental/bzip2/randtable.c
              src/main/cpp/incremental/bzip2/spewG.c
              src/main/cpp/incremental/bzip2/unzcrash.c
              src/main/cpp/incremental/bspatch.c
               )
add_library(
             native-lib
             SHARED
             src/main/cpp/native-lib.cpp )
find_library(
              log-lib
              log )
#include_dircetories : 包含头文件到类库里面 参数-->有头文件(.h文件)所涉及到的目录
include_directories(src/main/cpp)
include_directories(src/main/cpp/incremental)
include_directories(src/main/cpp/incremental/bzip2)
target_link_libraries(
                       BsPatch
                       native-lib
                       ${log-lib} )

7.Activity代码

package com.manager.textbasec

import android.os.Bundle
import android.os.Environment
import android.support.v7.app.AppCompatActivity
import android.util.Log
import android.widget.Button
import com.manager.framesource.R
import com.manager.framesource.incremental.BsPatchNative
import org.jetbrains.anko.async
import org.jetbrains.anko.uiThread
import java.io.File
import java.net.URL

/**
 * 增量更新
 */
class MainActivity : AppCompatActivity(){


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.incremental_activity_main)
        val tv = findViewById(R.id.tv) as Button
        tv.setOnClickListener {
            //版本比对-->可根据实际开发,定义定制规则
            if (this.getVersionCode() < 2){
                downAndPatch()
            }
        }
    }
    fun downAndPatch(){
        //kotlin 封装的异步下载
        async {
            //下载文件
            val bytes = URL(Contats.URL_PATCH_DOWLOAD).readBytes()
            //定义文件下载后存放路径
            val patchFile = File(Environment.getExternalStorageDirectory(),Contats.PATCH_FILE)
            Log.d("patchFile.ab = ",patchFile.absolutePath)
            if (patchFile.exists()){
                patchFile.delete()
            }
            //写入新文件
            patchFile.writeBytes(bytes)
            //进行合并 1.oldApk->放在系统的apk 目录= 当前安装的apk
            var oldPatch =  this@MainActivity.getSourceApkPath(Contats.PACKAGE_NAME)
            Log.d("oldPatch.ab = ",oldPatch)
            var newPath = Contats.NEW_APK_PATH
            Log.d("newPath.ab = ",newPath)
            var patchPath = patchFile.absolutePath
            Log.d("patchPath.ab = ",patchPath)
            //开始合并
            BsPatchNative.bspatch(oldPatch,newPath,patchPath)
            uiThread {
                //安装APK
                this@MainActivity.installApk(newPath)
            }
        }
    }
}

测试

1.将build.gradle versionCode设置 1

layout 截图

这里写图片描述

运行–>生成–>old.APK

2.将build.gradle versionCode设置 2

这里写图片描述

运行–>生成new.apk

3.生成差分文件 .patch

将old.apk 与 new.apk 放到ubuntu里面进行差分

4.把.patch差分文件放到服务器进行下载使用

总结

实现增量更新原理就是使用当前版本old.apk和差分文件.patch进行合并重新生成新的new.apk

难点:
1.需要对c/c++,jni,kotlin代码掌握
2.需要对cmakelists.txt配置有一定的掌握
3.ubuntu进行差分使用,本人在这上面踩过很多坑,起初想自己更改makeFile
生成bspatch和bsdiff工具,但是网上资料太杂以及对shell的不熟悉,始终没能生成工具,所以才使用了ubuntu现成工具,就轻松搞定
4.获取app当前版本APK一定要使用

var oldPatch = this@MainActivity.getSourceApkPath(Contats.PACKAGE_NAME)

来获取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值