Android Studio Gradle实践之多渠道自动化打包+版本号管理

转: Android Studio Gradle实践之多渠道自动化打包+版本号管理



上次介绍了Android Studio的安装、配置和基本使用。这次讲一下Android Studio用到的打包工具Gradle。Gradle是一种构建项目的框架,兼容Maven、Ant,为Java项目提供了很多插件去实现打包功能。废话不多说,下面直接进入实战。当我写这篇博客的时候,Android Studio的版本已经更新到了1.4,比上一篇博客的版本又更新了。

Android Studio工程build.gradle脚本介绍

在进行多渠道打包之前,先介绍一下Android Studio工程中的gradle脚本长什么样。打开Android Studio,新建一个Project,这里我给它命名为Hello Gradle,一路点击下一步,最后Android Studio自动为我们建立的如下图的这个工程。

hello AS

按照上篇博客中介绍的,我们推荐大家采用Android结构的视图来查看项目结构。展开Gradle Scripts我们可以看到里面有两个build.gradle文件和一个settings.gradle文件。其中的build.gradle(Project: HelloGradle)文件是我们整个工程的build文件,而build.gradle(Module: app)文件是我们工程下的一个Module的build文件。前面我们就说过Android Studio采用单工程多Module结构,一个工程可以理解为Eclipse下的一个Workspace,一个Module可以理解为Eclipse下的Project。当我们用Android Studio建立一个默认的工程时,它自动为我们建立了一个名字为app默认的Module。

所以我们可以知道,一个Android Studio工程会有一个工程级别的build.gradle文件,同时有N个Module,就还会有N个Module级别的build.gradle文件。

工程目录下的build.gradle(Project: HelloGradle)文件

接着我们先看下这个工程级别的build.gradle文件。

// Top-level build file where you can add configuration options common to all sub-projects/modules.buildscript {repositories {jcenter()}dependencies {classpath 'com.android.tools.build:gradle:1.3.0'// NOTE: Do not place your application dependencies here; they belong// in the individual module build.gradle files}
}allprojects {repositories {jcenter()}
}task clean(type: Delete) {delete rootProject.buildDir
}

这个文件里的buildscript闭包中为我们定义了工程用到的repository地址,默认为我们加上了jcenter,并添加了版本号为1.3.0的Android Gradle插件。关于闭包,由于gradle是基于Groovy语言编写的,而闭包是里面的一个概念,可以理解为最小的代码执行块。关于jcenter,可以理解为一个兼容Maven中央仓库的东西,是Google为Android建立的。

最下面还有一个task clean,task是gradle脚本中用到最多的东西了。Gradle实际上是一个容器,实现真正的功能的都是Gradle的插件Plugin,而Plugin中又定义了各式各样的Task,这一个个的Task是执行任务的基本单元。

这里一看就知道是一个delete类型的task,意思是在我们执行打包脚本前做一个清理工作,把项目输出文件夹中的文件先全部清理干净。

Module目录下的build.gradle(Module: app)文件

接着看app Module下的build.gradle文件。

apply plugin: 'com.android.application'

第一行apply plugin: 'com.android.application':指的是在这个脚本中应用Android Application插件。前面我们说到了Gradle中真正起作用的是插件,每个插件中可以定义各种各样的Task,当然还可以有一些Property属性,如果你以前是用Ant打包的,那么对属性一定不会陌生吧。

android {compileSdkVersion 22buildToolsVersion "23.0.1"defaultConfig {applicationId "com.nought.hellogradle"minSdkVersion 14targetSdkVersion 22versionCode 1versionName "1.0"}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}}
}

接着看android闭包,里面首先定义了我们这个Module使用的compileSdkVersionbuildToolsVersion,这两个属性大家肯定知道,一个是用来编译代码的sdk版本,一个是用来打包apk的build-tools版本。

