1.使用环境

  • Android Studio版本(Bumblebee ):Android Studio Bumblebee | 2021.1.1 Patch 3
    Build #AI-211.7628.21.2111.8309675, built on March 17, 2022
    Runtime version: 11.0.11+9-b60-7590822 amd64
  • Gradle插件版本(3.6.3):classpath 'com.android.tools.build:gradle:3.6.3'
  • Gradle版本(5.6.4):distributionUrl=https://services.gradle.org/distributions/gradle-5.6.4-all.zip
  • Bugly Gradle插件版本(1.2.3):(时间2021-4-25)适配AGP4 , 更新tinker插件至1.9.14.16 (新版本不再发布到jcenter,已经迁移到了mavenCentral) classpath "com.tencent.bugly:tinker-support:1.2.3"
  • Bugly应用升级SDK版本(1.5.23):(时间2021-05-31)implementation 'com.tencent.bugly:crashreport_upgrade:1.5.23'
  • Tinker插件版本(1.9.14.16):implementation 'com.tencent.tinker:tinker-android-lib:1.9.14.16'

注意事项:Bugly Gradle插件版本、Bugly应用升级SDK版本、Tinker插件版本这个三个的版本不要轻易更换,要不然可能会出现很多意料不到的问题

2.Bugly热更新接入步骤

2.1 接入前准备

首先需要在Bugly创建一个应用,拿到AppID,代码集成时需要用到这个AppID,最后的补丁包发布也是在Bugly的应用升级下进行发布、撤回等管理操作。

Bugly的maven 仓库地址:https://repo1.maven.org/maven2/com/tencent/bugly/获取最新的版本

2.2 第一步:添加Gradle插件依赖

