如何应对Android面试官 -> Android 如何实现增量更新,Tinker patch包生成核心原理

Android更新优化 - 增量更新是如何节省用户时间和流量的 最后聊一聊更新的具体逻辑。增量更新有固定数量法和固定业务法。你可以规定,相差版本小于等于3个,就采用增量更新,大于3个,直接走全量更新。此为固定数量法。固定业务法则是,版本分为大小版本号的更新,每个大版本必须走全量更新,而大版本中的小版本则采用增量更新。大版本采用强制更新,而小版本则可采用选择更新。每次更新到大版本,可能会再次拉取该版本的最终小版本的补丁文件发起二次更新。 阅读详情

前言


image.png

本章主要讲解 Dex 格式分析以及增量更新实现原理;

增量更新


增量更新:记录版本之间的差异信息,把这个差异信息记录到一个文件里面,将这个文件下发到旧的版本上,实现增量更新,而无需下载完整的新版本;

bsdiff

基于二进制的一个差分算法,不依赖产物,例如一个是 mp3 一个是 mp4 都可以进行差分算法;

官网下载链接:http://www.daemonology.net/bsdiff/

上面也有相关的教程,如何在 Window、Mac、Linux 上使用,我们重点要讲解的是 bsdiff 如何在 Android 上使用;

这里介绍下 WIndows 上的使用方式:

bsdiff.c 和 bspatch.c 两个c文件,通过CL编译器 可以生成可执行文件bsdiff.exe 和 bspatch.exe

打开 cmd 执行 bsdiff oldfile newfile pathcfile 生成差分文件,命令行举例:bsdiff old.apk new.apk patch.apk

打开cmd 执行 bspatch oldfile newfile patchfile 使用差分文件和旧文件 生成新文件,命令行举例:bspatch old.apk new.apk patch.apk

那么如何在 Android 上使用 bsdiff 呢?我们来一步一步的探索下;

Android 上使用 bsdiff

首先我们需要在 AS 上创建一个 C++ Project;

创建 C++ Project


image.png

当我们创建完一个 C++ Project 之后,可以看到在 app 下的 build.gradle 中,会额外多出几个配置选项,一个是 android 下的,一个是 defaultConfig 下的 externalNativeBuild

defaultConfig {
    externalNativeBuild{
        cmake{
            abiFilters 'x86','armeabi-v7a'
        }
    }
}
android {
    externalNativeBuild{
        cmake{
            path 'src/main/cpp/CMakeLists.txt'
        }
    }
}

可以看到 android 下的这个配置和 defaultConfig 下的配置是不一样的,android 下的是指定编译文件的地址,你可以修改它,只要能指向你的编译文件即可;

而 defaultConfig 下的 externalNativeBuild 下的是给编译用的,也就是说,我们要编译的目标平台有哪些;

另外,当我们在引入 so 文件的时候,可以通过 ndk 配置,指定要引入的 so 架构包

ndk {
   abiFilters  'x86', 'armeabi-v7a'
}

这个配置是说:打包 APK 的时候,哪些 CPU 平台的 so 要打包进 APK;

增量更新,针对的是手机上由旧版本的 apk 增量更新到新版本的 apk,也就是说我们的 apk 中要支持增量集成,而 patch 的生成,则是放到我们的流水线上,同时将增量 patch 上传到我们的服务器上,供旧的 apk 下载下来进行合成;

所以,我们的工程中要集成的时候 bspatch.c 文件来进行增量合成;

引入 bspatch.c 文件


将 bspatch.c 复制到 cpp 文件夹下即可;

我们接着打开 CMakeLists.txt 文件,来修改我们的编译配置;

我们将里面的注释全部删掉,方便我们来看代码;

image.png

重点要关注的是 add_library 方法,我们需要将 bspatch.c 加入进来;

add_library(
        bspatch_utlis
        SHARED
        native-lib.cpp
        bspatch.c)

第一个参数表示:我们想要生成的 so 文件名字,最终生成的 so 文件名字就是:libbspatch_utils.so;

同步的我们将 target_link_libraries 方法的第一个参数也修改成 bspatch_utlis

target_link_libraries(
        bspatch_utlis
        log)