再看里面的defaultConfig,又定义了几个属性。依次有applicationId,代表着你的包名,以前我们都是在AndroidManifest.xml文件中通过package="com.nought.hellogradle"指定应用程序的包名,现在我们可以在gradle打包脚本中指定它,后面你会发现我们结合 buildTypes和 productFlavors ,还可以动态的改变它,有点神奇了吧! minSdkVersion 指的是你的应用程序兼容的最低Android系统版本; targetSdkVersion 指的是你的应用程序希望运行的Android系统版本;versionCode 是你的代码构建编号,一般我们每打一次包就将它增加1;versionName 则是你对外发布时,用户看到的应用程序版本号,一般我们都用“点分三个数字”来命名,例如1.0.0

接着看下 buildTypes ,这里面默认只定义了 release 类型,其实还可以定 debug 类型以及你自己定义的例如 internal 国内类型、external 国外类型等等。以前在每一个type中,可以分别配置不同的选项,例如可以 配置不同的包名、是否混淆 等等,目前的默认release类型中配置了混淆文件,minifyEnabled false指的是不混淆代码,下面这行 proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 指定的是你的混淆配置文件。这里就不详细介绍了,马上我们就会看一下多渠道打包,实践一下大家就清楚了。

dependencies {compile fileTree(dir: 'libs', include: ['*.jar'])testCompile 'junit:junit:4.12'compile 'com.android.support:appcompat-v7:22.2.1'compile 'com.android.support:design:22.2.1'
}

最后,我们看dependencies闭包,这里指的是我们的工程依赖的库,以往在Eclipse中开发,我们常常通过jar包,以及添加library的形式来添加依赖,现在方便了,在gradle脚本里,一行代码通通搞定!真是简单啊!dependencies闭包下,有几种基本的语法。

  • 1:compile fileTree(dir: 'libs', include: ['*.jar']),指的是依赖libs下面所有的jar包,你还可以指定具体的每一个jar包,而不是采用*.jar通配符匹配的方式,例如compile files('libs/文件名.jar')

  • 2:compile 'com.android.support:appcompat-v7:22.2.1',这种语法是通过包名:工程名:版本号的形式来依赖的,

  • 3:testCompile 'junit:junit:4.12',指的是测试时才会用到的依赖,这里一看就知道是指做单元测试时依赖junit。

好了,上面介绍了Android Studio默认生成的基本的Gradle打包脚本的结构。下面我们在实践中学习,怎么修改这个脚本,来实现自己的各种需求,例如多渠道自动化打包等等。

多渠道打包实践

多渠道指的是你的应用程序可以发布到不同的应用市场,被不同的用户从各个市场下载以后,你可以监测到每一个用户安装的这个应用程序是来自哪个市场的。实现的方法有很多,主要是通过在安装包中的放置一个标志位来区分不同的渠道包。

多渠道打包实现思路

思路1:AndroidManifest.xml占位符与productFlavor结合

比较常见的友盟移动统计sdk中使用的方案,这种方案是 通过build.gradle脚本中的productFlavor 来实现的。首先在AndroidManifest.xml文件的 application 标签里指定一个 meta-data ,然后Umeng SDK会读取这个标签中value传到Umeng的后台,这样就可以让开发者监测到自己的应用程序渠道分布情况了。

<meta-data android:name="UMENG_CHANNEL" android:value="${UMENG_CHANNEL}"></meta-data>

其实meta-data元素可以作为子元素,被包含在 activity,application,servicereceiver标签中,但是不同位置下的 meta-data 读取方法不一样,我们这里就以在application中放置占位符为例。

思路2:一次打包,动态替换渠道标识符

在美团的技术博客上还分享过 另外一种实现思路 :就是在打包完apk之后,再拆包替换掉其中一个文件,或者替换文件中的标识符,实现不同渠道市场的打包。因为apk实际上也是一种zip文件,里面有Android定义的一些文件组织结构,比如可以在assert目录下塞一个文件,命名为version之类的,再动态改变其中的内容。这种思路和官方的buildTypes + productFlavor方式有所不同。因为这种思路只需要执行一次打包任务,剩下的操作是拆开apk,替换文件。可想而知这种速度比较快,如果你有很多个渠道包要打的话,这种思路能提高很多速度,据说100个渠道包大概只要2分钟。而普通的buildTypes + productFlavor方式,我打了4个渠道包也花费了几十秒。可见如果有很多渠道包要出,建议采用美团的这种思路。