在工程根目录下的"build.gradle"文件中添加:
buildscript {repositories {jcenter()}dependencies {// tinkersupport插件(1.0.3以上无须再配置tinker插件)// tinkersupport插件, 其中lastest.release指拉取最新版本,也可以指定明确版本号,例如1.2.3classpath "com.tencent.bugly:tinker-support:1.2.3"}
}

2.3 第二步:集成SDK

gradle配置

在app module的“build.gradle”文件中添加(示例配置):

// 依赖插件脚本
apply from: 'tinker-support.gradle'android {defaultConfig {//开启拆分包multiDexEnabled truendk {//设置支持的SO库架构abiFilters 'armeabi' //, 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a'}}
}
dependencies {implementation "com.android.support:multidex:2.0.1" // 多dex配置//注释掉原有bugly的仓库//compile 'com.tencent.bugly:crashreport:latest.release'//其中latest.release指代最新版本号,也可以指定明确的版本号,例如1.3.4// 远程仓库集成方式(推荐)implementation 'com.tencent.bugly:crashreport_upgrade:1.5.23'//1. 指定tinker依赖版本(注:应用升级1.3.5版本起,不再内置tinker)//2.为了便于解答问题,这里的tinker版本建议跟随此处demo设置,如果微信更新了tinker版本,bugly会定期同步更新implementation 'com.tencent.tinker:tinker-android-lib:1.9.14.16'//NDK 下使用//implementation 'com.tencent.bugly:nativecrashreport:latest.release' //其中latest.release指代最新版本号,也可以指定明确的版本号,例如2.2.0
}

后续更新升级SDK时,只需变更配置脚本中的版本号即可。

注意: 升级SDK已经集成crash上报功能,已经集成Bugly的用户需要注释掉原来Bugly的jcenter库; 已经配置过符号表的Bugly用户保留原有符号表配置; Bugly SDK(2.1.5及以上版本)已经将Java Crash和Native Crash捕获功能分开,如果想使用NDK库,需要配置: compile ‘com.tencent.bugly:nativecrashreport:latest.release’

在app module的“build.gradle”文件中添加:

// 依赖插件脚本
apply from: 'tinker-support.gradle'

tinker-support.gradle内容如下所示(示例配置):

注:您需要在同级目录下创建tinker-support.gradle这个文件哦。

apply plugin: 'com.tencent.bugly.tinker-support'/*** 配置打包生成的apk的路径,定位在APP模块下的build/bakApk目录下* 形式如.../main/app/bakApk*/
def bakPath = file("${buildDir}/bakApk/")/*** 此处填写每次构建生成的基准包目录* 每次打包都会在上面的bakApk目录下生成一个以当前打包时间命名的文件件,文件件中存放着本次打包生成的apk,mapping.txt,R.txt.*/
def baseApkDir = "app-0525-10-40-03"/*** 对于插件各参数的详细解析请参考*/
tinkerSupport {// 开启tinker-support插件,默认值trueenable = true// 指定归档目录,默认值当前module的子目录tinkerautoBackupApkDir = "${bakPath}"/*** 是否启用覆盖tinkerPatch配置功能,默认值false* 开启后tinkerPatch配置不生效,即无需添加tinkerPatch* tinkerSupport是bugly提供的配置属性* 如果为true ,下面tinkerPatch中配置的属性不会生效,推荐这种。tinker的配置太繁琐复杂*/overrideTinkerPatchConfiguration = true/*** 编译补丁包时,必需指定基线版本的apk,默认值为空* 如果为空,则表示不是进行补丁包的编译* 编译补丁包时,必需指定基线版本的apk,也就是针对哪个线上版本生成patch* @{link tinkerPatch.oldApk }*///    baseApk = "${bakPath}/${baseApkDir}/app-release.apk"baseApk = "${bakPath}/${baseApkDir}/app-debug.apk"// 对应tinker插件applyMapping 混淆规则
//    baseApkProguardMapping = "${bakPath}/${baseApkDir}/app-release-mapping.txt"baseApkProguardMapping = "${bakPath}/${baseApkDir}/app-debug-mapping.txt"// 对应tinker插件applyResourceMapping 资源id映射
//    baseApkResourceMapping = "${bakPath}/${baseApkDir}/app-release-R.txt"baseApkResourceMapping = "${bakPath}/${baseApkDir}/app-debug-R.txt"//建议设置true,用户就不用再自己管理tinkerId的命名,插件会为每一次构建的base包自动生成唯一的tinkerId,默认命名规则是versionname.versioncode_时间戳//具体参考https://github.com/BuglyDevTeam/Bugly-Android-Demo/wiki/Tinker-ID%E8%AF%A5%E6%80%8E%E4%B9%88%E8%AE%BE%E7%BD%AE
//    autoGenerateTinkerId = true//tinkerId必须保证唯一性,如果两个base包的tinkerid是一样的,并且都联网激活了,那么后续补丁上传到后台的时候会出现匹配错误tinkerId = "base-3.0"// 构建多渠道补丁时使用// buildAllFlavorsDir = "${bakPath}/${baseApkDir}"// 是否启用加固模式,默认为false.(tinker-spport 1.0.7起支持)// isProtectedApp = true// 是否开启反射Application模式enableProxyApplication = true// 是否支持新增非export的Activity(注意:设置为true才能修改AndroidManifest文件)supportHotplugComponent = true}/*** 一般来说,我们无需对下面的参数做任何的修改* 对于各参数的详细介绍请参考:* https://github.com/Tencent/tinker/wiki/Tinker-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97*/
tinkerPatch {//oldApk ="${bakPath}/${appName}/app-release.apk"ignoreWarning = trueuseSign = truedex {dexMode = "jar"pattern = ["classes*.dex"]loader = []}lib {pattern = ["lib/*/*.so"]}res {pattern = ["res/*", "r/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]ignoreChange = []largeModSize = 100}packageConfig {}sevenZip {zipArtifact = "com.tencent.mm:SevenZip:1.1.10"
//        path = "/usr/local/bin/7za"}buildConfig {keepDexApply = false//tinkerId = "1.0.1-base"//applyMapping = "${bakPath}/${appName}/app-release-mapping.txt" //  可选,设置mapping文件,建议保持旧apk的proguard混淆方式//applyResourceMapping = "${bakPath}/${appName}/app-release-R.txt" // 可选,设置R.txt文件,通过旧apk文件保持ResId的分配}
}

更详细的配置项参考tinker-support配置说明

注意事项:

  • “tinker-support.gradle”文件要与app Module下“build.gradle”文件保持在同一目录下
  • tinkerSupport 配置中baseApk 、baseApkProguardMapping 、baseApkResourceMapping 3个字段所使用的apk名称要与app Module下“build.gradle”文件设置的apk打包名称一致,否则使用Tinker打补丁包时报找不到apk的错误

2.4 第三步:初始化SDK

enableProxyApplication = false 的情况

这是Tinker推荐的接入方式,一定程度上会增加接入成本,但具有更好的兼容性。

集成Bugly升级SDK之后,我们需要按照以下方式自定义ApplicationLike来实现Application的代码(以下是示例):

自定义Application

public class SampleApplication extends TinkerApplication {public SampleApplication() {super(ShareConstants.TINKER_ENABLE_ALL, "xxx.xxx.SampleApplicationLike","com.tencent.tinker.loader.TinkerLoader", false);}
}

注意:这个类集成TinkerApplication类,这里面不做任何操作,所有Application的代码都会放到ApplicationLike继承类当中
参数解析
参数1:tinkerFlags 表示Tinker支持的类型 dex only、library only or all suuport,default: TINKER_ENABLE_ALL
参数2:delegateClassName Application代理类 这里填写你自定义的ApplicationLike
参数3:loaderClassName Tinker的加载器,使用默认即可
参数4:tinkerLoadVerifyFlag 加载dex或者lib是否验证md5,默认为false

我们需要您将以前的Applicaton配置为继承TinkerApplication的类:

自定义ApplicationLike

public class SampleApplicationLike extends DefaultApplicationLike {public static final String TAG = "Tinker.SampleApplicationLike";public SampleApplicationLike(Application application, int tinkerFlags,boolean tinkerLoadVerifyFlag, long applicationStartElapsedTime,long applicationStartMillisTime, Intent tinkerResultIntent) {super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent);}@Overridepublic void onCreate() {super.onCreate();// 这里实现SDK初始化,appId替换成你的在Bugly平台申请的appId// 调试时,将第三个参数改为trueBugly.init(getApplication(), "900029763", false);}@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)@Overridepublic void onBaseContextAttached(Context base) {super.onBaseContextAttached(base);// you must install multiDex whatever tinker is installed!MultiDex.install(base);// 安装tinker// TinkerManager.installTinker(this); 替换成下面Bugly提供的方法Beta.installTinker(this);}@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)public void registerActivityLifecycleCallback(Application.ActivityLifecycleCallbacks callbacks) {getApplication().registerActivityLifecycleCallbacks(callbacks);}}

注意:tinker需要你开启MultiDex,你需要在dependencies中进行配置compile "com.android.support:multidex:1.0.1"才可以使用MultiDex.install方法; SampleApplicationLike这个类是Application的代理类,以前所有在Application的实现必须要全部拷贝到这里,在onCreate方法调用SDK的初始化方法,在onBaseContextAttached中调用Beta.installTinker(this);

enableProxyApplication = true 的情况(这是我目前使用的方式)


public class MyApplication extends Application {@Overridepublic void onCreate() {super.onCreate();// 这里实现SDK初始化,appId替换成你的在Bugly平台申请的appId// 调试时,将第三个参数改为trueBugly.init(this, "900029763", false);}@Overrideprotected void attachBaseContext(Context base) {super.attachBaseContext(base);// you must install multiDex whatever tinker is installed!MultiDex.install(base);// 安装tinkerBeta.installTinker();}}

注:无须你改造Application,主要是为了降低接入成本,我们插件会动态替换AndroidMinifest文件中的Application为我们定义好用于反射真实Application的类(需要您接入SDK 1.2.2版本插件版本 1.0.3以上)。

2.5 第四步:AndroidManifest.xml配置

在AndroidMainfest.xml中进行以下配置:

1. 权限配置

<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_LOGS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

2. Activity配置

<activityandroid:name="com.tencent.bugly.beta.ui.BetaActivity"android:configChanges="keyboardHidden|orientation|screenSize|locale"android:theme="@android:style/Theme.Translucent" />

3. 配置FileProvider

注意:如果您想兼容Android N或者以上的设备,必须要在AndroidManifest.xml文件中配置FileProvider来访问共享路径的文件。

<providerandroid:name="android.support.v4.content.FileProvider"android:authorities="${applicationId}.fileProvider"android:exported="false"android:grantUriPermissions="true"><meta-dataandroid:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/provider_paths"/>
</provider>

如果你使用的第三方库也配置了同样的FileProvider, 可以通过继承FileProvider类来解决合并冲突的问题,示例如下:

<providerandroid:name=".utils.BuglyFileProvider"android:authorities="${applicationId}.fileProvider"android:exported="false"android:grantUriPermissions="true"tools:replace="name,authorities,exported,grantUriPermissions"><meta-dataandroid:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/provider_paths"tools:replace="name,resource"/>
</provider>

这里要注意一下,FileProvider类是在support-v4包中的,检查你的工程是否引入该类库。

在res目录新建xml文件夹,创建provider_paths.xml文件如下:


<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android"><!-- /storage/emulated/0/Download/${applicationId}/.beta/apk--><external-path name="beta_external_path" path="Download/"/><!--/storage/emulated/0/Android/data/${applicationId}/files/apk/--><external-path name="beta_external_files_path" path="Android/data/"/>
</paths>

这里配置的两个外部存储路径是升级SDK下载的文件可能存在的路径,一定要按照上面格式配置,不然可能会出现错误。

注:1.3.1及以上版本,可以不用进行以上配置,aar已经在AndroidManifest配置了,并且包含了对应的资源文件。

2.6 第五步:混淆配置

为了避免混淆SDK,在Proguard混淆文件中增加以下配置:

-dontwarn com.tencent.bugly.**
-keep public class com.tencent.bugly.**{*;}
# tinker混淆规则
-dontwarn com.tencent.tinker.**
-keep class com.tencent.tinker.** { *; }

如果你使用了support-v4包,你还需要配置以下混淆规则:

-keep class android.support.**{*;}

3.热更新使用流程

3.1 完整流程

  • 打基准包安装并上报联网(注:填写唯一的tinkerId)
  • 对基准包的bug修复(可以是Java代码变更,资源的变更)
  • 修改基准包路径、修改补丁包tinkerId、mapping文件路径(如果开启了混淆需要配置)、resId文件路径
  • 执行buildTinkerPatchRelease打Release版本补丁包
  • 选择app/build/outputs/patch目录下的补丁包并上传(注: 不要选择tinkerPatch目录下的补丁包,不然上传会有问题
  • 编辑下发补丁规则,点击立即下发
  • 杀死进程并重启基准包,请求补丁策略(SDK会自动下载补丁并合成)
  • 再次重启基准包,检验补丁应用结果
  • 查看页面,查看激活数据的变化

3.2 普通打包过程

1.编译基线包

配置基准包的tinkerId,关于tinkerId有配置有两种方式:

  • autoGenerateTinkerId = true根据时间戳自动生成tinkerId,无需手动配置tinkerId = "base-3.0",手动配置的tinkerId不生效
  • autoGenerateTinkerId = false或者不设置此参数(默认就是false),tinkerId以手动配置的为准tinkerId = "base-3.0"

    tinkerId最好是一个唯一标识,例如git版本号、versionName等等。 如果你要测试热更新,你需要对基线版本进行联网上报

这里强调一下,基线版本配置一个唯一的tinkerId,而这个基线版本能够应用补丁的前提是集成过热更新SDK,并启动上报过联网,这样我们后台会将这个tinkerId对应到一个目标版本,例如tinkerId = “bugly_1.0.0” 对应了一个目标版本是1.0.0,基于这个版本打的补丁包就能匹配到目标版本。

执行assemble编译生成基准包:

这个会在build/outputs/bakApk路径下生成每次编译的基准包、混淆配置文件、资源Id文件,如下图所示:

实际应用中,请注意保存线上发布版本的基准apk包、mapping文件、R.txt文件,如果线上版本有bug,就可以借助我们tinker-support插件进行补丁包的生成

启动apk,上报联网数据

我们每次冷启动都会请求补丁策略,会上报当前版本号和tinkerId,这样我们后台就能将这个唯一的tinkerId对应到一个版本,大家测试的时候可以打开logcat查看我们的日志,如下图所示:

//LogCat日志输出如下内容
CrashReport: app version is: [3.2.10.9.20220425], [deviceId:05bd849ce00f76f2], channel: [null], buildNo: [0], base tinkerId:[base-3.0], patch tinkerId:[], patch version:[], strategyType:[0]

如果看不到log,您需要将bugly初始化的第三个参数设置为true才能看到,即 Bugly.init(appContext,"ef857d31b6",true)

只有看到以上这些信息才能说明你生成的这个基线包才是正常可用的,否则这个基线包是不可用的。

2、对基线版本的bug修复

支持代码方法的增、删、改问题修复、资源修复、so库修复;测试的话任意改下界面显示或者代码都行,然后开始打补丁包

3、根据基线版本生成补丁包

修改待修复apk路径、mapping文件路径、resId文件路径



补丁包的tinkerId要与基线包的tinkerId不一样才行

执行构建补丁包的task

如果你要生成不同编译环境的补丁包,只需要执行TinkerSupport插件生成的task,比如buildTinkerPatchRelease就能生成release编译环境的补丁包。 注:TinkerSupport插件版本低于1.0.4的,需要使用tinkerPatchRelease来生成补丁包 。

生成的补丁包在build/outputs/patch目录下:

检查补丁包是否和基线包适配

4、上传补丁包到平台

上传补丁包到平台并下发编辑规则


点击立即下发,上传前面生成的patch包,我们平台会自动为你匹配到目标版本,你可以选择下发范围(开发设备、全量设备、自定义),填写完备注之后,点击立即下发让补丁生效,这样你就可以在客户端当中收到我们的策略,SDK会自动帮你把补丁包下到本地。

5、测试补丁应用效果

启动app应用patch

如果匹配到目标版本,后台就会下发补丁策略,可以在logcat看到如下日志:

下载成功之后,我们会立即去合成补丁,可以看到patch合成的日志:

重启app查看效果

4.遇到的问题及解决方式

1. 编译是报错:AAPT: E:\AndroidProject\OfficeProject\Hualan\histudenthomework_v3\app\build\intermediates\tinker_intermediates\public.txt: error: failed reading stable ID file.

新版本Tinker引入的问题,可以参考github上Tinker的issue解释:https://github.com/Tencent/tinker/issues/1551

2.1解决方式

在APP Module的build.gradle文件中增加如下内容

android {//.......其他配置项applicationVariants.all { variant ->tasks.all {if ("process${taskName.capitalize()}Resources".equalsIgnoreCase(it.name)){it.doFirst {createFile(path)}}}}
}static def createFile(path) {File file = new File(path)if (!file.exists()) {file.createNewFile()}
}

2.编译补丁包错误:Execution failed for task ‘:app:tinkerProcessReleaseResourceId’.

解决方式:https://github.com/Tencent/tinker/issues/961

3. 编译报错:Cannot constrain type: LONG for value: v51 by constraint: OBJECT

Execution failed for task ‘:app:minifyReleaseWithR8’.

com.android.tools.r8.CompilationFailedException: Compilation failed to complete

主要是引入了AspectJ

一个基于AspectJ并在此基础上扩展出来可应用于Android开发平台的AOP框架,可作用于java源码,class文件及jar包,同时支持kotlin的应用。

解决方式:exclude ‘tinker’

4.下载补丁包成功后报错(isSuccess:false表示合成补丁包失败)

2022-05-24 20:42:39.290 29639-29918/com.ehualan.histudent V/Tinker.TinkerResultService: TinkerResultService receive result: PatchResult: isSuccess:falserawPatchFilePath:/data/user/0/com.ehualan.histudent/app_tmpPatch/tmpPatch.apkcostTime:172dexoptTriggerTime:0isOatGenerated:false
2022-05-24 20:42:39.292 29639-29918/com.ehualan.histudent I/Process: Sending signal. PID: 29829 SIG: 9
2022-05-24 20:42:39.295 29639-29639/com.ehualan.histudent I/CrashReport: Tinker patch failure, result: PatchResult: isSuccess:falserawPatchFilePath:/data/user/0/com.ehualan.histudent/app_tmpPatch/tmpPatch.apkcostTime:172dexoptTriggerTime:0isOatGenerated:false

原因:补丁包和基线包TinkerId不一致造成的问题

解决办法:检查下补丁包和基线包版本一致性

Bugly+Tinker实现热修复相关推荐

  1. android热修复技术tinker,Android热修复方案第一弹——Tinker篇

    背景 一款App的正常开发流程应该是这样的:新版本上线-->用户安装-->发现Bug-->紧急修复-->重新发布新版本-->提示用户安装更新,从表面上看这样的开发流程顺理 ...

  2. 基于Tinker的热修复

    Tinker是腾讯推出的一种热修方案,下面介绍一下Tinker的接入步骤. 一.在工程的 build.gradle添加远程仓库依赖 jcenter buildscript {repositories ...

  3. Android 浅尝Tinker微信热修复

    现在热修复已经很热门了,比较著名的有阿里巴巴的AndFix.Dexposed,腾讯QQ空间的超级补丁和微信最近开源的Tinker. Tinker是一个android的热修复库,在不重新安装apk的情况 ...

  4. Android热修复-Tinker简析

    一.简介 日常工作工作中难免会遇到项目上线后出现bug问题,如果紧急发版往往由于渠道审核时间问题,导致bug修复不及时,影响用户体验.这时我们需要引入热修复,免去发版审核烦恼. 热更新优势: 让应用能 ...

  5. 腾讯Tinker 热修复 Andriod studio 3.0 配置和集成(一)

    本文说明 面试的时候经常问我有没有用过热修复?用谁的?能说下原理吗?当时我回答得不好,毕竟以前的项目都没有用,又不敢装逼,mmp,但是基本流程还是知道的,所以我们来初探下Tinker 这个热修复,如果 ...

  6. 【Android 热修复】热修复原理 ( 热修复框架简介 | 将 Java 字节码文件打包到 Dex 文件 )

    文章目录 一. 热修复框架简介 1.类替换 2.so 替换 3.资源替换 4.全平台支持 5.生效时间 6.性能损耗 7.总结 二. 将 Java 字节码文件打包到 Dex 文件 一. 热修复框架简介 ...

  7. Android热修复Tinker接入文档

    一.接入 ​ 按照官方文档https://bugly.qq.com/docs/user-guide/instruction-manual-android-hotfix/?v=2017052610012 ...

  8. 热修复——Tinker的集成与使用

    一.简述 Tinker是微信官方的Android热补丁解决方案,它支持动态下发代码.So库以及资源,让应用能够在不需要重新安装的情况下实现更新.当然,你也可以使用Tinker来更新你的插件. 上面是T ...

  9. 热修复——Bugly让热修复变得如此简单

    一.简述 在上一篇<热修复--Tinker的集成与使用>中,根据Tinker官方Wiki集成了Tinker,但那仅仅只是本地集成,有一个重要的问题没有解决,那就是补丁从服务器下发到用户手机 ...

最新文章

  1. 如何根据CSD寄存器计算SD卡容量(csd v1.0 csd v2.0)
  2. android最新知识点总结,学习android之 知识点总结
  3. oracle 查看库表状态
  4. 在Ubuntu下进行MongoDB安装步骤
  5. JDK 8.0 新特性——函数式接口和Lambda 表达式
  6. 【mysql】知识点
  7. qtouch跨平台组态软件
  8. python整人小代码_整人代码大全
  9. 关系型数据库的一对一、一对多、多对一、多对多关系
  10. 2018年10月Top 10机器学习开源项目
  11. 百度云下载文件方法:PC 和 群晖 Docker
  12. 【jquery】jquery $.fn $.fx是什么意思
  13. 曲阜水利学校计算机96级聚会,曲阜水利学校50年校庆
  14. stm32 mbed 入门教程(一)---前期准备
  15. 【Matlab】系统的响应分析
  16. Hive3入门至精通(基础、部署、理论、SQL、函数、运算以及性能优化)15-28章
  17. DAO跨事物调用---转账
  18. C++指针与const的结合用法
  19. 从上家公司离开一个多月,今天前同事竟跟我说,公司要求他们删除离职人员微信好友,不删就开除!...
  20. 谁偷窥了你的隐私?穿透无痕模式,浏览器指纹正在泄露你的秘密。

热门文章

  1. 在哪里尝试爆火的AI绘画?16款AI绘画工具大盘点!
  2. 嵌入式工程师题库(适用于各大名厂笔试、软考、计算机等级考试)
  3. ElasticSearch保姆级入门教程
  4. [翻译] [LaTeX] Misplaced \noalign
  5. carsim与simulink联合仿真模型 车道保持,轨迹跟随,横向控制
  6. openGL之API学习(一三九)求垂直向量
  7. OGG|利用 OGG 迁移 Oracle11g 到 19C
  8. Photoshop实例教程:打造Apple光芒…
  9. 体验全功能:Win 8激活方式详解
  10. 【UEFI基础】Shell下的命令(一)