执行 sync,等待 sync 完成,然后我们进入 bspatch.c 文件可以看到,很多红色的报错已经消失掉了,但是还是会有一些报错,我们来逐一的看下;

image.png

可以看到 bspatch 其实依赖了 bzip,但是它找不到 bzip,那么就需要手动的将 bzip 导入进来;我们只需要去官网下载 bzip 的源码,然后导入进来即可;

bzip官方地址:http://www.bzip.org/

然后复制到 cpp 目录下:

image.png

然后,我们需要将这些 .c 文件全部引入进来,批量引入是有技巧的,CMake 给我们提供了一些 API,方便我们批量引入;

aux_source_directory()

这个方法需要两个参数,一个传入 bzip2,一个传入 SOURCE; 第一个是 bzip2 的相对目录,第二个表示,把这个 bzip2 下的所有源文件放到一个 SOURCE 集合中,这个 SOURCE 是一个变量,你可以任意的起名字,可以叫 AAA、CCC等等;

这样,我们就可以直接引用这个 SOURCE 就可以了;

add_library(
        bspatch_utlis
        SHARED
        native-lib.cpp
        bspatch.c
        ${SOURCE})

sync 之后,我们执行一下 Make Project,结果发现还是报错:

image.png

发现,其实还是找不到 bzlib.h 我们明明已经引入了,但是还是报错,这个是因为什么呢?这个就是因为 C 和 C++ 在引入头文件的时候,使用 “” 和 <> 的区别,这么我们需要兼容处理下;

# 设置头文件查找路径
include_directories(bzip2)

我们需要告诉编译器,从 bzip2 下查找相关头文件;sync 一下,等待执行结果,我们来 make 下;可以看到,我们这次就 Make 成功了;

这样我们的 bspatch_utils 就引入进来了,怎么调用这个 so 呢?我们来写一个 Java 调用层;

调用 bspatch_utils


public class BsPatchUtils {

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

    public static native int patch();
}

我们来写一个 Java 层调用的类,加载我们创建的 so,并通过 native 关键字来调用这个 so 中的 patch 方法;

patch 方法默认是不存在的,我们通过 AS 的提示,可以在 native-lib.cpp 中生成这个方法的实现;

extern "C"
JNIEXPORT jint JNICALL
Java_com_example_bsdiff_BsPatchUtils_patch(JNIEnv *env, jclass clazz) {
    // TODO: implement patch()
}

因为我们调用 patch 逻辑,需要旧的 apk 文件,新的 apk 文件,以及对应的 patch 文件,所以 Java 层的方法修改如下:

public static native int patch(String oldApk, String newApk, String patch);

native_lib.cpp 中方法生成如下:

extern "C"
JNIEXPORT jint JNICALL
Java_com_example_bsdiff_BsPatchUtils_patch(JNIEnv *env, jclass clazz, jstring old_apk,
                                           jstring new_apk, jstring patch) {
    // TODO: implement patch()
}

我们需要在 Java_com_example_bsdiff_BsPatchUtils_patch 这个方法中调用 bspatch.c 中的 main 方法来实现 patch 的逻辑;

因为 main 方法是主入口,所以我们需要调用这个 main 方法,但是 main 其实在 Java C++ 等都属于关键字,我们修改 bspatch.c 中的 main 方法为另一个名字,我们这里把它改成 executePatch;

然后,我们需要在 native-lib.cpp 中引入这个方法;

extern "C" {
    extern int executePatch(int argc, char *argv[]);
}

因为是 cpp 文件调用 c 文件中的方法,这里我们使用兼容模式来执行;接着我们来调用这个函数,以及传递相应的参数进去