多渠道打包实现步骤

1. 在AndroidManifest.xml的application标签下定义UMENG_CHANNEL占位符。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.nought.hellogradle" ><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:supportsRtl="true"android:theme="@style/AppTheme" ><meta-data android:name="UMENG_CHANNEL" android:value="${UMENG_CHANNEL}"></meta-data><activityandroid:name=".MainActivity"android:label="@string/app_name"android:theme="@style/AppTheme.NoActionBar" ><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application></manifest>

2. 修改app目录下的build.gradle脚本,在android闭包中添加productFlavors 属性,配置替换占位符的渠道标识。

apply plugin: 'com.android.application'android {compileSdkVersion 22buildToolsVersion "23.0.1"defaultConfig {applicationId "com.nought.hellogradle"minSdkVersion 14targetSdkVersion 22versionCode 1versionName "1.0"}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}}productFlavors {GooglePlay {manifestPlaceholders = [UMENG_CHANNEL: "GooglePlay"]}Baidu {manifestPlaceholders = [UMENG_CHANNEL: "Baidu"]}Wandoujia {manifestPlaceholders = [UMENG_CHANNEL: "Wandoujia"]}Xiaomi {manifestPlaceholders = [UMENG_CHANNEL: "Xiaomi"]}}
}dependencies {compile fileTree(dir: 'libs', include: ['*.jar'])testCompile 'junit:junit:4.12'compile 'com.android.support:appcompat-v7:22.2.1'compile 'com.android.support:design:22.2.1'
}

3. 打开Android Studio自带的命令行工具,运行gradle build命令,就可以在 app/build/outputs/apk/ 目录下看到生成渠道包apk文件。注意:输出的apk文件是在app Module下的build目录中,不是工程根目录下的build目录。

hello AS

我们可以看到在 app/build/outputs/apk/ 中,生成的带有渠道标识的apk文件有12个,这时因为 buildTypes 与 productFlavors 两两组合,2*4=8,Android Studio默认必须有release 和 debug 这两种Type。此外,由于buildTypes中还可以定义 zipAlignEnabled true ,意思是混淆后的zip优化,该值默认为true,因此每个渠道还多了一个 app-渠道标识-debug-unaligned.apk 文件。

小结:

运行gradle build命令时,终端里会显示当前正在执行的task,里面有很多我们熟悉的任务,例如dex、javaCompile这些。前面我们说过,gradle脚本会以钩子的形式,执行一系列的tasks,最终构建出我们所需要的程序安装包。感兴趣的同学可以执行一下 gradle tasks 命令,这个命令可以查看当前工程下所有的tasks,后面我也将结合这些tasks,实践一下jar包的构建。

hello AS

PS:渠道包修改包名

如果你想修改不同的渠道包的包名,可以在你的 productFlavors 指定不同的 **applicationId ** 即可。在build.gradle文件中,输入的时候你就发现自动补全已经提示你,还有很多其他的属性可以配置了,感兴趣的同学不妨试试。

PPS:渠道包改应用名称

如果你还想给不同的渠道指定不同的应用名字,例如想要在Xiaomi市场上叫做 “HelloGradle-小米专供版” , 那么你可以新建 app/src/Xiaomi/res/values/strings.xml 的文件,里面填写<string name="app_name">HelloGradle-小米专供版</string>,这样打包出来的小米渠道包,应用程序的名称就改变成“HelloGradle-小米专供版” 了。

PPPS:单独打包某一个渠道

运行 gradle build 会一次性打包出所有的渠道包,花费的时间还是很长的。如果只想打一个渠道的渠道包话应该怎么做?以百度为例,可以在命令行中执行 gradle assembleBaidu ,我是怎么找到 assembleBaidu 这个任务名字的?前面提到过的,运行 gradle tasks,你就会发现所有的tasks列表,找到build类的tasks,就看到了!其实Android Studio里面,这些全部都有界面操作的,大家看下代码编辑窗口的右边栏,是不是有一个Gradle的按钮,点击一下展开它,然后点击面板左上角的刷新按钮,就可以将所有的tasks列出来了,和执行命令行的效果是一样的。定制化打包的需求还有很多,同学们可以自己尝试尝试,记得分享出来给大家啊!

