安卓打造安装包(应用打包、规范处理安装包、安全加固)
本章介绍应用安装包的基本制作规范,主要包括:如何导出既美观又精简的APK文件、如何按照上线规范调整App的相关设置、如何对APK文件进行安全加固以防止安装包被破解。
应用打包
本节介绍APK安装包的打包过程,包括:如何利用Android Studio导出APK格式的安装包、如何利用Android Studio制作App的个性化图标、如何通过个中国瘦身手段压缩APK文件的大小。
导出APK安装包
开发过程中运行App,都是先由数据线连接手机和计算机,再通过Android Studio的Run菜单把App安装到手机上。这种方式只能在自己手机上调试应用,如果想在别人手机上安装应用,就得把App打包成APK文件(该文件就是App的安装包),然后把APK传给其他人安装。
下面是使用Android Studio打包APK的具体步骤说明:
-
依次选择菜单Build->Generate Signed Bundle/APK…,弹出对话框如下图所示。
-
选中该窗口左下方的APK选项,再单击Next按钮,进入APK签名对话框,如下图所示。
-
在该窗口选择待打包的模块名(如chapter10),以及密钥文件的路径,如果原来有密钥文件,就单击Choose existing…按钮,在弹出的文件对话框选择密钥文件。如果首次打包没有密钥文件,就单击Create new…按钮,弹出密钥创建对话框,如下图所示。
-
单击该Key store path:对话框最右侧的文件夹按钮,弹出如下图的文件对话框,在此可选择密钥文件的保存路径。
-
在文件对话框中选择文件保存路径,并在下方的File name输入框中填写密钥文件的名字,然后单击OK按钮回到密钥创建对话框。在该对话框依次填写密码(Password)、确认密码(Confirm)、别名(Alias)、别名密码(Password)、别名的确认密码(Confirm),修改密钥文件的有效期限(Validity)。对话框下半部分的输入框只有姓名(First and Last Name)是必填的,填完后的对话框如下图。
-
单击OK按钮回到APK签名对话框,此时Android Studio自动把密码和别名都填上了,如下图所示。如果一开始选择已存在的密钥文件,这里就要手工输入密码和别名。
-
单击Next按钮进入下一个打包对话框,如下图所示。对话框上方可选择APK文件的保存路径,对话框中部可选择编译变量(Build Variants),如果是调试用,则编译变量选择release。最后单击Create按钮,等待Android Studio生成AKP安装包。
若无编译问题,片刻之后会在APK保存路径下看到release目录,打开该目录找到名为app-release.apk的安装包文件。把该文件通过QQ或微信传给他人,对方手机收到APK文件,点击APK即可安装应用。
如果APK文件安装失败,则可能是以下原因导致的:
- App只能升级不能降级,假如安装包的版本号小于已安装App的版本号,就无法正常安装。版本号在build.gradle.kts中的versionCode节点配置。
- 倘若新、旧App的签名不一致,也会造成安装失败。比如该手机之前安装了debug类型的App,现在又要安装release类型的版本,就会出现签名冲突。
制作App图标
新建一个App工程,默认的应用图标都是机器人,如果要发布正规的App,肯定得更换更醒目得专享图标。可是res目录下又好几种分辨率得mipmap-***目录,每种分辨率又有圆角矩形和圆形两类图标,加起来要做十几个图标,倘若每个图标都手工制作,是在要累得够呛。辛好Android Studio早早提供了专门得图标制作插件,只要简简单单几个步骤,即可自动生成所有规格的应用图标。该插件的具体使用步骤如下。
-
右击项目结构的模块名称,在右键菜单中依次选择菜单New->ImageAsset,弹出如下图所示的图标制作对话框。
-
上图所示的对话框左侧是图标的配置选项,右侧是各规格图标的展示效果。在对话框左侧中间找到Path区域,单击路径输入框右边的文件夹图标,在弹出的文件窗口中选择新图标的素材图片,再回到图标制作窗口,此时该对话框的界面如下图所示。
-
由上图可见,对话框右侧展示区域一下子全部换成了新的图标,完全自动加工好了。接着单击窗口下方的Next按钮,跳转到如下图所示的下一页对话框。
-
单击下一页窗口中的Finished按钮,借宿图标制作操作,然后再mipmap-***目录下就能看到各种规格的新图标了。
给APK瘦身
App不但要求功能完善,其他方面也得综合考虑,比如APK安装包的文件大小就是很重要的参考因素。具备同样功能的两个安装包,一个很大很占空间,另一个较小不怎么占空间,用户的选择结果自然不言而喻。如何压缩打包后的APK文件大小,也就是所谓的给APK瘦身,这涉及很多技术手段,最常用的主要有3种:去冗余功能、精简无用资源、压缩图片大小。分别介绍如下:
1.去除冗余功能
每当开发者创建新的Android项目,打开模块的AndroidManifest.xml,看到默认的application节点是下面这样的:
<applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/Theme.Chapter10">
注意application节点有两个属性allowBackup和supportsRtl,且都被设置为true,它俩到底是干什么用的呢?
首先看allowBackup,该属性若设为true,则允许用户备份APK安装包和应用数据,以便在刷机或者数据丢失后恢复应用。这里其实隐含着高危漏洞,因为备份后的应用数据可能被人复制到其他设备,如此一来用户的隐私就会泄露出去,账号密码、聊天记录等均可遭窃。所以还是赶紧关闭这个鸡肋功能吧,把allowBackup属性值由默认的true改为false。
然后看supportsRtl,该属性名称当中的Rtl为“Right-to-Left”(从右到左)的缩写,像中东的阿拉伯语、希伯来文等都是从右到左书写,supportsRtl属性值为true时表示支持这种从右向左的文字系统。可是常用的中文、英文都是从左往右书写,根本用不着从右到左的倒排共嗯那个,因此若无特殊情况可把supportsRtl属性值由默认的true改为false。
关闭备份与倒排功能之后,application节点变成了下面这样:
<applicationandroid:allowBackup="false"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="false"android:theme="@style/Theme.Chapter10">
2.精简无用资源
同样打开新项目种模块级别的build.gradle.kts,发现buildTypes节点下面是这样的:
buildTypes {release {isMinifyEnabled = falseproguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"),"proguard-rules.pro")}
}
可见有个isMinifyEnabled 属性,默认值false,该属性的字面意思为是否启用最小化,如果将它设置为true,则Android Studio在打包APK时会惊醒以下代码处理:
- 压缩代码,移除各种无用的实体,包括类、接口、方法、属性、临时变量等。
- 混淆代码,把类名、属性名、方法名、实例名、变量名替换为简短且无意义的名称,例如Student类的名称可能改为a,方法getName的名称可能改为b等。
App的Java代码经过压缩和混淆之后,打包生成的APK文件会随着变小。除了代码之外,应用项目还包括各种资源文件,若想移除无用的资源文件(包括XML布局和图片),就要引入新属性shrinkResources
,并将该属性值设置为true,这样Android Studio在打包APK时会自动移除无用的资源文件。同时开启代码压缩和资源压缩的buildTypes节点示例如下:
buildTypes {release {isMinifyEnabled = trueproguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"),"proguard-rules.pro")}
}
3.压缩图片大小
由于手机屏幕的尺寸有限,原始的高清图片与有损压缩后的图片在视觉上没有太大差别,因此适当压缩图片质量也是减少APK体积的一个重要途径。App传统的资源图片主要有JPG和PNG两种格式,
对于JPG图片来说,利用Photoshop打开图片。然后将图片另存,即可选择一个合适的压缩率保存即可。
对于PNG图片,利用Photoshop打开图片。然后将图片另存,选择“存储为Web所用格式”,在打开的对话框中的右上角选择保存格式为“PNG-8”,然后保存文件即可完成图片压缩。
规范处理
本节介绍App上线前必做的准备工作,包括:如何正确设置App的版本编号和版本名称、如何把App从调试模式切换到发布模式、如何给多个渠道同时打包APK文件。
版本设置
每个App都有3个基础信息:第一个App的图标,图标文件为res/mipmap-***目录下的ic_launcher.png;第二个是App的名称,名称文字保存在res/values/strings.xml的app_name当中;第三个是App的版本号,版本信息包括build.gradle.kts的versionCode与versionName两个参数,其中versionCode为纯数字的版本编号,versionCode为带点号的字符串,格式如“数字.数字.数字”。
App图标和App名称都好理解,在手机桌面上也能看到App的图标和名称,那么为什么App还需要版本编号与版本名称这样的版本信息呢?这是因为App需要经常升级,但不允许App降级,也就是说,一旦安装了某个版本的App,那么之后只能安装版本更新的同名App,不能安装版本更旧的同名App。这种只能升级不能降级的判断,就依赖于每个APK设定的版本号versionCode,versionCode的数值越大,表示该安装包的版本越高;versionCode的数值越小,表示该安装包的版本越低。依据当前App的版本号与待安装APK的版本号,系统方能比较得知是否允许升级App。
至于版本名称versionName,则用来标识每次App升级的改动程度,按照通常的版本名称格式“数字.数字.数字”,第一个数字为大版本号,每当有页面改版或代码重构等重大升级时,大版本号要加1,后面两个数字清零;第二个数字为中版本号,每当要更新局部页面或添加新功能时,中版本号要加1,第三个数字清零;第三个数字为小版本号,每当界面有微调或问题修复时,小版本号加1。
每次App升级重新导出APK的时候,versionCode与versionName都要一起更改,不能只改其中一个。并且升级后的versionCode与versionName只能比原来的大,不能原来小。如果没有按照规范修改版本号,就产生以下问题:
- 版本号比已安装的版本号小,在安装时系统直接提示失败,因为App只能做升级操作,不能做降级操作。
- 在升级系统应用(手机厂商内置的应用,非普通应用)时,如果只修改versionName,没修改versionCode,重启手机后会发现更新丢失,该应用被还原到升级前的版本。这是因为:对于系统应用,Android会检查versionCode的数值,如果versionCode不大于当前已安装的版本号,本次更新就被忽略了。
除了系统要求检查应用的基础信息,App又是也需要获取自身信息,比如应用图标可以从资源图片获取,应用名称可调用getString方法来获取。其他像应用包名、应用版本等信息,可以编译配置工具defaultConfig获取,该类提供了几个配置属性说明如下:
- applicationId:应用包名。
- buildTypes:编译类型。为debug表示这是调试包,为release表示这是发布包。
- versionCode:应用的版本编号。
- versionName:应用的版本名称。
下面是获取App基础信息的代码例子:
public class AppVersionActivity extends AppCompatActivity {private static final String TAG = "AppVersionActivity";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_app_version);ImageView iv_icon = findViewById(R.id.iv_icon);iv_icon.setImageResource(R.mipmap.ic_launcher); // 应用图标取自ic_launcherTextView tv_desc = findViewById(R.id.tv_desc);try {PackageManager packageManager = getPackageManager();PackageInfo packageInfo = packageManager.getPackageInfo(getPackageName(), 0);// 应用名称取自app_name,应用包名、版本号、版本名称均来自BuildConfigString desc = String.format("App名称为:%s\nApp包名为:%s\n" +"App版本号为:%d\nApp版本名称为:%s",getString(R.string.app_name), packageInfo.packageName,packageInfo.getLongVersionCode(), packageInfo.versionName);tv_desc.setText(desc);} catch (PackageManager.NameNotFoundException e) {e.printStackTrace();Log.d(TAG, "exception:" + e.getMessage());}}
}
运行App,看到App版本信息的获取页面如下图所示,可见分别展示了App的图标、名称、包名,以及版本编号和版本名称。
发布模式
为了编码调试方便,开发者经常在代码里添加日志,还在页面上弹出各种提示。这样固然有利于发现bug、提高软件质量,不过调试信息过多往往容易泄露敏感信息,例如用户的账号密码、业务流程的逻辑等。从保密角度考虑,App在上线必须去掉多余的调试信息,也就是生成发布模式的安装包,与之相对的时开发阶段的调试模式。
建立发布模式拥有下列两点优势:
- 保护用户的铭感账户信息不被泄露。
- 保护业务逻辑与流程处理的交互数据不被泄露。
发布模式与调试模式的安装包很区分,通过菜单Generate Signed Bundle/APK…导出安装包的打包对话框,在Build Variants一栏即可选择安装包类型。选中release时表示生成发布模式的安装包,选中debug时表示生成调试模式的安装包。
发布模式不是直接删掉调试代码,而是通过某个开关控制是否调试信息,因为App后续还得修改、更新、重新发布,这个迭代过程要不断调试,从而实现并验证新功能。App代码可通过代码boolean isDebuggable = (0!= (getApplicationContext().getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE));
判断当前是发布模式还是调试模式,此值为false表示处于发布模式,为true表示处于调试模式。于是利用此方法能够控制是否打开日志,在开发阶段导出调试包,在上架阶段导出发布包,这样日志只会在调试包中打印日志,不会在发布包中打印。
控制调试信息的工具类主要有两种,分别对Log工具和Toast工具加以封装,说明如下:
1.Log日志
Log工具用于打印调试日志。在App运行过程中,日志信息会输出到Logcat窗口。因为最终用户不关心App日志,所以除非特殊情况,发布上线的App应屏蔽所有日志信息。下面是封装了调试模式的Log工具代码:
public class LogUtil {public static boolean isDebug = false; // 标志位需要在启动App时同步public static void v(String tag, String msg) {if (isDebug) {Log.v(tag, msg); // 打印冗余日志}}public static void d(String tag, String msg) {if (isDebug) {Log.d(tag, msg); // 打印调试日志}}public static void i(String tag, String msg) {if (isDebug) {Log.i(tag, msg); // 打印一般日志}}public static void w(String tag, String msg) {if (isDebug) {Log.w(tag, msg); // 打印警告日志}}public static void e(String tag, String msg) {if (isDebug) {Log.e(tag, msg); // 打印错误日志}}
}
2.提示Toast
Toast工具在界面下方弹出小窗,给用户一两句话的提示,小窗暂停一会后消失。由于Toast窗口无交互动作,样式也基本固定,因此除了少数弹窗在发布时予以保留,其他弹窗都应在发布时屏蔽。下面是封装了调试模式的Toast工具代码:
public class ToastUtil {public static boolean isDebug = false; // 标志位需要在启动App时同步// 不管发布模式还是调试模式,都弹出提示文字public static void show(Context ctx, String desc) {Toast.makeText(ctx, desc, Toast.LENGTH_SHORT).show();}// 调试模式下弹出短暂提示public static void showShort(Context ctx, String desc) {if (isDebug) {Toast.makeText(ctx, desc, Toast.LENGTH_SHORT).show();}}// 调试模式下弹出长久提示public static void showLong(Context ctx, String desc) {if (isDebug) {Toast.makeText(ctx, desc, Toast.LENGTH_LONG).show();}}
}
除此之外,AndroidManifest.xml也要区分发布模式与调试模式。应用上架之后,若无特殊情况,开发者都不希望activity和service对外部开放,所以要在activity和service标签下分别添加属性android:exported="false"
,表示该组件不允许对外开放。
多渠道打包
对于很对大型App来说,针对不同渠道进行精细化运营是必不可少的,并且客观上也要求对App分渠道管理。这里所谓的渠道,指的是提供App下载的各大应用商店,尤其是各大手机厂商预装的自家应用商店,包括荣耀、小米、OPPO、vivo等品牌。根据不同渠道打造对应的App安装包,带来的好处包括但不限于以下几点:
- 各厂商的底层系统有着不同的适配要求,需要分别加以定制。
- 有助于统计各家渠道的App下载量、用户数量以及业务交易量。
- 有助于统计各家厂商的App用户分别展开精准营销活动。
那么应该如何应对App分渠道打包呢?倘若每打开一个安装包,就要手工改配置手工导出APK,无疑费力费神。其实略施小计,通过修改build.gradle.kts,即可实现自动划分渠道打包的功能。
我们只需给android节点添加flavorDimensions与productFlavors配置,指定风味维度与产品风味,表示开启多渠道打包功能。配置如下:
android {// ......flavorDimensions += "version"productFlavors {create("honor") {dimension = "version"applicationIdSuffix = ".honor"versionNameSuffix = "-honor"}create("xiaomi") {dimension = "version"applicationIdSuffix = ".xiaomi"versionNameSuffix = "-xiaomi"}create("oppo") {dimension = "version"applicationIdSuffix = ".oppo"versionNameSuffix = "-oppo"}create("vivo") {dimension = "version"applicationIdSuffix = ".vivo"versionNameSuffix = "-vivo"}}
}
点击同步按钮,然后依次选择菜单Build->Generate Singned Bundle/APK…,在最后一页的打包对话框中看到多个渠道名称,如下图:
选中列表中的小米Debug和Release两个版本,然后点击Create按钮开始打包。稍等片刻,即可在设置的目标路径看到打包好的各渠道安装包了,安装包文件名形如app-xiaomi-release.apk这样。
安全加固
本节介绍如何对APK安装包进行安全加固:首先通过反编译工具成功破解App源码,从而表明对APK实施安全防护的必要性;然后说明代码混淆的开关配置,并演示代码混淆如何加大源码破解的难度;最后描述怎样利用第三方加固网站对APK进行加固,以及如何对加固包进行重签名。
反编译
编译是把代码编译为程序,反编译是把程序破解为代码。
谁都不想自己的劳动成果被别人窃取,何况是辛辛苦苦敲出来的App代码。然而由于Java语言的特性,Java写的程序往往很容易被破解,只要获得App的安装包,就能通过反编译工具破解出改App的完整源码。开发者绞尽脑汁上架一个App,结果这个App却被他人从届满到代码都“山寨”了,那可是欲哭无泪。为了说明代码安全的重要性,下面详细介绍反编译的完整过程,警醒开发者防火、防盗、防破解。
首先准备反编译的3个工具,分别是apktool、dex2jar、jd-gui,注意下载它们的最新版本。下面是这3个工具的简要说明。
- apktool:对APK文件解包,主要用来解析res资源和AndroidManifest.xml。
- dex2jar:将APK包中的classes.dex转为JAR包,JAR包就是Java代码的编译文件。
- jd-gui:将JAR包反编译为Java源码。
以Windows环境为例,下面是反编译APK的具体步骤。
- 依次选择开始菜单->Windows系统->命令提示符,打开命令窗口,进入apktool所在目录,运行命令“apktool.bat d -f 解包后的保存目录 待处理的APK文件名”,等待反编译过程,如下图所示。
反编译完成,即可在apktool目录下看到破解目录。apktool的用途是解析出res资源,包括AndroidManifest.xml和res/layout、res/values、res/drawable等目录下的资源文件。 - 用压缩软件(如WinRAR)打开APK文件,发现APK安装包其实是一个压缩文件,使用WinRAR打开的APK文件的目录结构如下图。
先从APK包中解出classes.dex文件,并打开classes.dex将dex开头改为036(据了解dex2jar-2.0版本的工具只支持dex开头字节为035和036的Android版本,由于高版本的Android编译生成的dex开头字节不同,如Android 7.0的dex开头字节是037,Android 8.0的dex开头字节是038,Android9.0的dex的开头字节是039)。
再进入dex2jar所在的目录,运行命令.\d2j-dex2jar.bat classes.dex
,等待转换过程,如下图。
转换完毕即可在dex2jar目录下看到新文件classes-dex2jar.jar,该JAR包即为Java源码的编译文件。 - 双击打开jd-gui.exe,用鼠标把第二步生成的classes-dex2jar.jar拖到jd-gui界面中,程序就会自动将JAR包反编译为Java源码,反编译后的Java源码目录结构如下:
在jd-gui界面依次选择菜单File->Save All Source,输入保存路径再单击保存按钮,即可在指定目录下生成ZIP文件,解压ZIP文件就能看到反编译后的全部Java代码了。
由此可见,反编译过程不但破解了Java代码,而且res目录下的资源文件也被一起破解了,所以,如果App不采取以下保护措施,整个工程源码就会暴露在大庭广众之下。
代码混淆
前面讲到反编译就能够破解App的工程源码,因此有必要对App源码采取防护措施,代码混淆就是保护代码安全的措施之一。Android Studio已经自带混淆器ProGuard,它的用途主要有下列两点:
- 压缩APK包的大小,删除无用代码,并简化部分类名和方法名。
- 加大破解源码的难度,部分类名和方法名被重命名使得程序逻辑变得难以理解。
代码混淆的配置文件其实一直存在,每次在Android Studio新建一个模块,该模块的根目录下会自动生成文件proguard-rules.pro。打开build.gradle.kts,在android->buildTypes->release节点下可以看到两行编译配置,其中便用到了proguard-rules.pro:
isMinifyEnabled = false
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"),"proguard-rules.pro"
)
由于Android Studio默认不做代码混淆,因此上面第一行的isMinifyEnabled 为false,表示关闭混淆功能,要把该参数改为true才能开启混淆功能。上面第四行指定了proguard-rules.pro作为本模块的混淆规则文件,该文件保存着各种详细的代码混淆规则。
对于初学者来说,采用Android Studio默认的混淆规则即可,所以无需改动proguard-rules.pro,只要把build.gradle.kts里的isMinifyEnabled改为true,Android Studio就会按照默认的混淆规则对App代码进行混淆处理。
经过代码混淆后重新生成的APK安装包,再用反编译工具破解APK文件,反编译后的Java源码结构如下所示:
从图中可以看出,混淆后的包名与类名都变成了a、b、c、d这样的名称,无疑加大了黑客理解源码的难度。
第三方加固及重签名
App经过代码混淆后初步结束了裸奔的状态,但代码混淆只能加大源码破译的难度,并不能完全阻止被破解。除了代码破解外,App还存在其他安全风险,比如二次打包、篡改内存、漏洞暴露等情况。对于这些安全风险,Android Studio基本无能为力。因此,鉴于术业有专攻,不妨把APK为文件交给专业网站进行加固处理。例如360加固,其网址是https://jiagu.360.com/。开发者要先注册并购买服务后才能使用。
APK加固后可能会破坏原来的签名,也就无法在手机上安装,此时要对该文件进行重签名,才能成为合法的APK安装包。重签名可使用专门的签名软件,比如爱加密的APKSign等。
工程源码
文章涉及的所有代码可点击工程源码下载。
相关文章:
安卓打造安装包(应用打包、规范处理安装包、安全加固)
本章介绍应用安装包的基本制作规范,主要包括:如何导出既美观又精简的APK文件、如何按照上线规范调整App的相关设置、如何对APK文件进行安全加固以防止安装包被破解。 应用打包 本节介绍APK安装包的打包过程,包括:如何利用Androi…...
ElasticSearch教程(详解版)
本篇博客将向各位详细介绍elasticsearch,也算是对我最近学完elasticsearch的一个总结,对于如何在Kibana中使用DSL指令,本篇文章不会进行介绍,这里只会介绍在java中如何进行使用,保证你看完之后就会在项目中进行上手&am…...
[office] excel做曲线图的方法步骤详解 #经验分享#知识分享#其他
excel做曲线图的方法步骤详解 Excel是当今社会最流行用的办公软件之一,Excel可以用于数据的整理、分析、对比。可以更直观的看到数据的变化情况,而有很多时候需要制作曲线图表进行数据比较,因此,下面是小编整理的如何用excel做曲线…...
Git+Gitlab 远程库测试学习
Git远程仓库 1、Git远程仓库 何搭建Git远程仓库呢?我们可以借助互联网上提供的一些代码托管服务来实现 Gitee 码云是国内的一个代码托管平台,由于服务器在国内,所以相比于GitHub,码云速度会更快 码云 Gitee - 基于 Git 的代码托…...
Python可视化 | 使用matplotlib绘制面积图示例
面积图是数据可视化中的一个有效工具,用于说明时间上的关系和趋势。它们提供了一种全面的、视觉上迷人的方法,通过熟练地将折线图的可读性与填充区域的吸引力相结合来呈现数值数据。 在本文中,我们将学习更多关于在Python中创建面积折线图的…...
【环境搭建】2.阿里云ECS服务器 安装MySQL
在阿里云的 Alibaba Cloud Linux 3.2104 LTS 64位系统上安装 MySQL 8,可以按照以下步骤进行: 1.更新系统软件包: 首先,更新系统软件包以确保所有软件包都是最新的: sudo yum update -y2.下载 MySQL 8 官方 Yum 仓库…...
Python Flask 入门开发
Python基础学习: Pyhton 语法基础Python 变量Python控制流Python 函数与类Python Exception处理Python 文件操作Python 日期与时间Python Socket的使用Python 模块Python 魔法方法与属性 Flask基础学习: Python中如何选择Web开发框架?Pyth…...
PostgreSQL查看当前锁信息
PostgreSQL查看当前锁信息 基础信息 OS版本:Red Hat Enterprise Linux Server release 7.9 (Maipo) DB版本:16.2 pg软件目录:/home/pg16/soft pg数据目录:/home/pg16/data 端口:5777查看当前锁信息的sql SELECT pg_s…...
毫米波雷达深度学习技术-1.6目标识别2
1.6.4 自动编码器和变体自动编码器 自编码器包括一个编码器神经网络,随后是一个解码器神经网络,其目的是在输出处重建输入数据。自动编码器的设计在网络中施加了一个瓶颈,它鼓励原始输入的压缩表示。通常,自编码器旨在利用数据中的…...
MineAdmin 前端打包后,访问速度慢原因及优化
前言:打包mineadmin-vue前端后,访问速度很慢,打开控制台,发现有一个index-xxx.js文件达7M,加载时间太长; 优化: 一:使用文件压缩(gzip压缩) 1、安装compre…...
使用Obfuscar 混淆WPF(Net6)程序
Obfuscar 是.Net 程序集的基本混淆器,它使用大量的重载将.Net程序集中的元数据(方法,属性、事件、字段、类型和命名空间的名称)重命名为最小集。详细使用方式参见:Obfuscar 在NetFramework框架进行的WPF程序的混淆比较…...
高中数学:数列-基础概念
一、什么是数列? 一般地,我们把按照确定的顺序排列的一列数称为数列,数列中的每一个数叫做这个数列的项,数列的第一项称为首项。 项数有限个的数列叫做有穷数列,项数无限个的数列叫做无穷数列。 二、一般形式 数列和…...
linux中dd命令以及如何测试读写速度
dd命令详解 dd命令是一个在Unix和类Unix系统中非常常用的命令行工具,它主要用于复制文件和转换文件数据。下面我会详细介绍一些dd命令的常见用法和功能: 基本语法 dd命令的基本语法如下: bash Copy Code dd [option]...主要选项和参数 if…...
centos官方yum源不可用 解决方案(随手记)
昨天用yum安装软件的时候,就报错了 [rootop01 ~]# yum install -y net-tools CentOS Stream 8 - AppStream 73 B/s | 38 B 00:00 Error: Failed to download metadata for repo appstream: Cannot prepare internal mirrorlis…...
langchian_aws模块学习
利用langchain_aws模块实现集成bedrock调用模型,测试源码 from langchain_aws.chat_models import ChatBedrock import jsondef invoke_with_text(model_id, message):llm ChatBedrock(model_idmodel_id, region_name"us-east-1")res llm.invoke(messa…...
归并排序-成绩输出-c++
注:摘自hetaobc-L13-4 【任务目标】 按学号从小到大依次输入n个人的成绩,按成绩从大到小输出每个人的学号,成绩相同时学号小的优先输出。 【输入】 输入第一行为一个整数,n,表示人数。(1 ≤ n ≤ 100000…...
✔️Vue基础+
✔️Vue基础 文章目录 ✔️Vue基础computed methods watchcomputed计算属性methods计算属性computed计算属性 VS methods方法计算属性的完整写法 watch侦听器(监视器)watch侦听器 Vue生命周期Vue生命周期钩子 工程化开发和脚手架脚手架Vue CLI 项目目录介…...
基于VS2022编译GDAL
下载GDAL源码;下载GDAL编译需要依赖的必须代码,proj,tiff,geotiff三个源码,proj需要依赖sqlite;使用cmake编译proj,tiff,geotiff;proj有版本号要求;使用cmake…...
C语言之字符函数总结(全部!),一篇记住所有的字符函数
前言 还在担心关于字符的库函数记不住吗?不用担心,这篇文章将为你全面整理所有的字符函数的用法。不用记忆,一次看完,随查随用。用多了自然就记住了 字符分类函数和字符转换函数 C语言中有一系列的函数是专门做字符分类和字符转换…...
vite常识性报错解决方案
1.导入路径不能以“.ts”扩展名结束。考虑改为导入“xxx.js” 原因:当你尝试从一个以 .ts 结尾的路径导入文件时,ESLint 可能会报告这个错误,因为它期望导入的是 JavaScript 文件(.js 或 .jsx)而不是 TypeScript 文件&…...
2024.06.08【读书笔记】丨生物信息学与功能基因组学(第十二章 全基因组和系统发育树 第四部分)【AI测试版】
读书笔记:《生物信息学与功能基因组学》第十二章 - 第四部分 目录 基因组测序的生物信息学工具 1.1 常用生物信息学软件介绍1.2 基因组数据的管理和分析 基因组序列的比较分析 2.1 基因组之间的相似性与差异性2.2 比较基因组学的应用 基因组学在医学和健康科学中…...
IO进程线程(八)线程
文章目录 一、线程(LWP)概念二、线程相关函数(一)创建 pthread_create1. 定义2. 使用(不传参)3. 使用(单个参数)4. 使用(多个参数)5. 多线程执行的顺序6. 多线程内存空间 ࿰…...
Linux基础指令网络管理003
本章主要讲述如何进行网络诊断。 操作系统: CentOS Stream 9 操作步骤: 操作指令 ping: 测试网络连接的连通性和延迟。 [rootlocalhost ~]# ping 192.168.80.111 PING 192.168.80.111 (192.168.80.111) 56(84) 比特的数据。 64 比特&a…...
在Android中使用 MQTT 服务实现消息通信
1.摘要 MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)是一种轻量级的、基于发布/订阅(Publish/Subscribe)模式的通信协议,最初由 IBM 在1999年开发。它设计用于在低带宽、不稳定的网络环境下…...
qsort函数
学习c语言的过程中少不了的就是排序,例如冒泡排序(不清楚的同学可以翻找一下之前的文章), 我们这里将冒泡排序作为一个自定义函数来呈现一下 #include<stdio.h>void bubble_sort(int arr[], int len) {for (int i 0; i &…...
你可以直接和数据库对话了!DB-GPT 用LLM定义数据库下一代交互方式,数据库领域的GPT、开启数据3.0 时代
✨点击这里✨:🚀原文链接:(更好排版、视频播放、社群交流、最新AI开源项目、AI工具分享都在这个公众号!) 你可以直接和数据库对话了!DB-GPT 用LLM定义数据库下一代交互方式,数据库领…...
数据结构笔记2 栈和队列
为什么在循环队列中,判断队满的条件是(Q.rear1)模maxqsize? 取模运算(%)在循环队列中起到关键作用,主要是因为它能确保索引值在数组的有效范围内循环。具体来说,取模运算有以下几个重要作用&am…...
Python | 刷题笔记
继承 class Father:__secret"you are your own kid"stroy"iam a handsome boy..."def tellstory(self):print("我的故事:",self.stroy)def __tellstory(self):print("我的秘密:",Father.__secret) class Son(Father):def tell(self…...
软件三班20240605
文章目录 1.创建工程和模块2.添加 web支持3.创建前端代码4.添加servlet 依赖5. 代码6.案例2 1.创建工程和模块 2.添加 web支持 方法1 方法2 3.创建前端代码 4.添加servlet 依赖 5. 代码 <!DOCTYPE html> <html lang"en"> <head><meta c…...
http和https数据传输与协议区分
目录 1. 数据传输安全性2. 端口号3. URL 前缀4. SSL/TLS 证书5. 性能6. SEO 和用户信任7. 应用场景总结 HTTP(HyperText Transfer Protocol)和 HTTPS(HyperText Transfer Protocol Secure)是用于在客户端(如浏览器&…...
对网站做打包备份处理/百度引擎
// 对Date的扩展,将 Date 转化为指定格式的String // 月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符, // 年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字) // 例子: // (new D…...
网站界面设计说明/站牛网是做什么的
构建这里搜集了用来构建应用程序的工具。Apache Maven:Maven使用声明进行构建并进行依赖管理,偏向于使用约定而不是配置进行构建。Maven优于Apache Ant。后者采用了一种过程化的方式进行配置,所以维护起来相当困难。Gradle:Gradle…...
易语言做网站登录/域名服务器ip地址查询
趣味跑步比赛手机版是一款非常的魔性的休闲跑步小游戏,靓丽的颜色组成了精彩的游戏画面,把玩家带进这个有趣的世界,充满了想象力的那些脑洞大开的跑道的设计十分的好玩,让玩家在不用于一般的赛道之上比赛冲满了惊险和刺激…...
微信网站怎么做的好/seo网站推广案例
UART与串口(serial port)的区别 UART:通用异步收发传输器,将并行数据与串行数据进行转换 串口:串行数据传输 所谓串行,就是按照顺序,一个一个的来,就比如食堂打饭,只有一个窗口,必须…...
学做网站多长时间/教育培训机构平台
周四见 公开课系列We,知数堂习惯用实力介绍自己—我们只分享干货重磅福利来袭2018年8月9日,20:30-22:00周四见不见不散!郑 松 华知数堂《SQL优化》课程讲师资深数据库工程师对SQL优化有独到见解7年SQL开发和调优经验于韩国法院数据中心从事数据库技术支…...
临沂网站推广goldball/河源市企业网站seo价格
对于一些数据我们需要进行加密,下面介绍加密的一种方法 前端vue对密码进行加密 import CryptoJS from crypto-js// 默认的 KEY 与 iv 如果没有给 const KEY CryptoJS.enc.Utf8.parse("1234567890123456"); const IV CryptoJS.enc.Utf8.parse(1234567890…...