extern "C" {
extern int executePatch(int argc, char *argv[]);
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_enjoy_dexdiff_BsPatchUtils_patch(JNIEnv *env, jclass clazz, jstring old_apk,
                                          jstring new_apk, jstring patch) {
    // bspatch oldfile newfile patchfile                                      
    int args = 4;
    char *argv[args];
    
    argv[0] = "bspatch";
    argv[1] = (char *) (env->GetStringUTFChars(old_apk, 0));
    argv[2] = (char *) (env->GetStringUTFChars(new_apk, 0));
    argv[3] = (char *) (env->GetStringUTFChars(patch, 0));

    //此处 executePatch() 就是上面我们修改出的
    int result = executePatch(args, argv);
    
    // 用完之后 release 释放
    env->ReleaseStringUTFChars(old_apk, argv[1]);
    env->ReleaseStringUTFChars(new_apk, argv[2]);
    env->ReleaseStringUTFChars(patch, argv[3]);
    
    __android_log_print(ANDROID_LOG_ERROR,"diff","==%s==%s==%s==%d",argv[1] ,argv[2] ,argv[3],result );
    
    return result;
}

然后 我们接下来在 MainActivity 中调用这个 patch 逻辑;

executePatch


调用非常简单,直接贴代码

fun patch(view: View?) {
    val newFile = File(getExternalFilesDir("apk"), "app.apk")
    val patchFile = File(getExternalFilesDir("apk"), "patch.apk")
    val result = BsPatchUtils.patch(applicationInfo.sourceDir, newFile.absolutePath, patchFile.absolutePath)
    if (result == 0) {
        install(newFile)
    }
}

private fun install(file: File) {
    val intent = Intent(Intent.ACTION_VIEW)
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // 7.0+以上版本
        val apkUri = FileProvider.getUriForFile(
            this,
            "$packageName.fileprovider", file
        )
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
        intent.setDataAndType(apkUri, "application/vnd.android.package-archive")
    } else {
        intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive")
    }
    startActivity(intent)
}

我们运行整个程序,先将 1.0 的代码安装到手机上,然后修改 1.0 的代码,build 出新的 apk,然后用旧的 apk 和新的 apk 产出 patch 包,然后将 patch 包 push 到指定的目录;

我们最终的运行结果:
在这里插入图片描述

Dex文件


dex 是 Android 系统的可执行文件,包含应用程序的全部操作指令以及运行时数据。将原来每个 class 文件都有的共有信息合成一体,这样减少了 class 的冗余;

查看 dex 的格式,我们可以借助 010Editor 来查看想要查看的文件格式;

image.png

dex 的生成,我们可以借助命令行来实现;首先需要将 .java 文件编译成 .class 文件,借助 javac xxx.java

javac xxx.java

然后使用 sdk 中提供的 dx 工具来将 class 文件编译成 dex 文件

dx --dex --output=xxx.dex xxx.class

然后就可以将生成的 dex 文件,在 010Editor 中打开查看其格式了;

一个完整的 dex 文件由这几部分构成

image.png

它是由文件头、索引区、数据区来构成;

文件头

文件头中有文件的魔数、文件的总长度、文件的校验码;而文件的格式(dex)就是由『魔数』来确定的;

image.png

文件头中的所有数据,可以看到第一个就是 magic(魔数)

image.png

可以看到魔数它是由 三个字节 + 一个字节的换行符 + 三个字节的版本号 + 0 来构成的,它是一个固定的数据,由 8 个字节组成,

dex 文件头除了第一个魔数,还有 checksum(校验码)、signature【20】(20字节的签名),unit file_size(unit 类型的文件长度)

索引区

索引区中有字符串索引、方法的定义索引、类中的成员属性、方法的索引

image.png

我们来看下字符串索引中具体是什么?

image.png

可以看到,这个 dex 中有 21 个字符串

image.png

每一个字符串都是 4 个字节,这 4 个字节代表一个索引,这个四个字节,在 Java 中就是一个 int,比如说,这个四个字节读取到的数据转换成 int 后是 100,这个 100 就是索引的意思,代表着你从整个文件的第 0 个字节开始读,读到第 100 个字节,从第 100 个字节开始,就是我们的字符串,也可以理解为一个偏移量;读取到偏移值后,再去根据偏移值定位到对应的位置,

Dex Header 解析示例

Header 解析,本质上就是文件IO操作,实现起来很简单,就是根据 010Editor 读取出来的格式读取不同的字节即可;

public class DexHeaderParser {