hello AS


版本号管理实践

版本号管理,在实际的业务中有很重要的作用,因为有的时候我们需要在做新版本特性的时候对旧版本做一些兼容处理,即使旧版本不能享受新版本功能,但是也不能影响到旧版本上已有功能的稳定运行。例如,新版本的应用程序支持视频播放,而旧版本的应用无法支持,那么可以在后台做控制,只针对新版本的应用返回视频数据,而旧版本不需要返回视频数据。

一般版本号都会用 major.minor.patch 表示,例如 1.0.0 这样的形式。第一个数字major表示主版本号,第二个数字minor表示副版本号,第三个数字patch表示小版本号或者叫补丁号。当然也不一定要强制用三个数字来表示,直接用 major.minor 也是可以的,但是一旦你的应用程序版本号按照一个规则进行管理后,如果后台有逻辑依赖这个版本号,那么就不应该随意进行修改,一定要保持一致。

做Android开发的同学都熟悉 versionCode 和 versionName 这两个与版本管理相关的属性。以前用Eclipse开发项目时,都是在 AndroidManifest.xml文件中定义,如下所示。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.nought.hellogradle"android:versionCode="1"android:versionName="1.0" >

但是在Android Studio中,这两个属性已经被放在Module下的build.gradle中,一般是在android闭包的defaultConfig中,如下所示。

android {...defaultConfig {applicationId "com.nought.hellogradle"minSdkVersion 14targetSdkVersion 22versionCode 1versionName "1.0"}...}

正是由于放到了gradle脚本中,而 gradle脚本中可以写groovy代码和java代码 ,使得动态改变这两个属性变得更加方便了。关于 versionCode ,是一个整数,每build一次工程,我们都会将它增加1,因此可见应用程序的versionCode越大,其版本越新。而 versionName 是一个字符串,通常我们用这个来告诉用户,他们正在使用的应用程序版本名是多少,至于这个versionName每次打包怎么增加,就由你的自己来定义了,但是请记住,遵循这个规则不要改变,否则它就没有什么意义了。

下面介绍一种版本号管理的思路,还是以前面的工程为例,这里为了简便,我把多渠道打包的代码注释掉了,先实现版本号自增。

versionCode版本号自增实现步骤

1. 在app目录下新建一个文本类型的文件 version.properties,在文件中添加:

build.number=1

这里简单提一下 properties ,在gradle脚本中,我们可以定义各种 name=value ,然后通过读取属性的方式load进来,在脚本中使用。

2. 在app目录的build.gradle文件中,定义一个getVersionCode方法。

def getVersionCode() {def versionFile = file('version.properties')if (versionFile.exists()) {def Properties versionProps = new Properties()versionProps.load(new FileInputStream(versionFile))def versionCode = versionProps['build.number'].toInteger()println('Current version code is ' + versionCode.toString())return versionCode} else {throw new GradleException("Could not find version.properties!")}
}

3. 修改build.gradle中的defaultConfig闭包,将versionCode的属性赋值改为通过getVersionCode方法获取。

android {...def currentVersionCode = getVersionCode()defaultConfig {applicationId "com.nought.hellogradle"minSdkVersion 14targetSdkVersion 22versionCode currentVersionCodeversionName "1.0"}...}

4. 再定义一个updateVersionCode方法。

def updateVersionCode() {def runTasks = gradle.startParameter.taskNamesif (!('assemble' in runTasks || 'assembleRelease' in runTasks || 'aR' in runTasks)) {return}def File versionFile = file('version.properties')if (versionFile.exists()) {def Properties versionProps = new Properties()versionProps.load(new FileInputStream(versionFile))def currentVersionCode = versionProps['build.number'].toInteger()currentVersionCode++versionProps['build.number'] = currentVersionCode.toString()versionProps.store(versionFile.newWriter(), null)println('Updated version code to ' + currentVersionCode.toString())} else {throw new GradleException("Could not find version.properties!")}
}

可以看到这个方法中,首先获取了本地build任务中所有的任务名字,前面说过build任务实际上是个钩子,里面会去依赖很多其他的任务,例如assembleassembleRelease及其驼峰式缩写aR。这里我们约定为只要执行过assemble任务,就将versionCode加1。当然你可以根据需要改成其他的条件。

5. 给assembleRelease任务依赖,使得release版本构建成功后,versionCode增加1,并写入version.properties文件。

assembleRelease {}.doLast {updateVersionCode()
}

6. 打开Android Studio自带的命令行,运行cd app进入app目录,接着运行gradle assembleRelease

记得一定要进入app目录以后,再build。 当打包成功以后,versionCode增加了1,并保存在version.properties文件中。打开文件看下,果然变成了2,gradle还在第一行添加了修改时间。

#Fri Oct 23 17:34:28 CST 2015
build.number=2

小结:

说了很多,直接把完整的build.gradle脚本贴出来吧。

apply plugin: 'com.android.application'android {compileSdkVersion 22buildToolsVersion "23.0.1"def currentVersionCode = getVersionCode()defaultConfig {applicationId "com.nought.hellogradle"minSdkVersion 14targetSdkVersion 22versionCode currentVersionCodeversionName "1.0"}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}}
}dependencies {compile fileTree(dir: 'libs', include: ['*.jar'])testCompile 'junit:junit:4.12'compile 'com.android.support:appcompat-v7:22.2.1'compile 'com.android.support:design:22.2.1'
}assembleRelease {}.doLast {updateVersionCode()
}def getVersionCode() {def versionFile = file('version.properties')if (versionFile.exists()) {def Properties versionProps = new Properties()versionProps.load(new FileInputStream(versionFile))def versionCode = versionProps['build.number'].toInteger()println('Current version code is ' + versionCode.toString())return versionCode} else {throw new GradleException("Could not find version.properties!")}
}def updateVersionCode() {def runTasks = gradle.startParameter.taskNamesif (!('assemble' in runTasks || 'assembleRelease' in runTasks || 'aR' in runTasks)) {return}def File versionFile = file('version.properties')if (versionFile.exists()) {def Properties versionProps = new Properties()versionProps.load(new FileInputStream(versionFile))def currentVersionCode = versionProps['build.number'].toInteger()currentVersionCode++versionProps['build.number'] = currentVersionCode.toString()versionProps.store(versionFile.newWriter(), null)println('Updated version code to ' + currentVersionCode.toString())} else {throw new GradleException("Could not find version.properties!")}
}

如果想验证的话,我们可以在代码里读取一下应用程序的versionCode,并显示出来,这里就不演示了哈。其实我这里只是演示了一下gradle中实现一个小需求的方式,大家还可以根据需要写出各种各样的脚本。例如我工作中会将versionCode替换到代码里的某一个常量,实现方式是通过gradle脚本读取java源码文件,并通过正则表达式替换的。Gradle是用Groovy语言写的,兼容Java语法,因此我觉得特别适合Android程序员,大家也看到了上面定义的两个方法,实际上和Java语言差别不大,大家多参考一下官方的教程就会了。

versionName怎么实现自增?

前面我们在app模块的目录下添加了一个version.properties文件,里面以name=value的形式定义了build.number=1,那么我们也可以添加两行version.major=1version.minor=0,然后在gradle脚本中以属性的方式读取,语法和前面读取build.number是一样的,至于major和minor号怎么增加,每个人有自己的约定规则,我这里就不演示了。

PS:现成的版本号管理插件

后来我发现github上还有很多现成的插件可以用,里面已经内置了丰富的版本号管理功能。例如packer插件,这个插件默认可以为我们实现版本号自增,apk输出文件按照版本号命名等等,感兴趣的同学也可以去看一下。

实际上使用Gradle有非常好扩展性,前面说了它只是一个容器,真正实现功能的是插件,而插件里实现功能的是一个一个的任务Task。我们可以自己写一些Gradle Task,并进一步封装成Gradle Plugin,apply到自己项目中。