    public void readDexHeader() {
        try {
            FileInputStream fis = new FileInputStream("src/source/apk/temp/classes.dex");
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            byte[] buffer = new byte[4096];
            int len;
            while ((len = fis.read(buffer)) != -1) {
                bos.write(buffer, 0, len);
            }
            byte[] bytes = bos.toByteArray();
            ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
            byteBuffer.order(ByteOrder.LITTLE_ENDIAN);

            long magic = byteBuffer.getLong();
            long checksum = byteBuffer.getInt();
            byte[] signature = new byte[20];
            byteBuffer.get(signature);
            int fileSize = byteBuffer.getInt();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

其他的数据读取,照着格式写就可以;

DexDiff


DexDiff 是微信结合 Dex 文件格式设计的一个专门针对Dex的差分算法。根据 Dex 的文件格式,对两个 Dex 中每一项数据进行差分记录;

DexDiff 的差分算法更稳定,bsdiff 是基于二进制的,生成的产物不稳定,有时候大,有时候小,而 DexDiff 会比较稳定,它充分利用了 dex 的特点,它会对比两个 dex 中索引区、数据区的每一项数据的差分,并记录下来;

DexDiff 如何对比

这里以 string_ids 为例来进行对比差分,对照两个 dex 文件字符串数据(Dex中数据必须排序):oldDex 与 newDex

image.png

oldDex 中有 a b c 三个字符串,newDex 中有 b c e 三个字符串,用肉眼可以看出,oldDex 中的 a 需要标记为DELETE,newDex 中的 e 需要标记为 ADD,依靠这两个数据,结合 oldDex,把 oldDex 中的 a 删除,并把 e 添加到 oldDex 中;

具体的比较标记逻辑是:

oldDex 中 a 与 newDex 中的 b 对比,“a”.compareTo(“b”) < 0 : oldDex 中的 a 标记为:del,oldIndex++ 继续对比;

此时,newIndex = 0; oldIndex = 1; newCount = 3; oldCount=3;

然后 oldDex中 b 与newDex中的 b 对比,“b”.compareTo(“b”) == 0 ,不处理。oldIndex++,newIndex++;

此时,newIndex = 1; oldIndex = 2; newCount = 3; oldCount=3;

接着 oldDex 中 c 与 newDex 中的 c 对比,“c”.compareTo(“c”) == 0 ,不处理。oldIndex++,newIndex++;

到此,oldIndex = 3 = oldCount,newIndex = 2 < oldCount;

因此 newDex 剩余的 Item 全部记为:add;

然后将这些差分信息,写入到一个新的 dex 中,这就是生成的 patch 包;

好了,今天的讲解就到这里吧~~

下一章预告


AMS

欢迎三连


来都来了,点个关注,点个赞吧,你的支持是我最大的动力~~~

Android 增量更新 Android 增量更新使用笔记 阅读详情

相关推荐

android差异化更新(增量更新)

流程图: 待补充 解释: 1、把当前版本的versionName和versionCode发给服务器,服务器根据版本信息把后台的更新信息发给客户端; 2、判断是否有更新; 3、没有更新则结束,有更新则从服务器获取补丁包; 4、用补丁包和当前包合成新包; 5、验证新包签名是否成功; 6、验证成功则安装新包; 7、验证失败则提醒用户是否下载最新版本的整包; 8、直接下载整包的

乌龟赛跑 3103

Android增量更新方案

背景 咱们的App由于业务原因安装包尺寸日益加大,目前掌上炫舞已经170M、梦工厂也已经120M+,用户下载成本加大。 什么是增量更新? 增量更新的关键在于增量一词。平时我们的开发过程,往往都是今天在昨天的基础上修改一些代码,app的更新也是类似的,往往都是在旧版本的app上进行修改。这样看来,增量更新就是原有app的基础上只更新发生变化的地方,其余保持原样。 与之前每次更新都要下载完整apk包的做法相比,这样做的好处显而易见,每次变化的地方总是比较少的,因此更新包的体积就会小很多。...

cplasf2012的博客 3187

Android增量更新

什么是增量更新? 增量更新是指在进行更新操作时,只更新需要更改的地方,已经更新过的不会重复更新。 在实际情况中,应用的体积越来越大,在做一个小小的改动时就需要重新下载整个APP,这种方式即增加了服务器的压力,又浪费了用户的流量,因此每当我们发布新版本的时候,一些用户升级并不是很积极,这就造成了新版本的升级率并不高,而google为了解决这个问题,就提出了Smart App Update,也就是增量更新,也称差分升级。 增量更新的特点: 1、只更新需要更新的地方 2、相对于完全更新,速度会更快,更新

郭宝的博客 994

Android增量更新(简单易懂)

一、介绍 增量更新顾名思义就是在旧的apk基础上进行更新,那么我们需要如何操作呢,这里先介绍下bsdiff.exe工具,用其对新旧版本安装包做拆分,生成patch文件。在更新时只需下载这个文件即可,前提条件“旧版本apk必须存在”,如果获取不到源apk,那么就无法进行增量更新了。 bsdiff官网:http://www.daemonology.net/bsdiff/ BSDiff基本操作 1、对old文件中所有的子字符串形成一个字典 2、对比old文件和new文件,产生diffstring和ext

JerryQuery的博客 1722

003:大疆智图新手入门:从下载安装到首次建模实战

本文为大疆智图新手提供从下载安装到首次三维建模的完整实战指南。详细介绍了软件安装的硬件要求、试用许可申请流程,并指导用户如何准备航拍数据或使用开源样例进行首次建模。通过分步操作演示与核心参数设置讲解,帮助初学者快速掌握使用大疆智图进行实景三维重建的核心流程与常见问题解决方案。

t8u9v0的博客 701

Mamba实战:如何用Samba框架轻松搞定RGB-D显著性检测(附避坑指南)

本文提供了基于Samba框架进行RGB-D显著性目标检测的实战指南。详细解析了从环境配置、数据预处理到模型推理与可视化的全流程,并重点剖析了多模态数据融合中的常见陷阱与调试技巧,旨在帮助开发者高效部署这一基于Mamba的先进视觉模型,规避工程实践中的典型问题。

dapp9builder的博客 615

手把手带你实现Android增量更新

当我们开发完一个项目之后,上线维护 , 增加新功能 , 添加第三方库 , APK大小从4 - 5M , 增长到10+M , 用户由原来的几十秒下载 , 到现在几分钟以上的下载 , 网络情况不好的时候 , 或许就是十分钟不等。如果是多个文件都需要进行声明,显然一条条的声明太麻烦了,所以可以在属性中声明,所有的文件都适用。看这么多文件 , 还有一些乱七八糟的不知道什么的文件 , 那么我们只关注 , 我们想要的文件 , 将C/C++源文件以及.h头文件,找出来 ,放到一个干净的文件夹中。

老皮的博客 856

Linux -- 图形界面配置Samba;system-config-samba工具

图形界面配置Samba6.6.1 system-config-sambasystem-config-samba是Red Hat公司专门为Samba服务器管理编写的图形界面的管理工具,通过system-config-samba几乎可以完成对Samba服务器的所有配置,所以在只希望通过Samba服务器实现一些简单的资源共享时,system-config-samba是一个不错的选...

weixin_33695450的博客 2200

Android中的全量更新、增量更新以及热更新

在客户端开发过程中,我们可能会遇到这样一种需求:点击某个按钮弹出一个弹窗,提示我们可以更新到apk的某个版本,或者我们可以通过服务端接口进行强制更新。在这种需求中,我们是不需要通过应用商店来更新我们的apk的,而是直接在apk内部进行版本更新。这次我们就来看看实现这种应用内更新的几种方式。当然,这种玩法只能在国内玩,海外的话会被Googleplay据审的。如果是海外的应用要更新apk,只能在GooglePlay上上传新版本的包。

qq_36828822的博客 8723

EEGLAB安装、EDF数据导入、EEG预处理(Matlab)

目录下载和安装下载安装遇到问题及解决方案导入EDF数据EEG预处理 下载和安装 下载 链接:https://pan.baidu.com/s/17eMT6B7LgZ4PyqWvD5ixmg 提取码:12gz 安装 **第一步:**将下载下来的文件解压,修改名称为eeglab,将此文件夹存放在matlab的安装目录里的toolbox文件夹下,下图是我的路径,需要拷贝到自己相对应的路径中 第二步: 打开Matlab软件,点击主页中的设置路径 设置路径 点击添加并包含子文件夹,将eeglab文件夹添加进来,保

y18771025420的博客 1万+

EtherCAT丢包问题深度解析:从原理到实战排查指南

在工业自动化与运动控制领域,实时通讯网络的稳定性是系统可靠运行的基石。EtherCAT(以太网控制自动化技术)作为一种高性能工业以太网协议,其核心在于独特的“飞读飞写”机制和纳秒级的分布时钟同步,以实现多设备间的精准协同。然而,物理链路干扰、配置不当或硬件性能瓶颈都可能导致数据帧丢失,破坏时钟同步,进而引发伺服抖动、定位精度下降乃至生产线停机。本文聚焦于EtherCAT丢包这一典型故障,结合SSC(Slave Stack Code Tool)从站协议栈开发与实时系统调优等工程实践,系统阐述其成因、影响及一套

weixin_32050773的博客 341

Android实现App增量更新代码实现。

Android app 增量升级 客户端逻辑实现。

牛永志的专栏 884

基于STM32设计的物联网疫苗冷链物流监测系统

本设计基于物联网技术,开发了一套疫苗冷链物流环境监测系统。系统采用STM32F103C8T6作为主控芯片,集成SHT30温湿度传感器、震动传感器、GPS定位模块和4G通信模块,实现对运输环境的温度、湿度及震动状况的实时监测与控制。通过继电器驱动半导体制冷模块和加湿器,系统能够自动调节环境温湿度,保障疫苗运输的安全性。采集的环境数据通过MQTT协议上传至华为云物联网平台,配套开发的Android手机APP和Windows上位机软件基于Qt平台设计,支持远程实时查看环境数据和车辆定位,提升了疫苗冷链运输的智能化

862

手把手教你在Android中使用bsdiff实现文件增量更新 (超详细)

从Android端到服务端完整的增量更新技术方案

喻志强的博客 8218

基于Lumerical的1550 nm InGaAs/InP与InGaAs/InAlAs APD设计与功率饱和度优化

摘要 本研究利用Ansys Lumerical软件对1550 nm激光雷达应用的InGaAs/InP和InGaAs/InAlAs雪崩光电二极管(APD)进行仿真设计与优化。通过FDTD光学仿真获取光吸收分布,结合CHARGE模块的电学仿真分析暗电流、光电流及雪崩增益特性。重点研究了两种材料体系的性能差异,并针对近距离强反射导致的功率饱和问题提出结构优化方案。仿真结果表明,InGaAs/InAlAs APD具有更优的过量噪声特性,而优化的电荷层设计可显著提升器件的功率饱和阈值。研究提供了完整的仿真流程与代码实

huanghm88的专栏 95

20、超声多普勒成像技术全解析

本文全面解析了超声多普勒成像技术,涵盖彩色血流成像(CFI)、能量多普勒成像、超快多普勒成像和矢量多普勒成像的原理、流程、优势及临床应用场景。详细探讨了各成像模式的特点,如CFI的血流方向与速度可视化、能量多普勒对小血管的高灵敏度、超快多普勒的高帧率分析能力,以及矢量多普勒消除角度依赖性的创新方法。同时分析了技术面临的挑战及未来发展趋势,包括更高分辨率、人工智能辅助诊断和便携式设备的发展,为医学影像研究和临床诊断提供重要参考。

z2a3b4c5d的博客 277

Android增量更新(bsdiff使用)

简单了解 增量更新是什么? APP版本更新时不需要客户端下载新的apk,只需要下载差分包(两个apk的差异)。 差分包怎么得到?怎么用旧的apk和差分包合成新的apk? 使用bsdiff。 bsdiff是怎么工作的? 比较两个文件二进制,具体实现有兴趣可以去深挖。 举个例子方便理解:Git,就可以记录你每次修改、增加、删除,而且不是以文件为单位的,可具体到某个文件中的某一行,每次上传代码其实只上传的差异。 bsdiff咋用? 先下载bsdiff,解压看下都有什么

Momo_Da的博客 1293

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

Android 应用增量更新利器:IncrementallyUpdate

Android 应用增量更新利器:IncrementallyUpdate 项目介绍 IncrementallyUpdate 是一个专为 Android 平台设计的开源项目,它实现了应用程序的增量更新和升级功能。项目基于 BSDiff 差分算法,允许用户仅下载必要的更新部分,显著减少数据流量,提升用户体验。通过简单的 API 调用,开发者可以轻松地将这一特性集成到自己的应用中。 项目技术分析 Inc...

gitblog_00092的博客 492

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Android的增量更新,差分更新--服务器端&客户端

前言随着应用越来越大,应用更新耗时间和流量的问题,就显得格外突出.目前原生app的更新分为两种:重新下载源文件,还有一种就是差分包更新,也叫增量更新.在有些应用市场,例如google play,会对安装包进行拆分和合并,来达到差分更新的目的.首先解释一下差分包: 差分包是apk新版本和旧版本之间的包,可以称之为patch.应用流程:操作流程 确保客户端是old_app 改变app大小生成新的new

Chauncey的博客 6320

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

Android 增量升级全解析:从原理到实战的包体优化方案

摘要:增量升级技术通过传输版本差异数据而非完整安装包,有效解决移动应用升级中的流量消耗大、下载时间长等问题。本文系统阐述了Android增量升级的核心原理与实现方案,包括BSDiff差分算法、服务器端差分包生成、客户端合并技术等关键环节。通过实际案例展示了该技术如何将升级包体积缩减70-90%,显著提升用户升级意愿。同时探讨了签名验证、版本兼容等安全性问题,以及未来结合AAB动态交付的发展趋势。实践证明,增量升级对50MB以上应用可提升20%以上升级完成率,是优化用户体验的重要技术手段。

weixin_37794278的博客 1718

Android开发——增量更新实战总结

0. 前言增量更新的原理还是比较简单的,但是真的实现起来遇到的坑简直让人吐血,我是在windows下实现的,各种各样的坑,各种各样的错误,折腾了整整两天多才完成了,因此记录下来。先介绍下什么是增量更新和为什么我们需要增量更新,当我们发布新版本的时候,一些用户升级并不是很积极,反正我个人是不太喜欢更新手机上的APP,不知道大家是什么样的习惯...

关于Android开发的一些技术点总结 ╮( ̄▽ ̄”)╭ 2216

Android-增量更新

前言 在我们平常开发app时候,版本1.0迭代到2.0的时候目前还有不少开发得应用是全量更新,也就是重新下载整个2.0的安装包,重新安装到手机,其实是有些浪费资源,用户体验也不是很好,虽然现在是无限流量,一个app老是出现让用户更新全量包,极其影响用户体验,而增量更新可以解决2.0-1.0的差分包的大小,用户只需要在old.apk上更新new.apk的差分包,合成新的apk即可,更新数据包会大大...

缘聚缘随缘 391

Android增量更新----java版

开发过程中,随着apk包越来越大,全量更新会使得耗时,同时浪费流量,为了节省时间,使用增量更新解决。网上很多文章都不是很清楚,没有手把手教学,使得很多初学者,摸不着头脑,只知其然,而不知其所以然,甚至看了,也无从下手,为此,本文将一步步,带你实现增量更新,而没必要浪费太多的时间。

shi450561200的博客 858

Android 增量更新实现

如果app依赖本地更新,更新比较频繁的话,每次更新都要让用户下载完整安装包,用户体验会比较差。目前,很多应用商店都实现了apk的增量更新 正常apk更新逻辑: 打包V1.0版本,大小30M,用户安装完成 更新V1.1版本,大小31M,上传服务器得到远程链接,用户打开app收到更新提示,下载V1.1的apk,覆盖安装,更新完成 增量更新apk逻辑 打包V1.0版本,大小30M,用户安装完成 更新V1.1版本,大小31M,上传服务器得到远程链接 https://www.abc.com/apk/.

weixin_42602900的博客 1226

Android高级开发进阶之路4——增量更新(bsdiff,bspatch,bzip,ndk)

目录 增量更新 介绍 效果 步骤 总结 增量更新 最近换了个新手机,号称2019年的android机皇一加7plus。面对这90z的屏幕,原本不玩游戏的我也入了王者农药的坑!不过很少玩,所以基本上每次玩都要下载补丁更新才能玩。今天我们就来讲一下Android应用的增量更新。 介绍 优点: 节省用户更新新版本的流量、时间、内存空间。 基本流程如下: app...

u014736095的专栏 1180
上一篇: Jetpack Compose -> 状态机制的背后秘密
下一篇: 如何应对Android面试官 -> ActivityManagerService 是怎么启动的?
老A说
博客等级 码龄14年 1181粉丝 85原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值