下面我还会介绍一下如何使用Gradle打包jar包+Log开关自动关闭,而不是apk文件,并在打包时实现关闭Log开关,打包完成后恢复Log开关。

最后奉上这篇博客和下一篇博客的示例工程代码,链接https://github.com/unclechen/HelloGradle。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/352471.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

【测试实践】搞定云网络系统性能测试

一、前言 在部署或管理网络系统时&#xff0c;我们更多的是关心网络的连通性&#xff0c;而对于其整体的性能往往考虑不多&#xff0c;或者即使考虑到性能、异常或稳定性的问题&#xff0c;但却发现没有合适的手段去测试或监控网络健康状况。在本文中&#xff0c;首先简单介绍…

树莓派做一个获取GPS时间的NTP服务器

由于单位用的是内部网络&#xff0c;机器时间无法与internet同步&#xff0c;导致内网的所有设备各自为政&#xff0c;对工作和管理带来的麻烦说不清&#xff0c;为解决这个问题&#xff0c;决定用手里的树莓派做通过GPS获取时间&#xff0c;然后提供NTP服务。在某宝逛了一圈&a…

自动(智能)驾驶 | 全网第一本激光雷达手册阅读指南(下)

接着从上篇继续说&#xff0c;本部分主要介绍激光雷达数据等内容&#xff0c;这个可以说是看懂激光雷达手册的重中之重&#xff0c;也就是关键问题的关键&#xff08;皮~&#xff09;。同样由于velodyne的手册比较难啃但是内容完整丰富&#xff0c;本期我们依然以velodyne 128&…

GEO-SPARK 2000X PPS使用记录(三)

最近单位突然要使用GeoSpark电火花&#xff0c;至少有4年没有用过了。我一直没有独立操作过&#xff0c;只是看同事操作过&#xff0c;碰巧的是会使用这个设备的同事去大洋了&#xff0c;悲剧的是他们把所有的采集狗都拿走了&#xff0c;留下一个巨大的坑让我来填。 1、首先看看…

CMOS图像传感器——工作原理

一、像素阵列结构 一般像素阵列是由水平方向的行( Row ) 和垂直方向的列(Column)正交排列构成的。像素排列的最基本设计原则是:摄像器件像素排列的坐标,必须在显示的时候能够准确地还原在图像原来的相对位置上。在大多数情况下,每个像素中心线在行的方向和列的方向,即…

Kerberos从入门到精通以及案例实操系列(二)

5、安全集群使用说明 5.1、用户要求 具体要求以下使用说明均基于普通用户&#xff0c;安全集群对用户有以下要求&#xff1a; 集群中的每个节点都需要创建该用户该用户需要属于hadoop用户组需要创建该用户对应的Kerberos主体 实操&#xff0c;此处以atguigu用户为例&#x…

3.了解Spring Boot2自动配置原理

了解Spring Boot2自动配置原理 1、SpringBoot特点 1.1、依赖管理 1.父项目做依赖管理 依赖管理 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.4.RELEAS…

Docker搭建Elasticsearch方法及启动后服务自动关闭的问题

太长不看&#xff0c;一句话总结 内存太小&#xff0c;启动命令中添加参数修改虚拟机大小即可 docker run -e ES_JAVA_OPTS"-Xms256m -Xmx256m" -e "discovery.typesingle-node" -d -p 9200:9200 -p 9300:9300 --name elasticsearch elasticsearch:7.6.0配…

NTP自动退出问题排查

为什么80%的码农都做不了架构师?>>> 问题现象:ntp进程运行一段时间后自动退出 排查过程: 查看/var/log/message ntp异常信息如下 Feb 20 22:54:41 xnu_1 ntpd[2187]: 0.0.0.0 0617 07 panic_stop -28777 s; set clock manually within 1000 s. Feb 20 22:54:57 …

Docker部署Elasticsearch及安装后自动关闭的问题

Docker搭建Elasticsearch方法 前往dockerhub官网&#xff1a;dockerhub 可查看elasticsearch相应版本 拉取镜像 选取一个版本拉取镜像&#xff0c;如docker pull elasticsearch:7.9.2 不输入版本默认拉取最新版 启动容器 映射端口为9200和9300 docker pull elasticsearch:…

自动化基础笔记--元素操作

元素操作&#xff08;附带鼠标悬停&#xff09; 一些课程回顾&#xff0c;问题原因总结 函数只有在return的时候&#xff0c;才会返回一个数据。返回数据了才可以赋值的 报错&#xff1a; 等待时间不够返回的元素不是你想要的确实是你表达式写错了有句柄未切换或者有iframe未…

服务器设置了自动校时但是,同步时钟配置电脑自动校时

同步时钟配置电脑自动校时 一、同步时钟操作和指示灯说明。 同步时钟可接收全球定位系统GPS、北斗卫星信号。当GPS/北斗天线架设到屋顶时&#xff0c;天线的高度必须低于房屋顶避雷天线的高度&#xff0c;防止被雷击。 先关闭电源后再连接或者去掉天线。 电源指示灯&#xff1a…

计算机怎么关闭开机自启应用,如何关闭电脑开机自动启动的软件程序

如何关闭电脑开机自动启动的软件程序 我们在电脑中安装软件的时候,不小心设置了开机启动,该怎么取消呢?今天就跟大家介绍一下如何关闭电脑开机自动启动的软件程序的具体操作步骤。 1. 首先打开电脑,找到桌面上的【360安全卫士】软件,双击打开: 2. 进入主页面后,点击上方…

Latex使用algorithm2e包写伪代码

用Latex写伪代码我们需要用到一个包&#xff0c;Algorithm2e&#xff0c;这个工具包的使用手册下载地址为&#xff08;http://mlg.ulb.ac.be/files/algorithm2e.pdf&#xff09;CSDN的链接为&#xff08;&#xff09; 准备 导入该包 \usepackage[ruled,linesnumbered]{algor…

【复杂网络建模】——基于微博数据的影响力最大化算法(PageRank)

&#x1f935;‍♂️ 个人主页&#xff1a;Lingxw_w的个人主页 ✍&#x1f3fb;作者简介&#xff1a;计算机科学与技术研究生在读 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4a…

【Markdown小技巧】 整理小图标和表情符号

&#x1f40b;作者简介&#xff1a;博主是一位.Net开发者&#xff0c;同时也是RPA和低代码平台的践行者。 &#x1f42c;个人主页&#xff1a;会敲键盘的肘子 &#x1f436;座右铭&#xff1a;总有一天你所坚持的会反过来拥抱你。 &#x1f308;写在前面&#xff1a; 让博客变得…

工信部—高级软件开发工程师认证

工业和信息化部教育与考试中心是工业和信息化部直属事业单位&#xff0c;承担计算机技术与软件专业技术资格考试、通信专业技术人员职业水平考试、电子通信行业职业技能鉴定、全国信息技术人才培养工程、产业工人网络平台建设等人才培养选拔工作。 软件工程师(Software Enginee…

EasyCVR视频汇聚智能边缘网关在多平台级联及上下级对接中的应用说明

一、行业背景 近年来&#xff0c;在政务数据共享平台的建设上&#xff0c;对国家、省数据、市数据及区县对接上要求打破“信息孤岛”&#xff0c;拔掉“数据烟囱”&#xff0c;全面打通数据“脉络”的主通道。省市平台“级联对接”工作&#xff0c;由国家数据共享平台、省级数据…

水球图的使用

水球图的使用 注意&#xff1a; echarts-liquidfill3 版本匹配 echarts5 版本&#xff0c;echarts-liquidfill2 版本匹配 echarts4 版本 npm install echarts-liquidfill2.0.2 --savevue代码演示&#xff1a; <template> <div class"com-container">…

python制作水球图

水球图是一种适合于展现单个百分比数据的图表类型&#xff0c;pyecharts模块能够非常方便画出水球图&#xff0c;进而实现酷炫的数据展示效果。 下面给大家介绍一下常见水球图的实现方法&#xff1a; 一、基本水球图 from pyecharts import options as opts from pyecharts.cha…