简介

如题所示!本篇文章就是为了解决这种问题。方便打包和运行的时候能做到无需手动替换配置,即可打包想要的apk。打包的时候,只需选一下想打哪种配置的apk就OK啦。 (^o^)/~

先来看,有需求如下:

  1. 同一个项目
  2. 不同的apk图标
  3. 不同的服务器域名
  4. 不同的包名
  5. 不同的名称
  6. 不同的签名
  7. 不同的第三方key
  8. 不同的版本名版本号

解决思路

  1. 当然最直接的方式不过于每次打不同包的时候都去替换对应的配置,这种方式的麻烦之处不言而喻。
  2. 将所有配置,资源等都配置入项目中,打包的时候,根据选择渠道打包不同配置的apk。(本篇文章就是要讲怎么这么做的)
  3. 相信还有其他的。。。

相关的几个要点

  1. 首先我们需要知道productFlavors来配置渠道,这里我将渠道用来表示哪种apk,如下我需要配置四种应用:
productFlavors {userquhua {}quhua {}cuntuba {}xemh {}
}
  1. 如果我们选择了某一个渠道,那么运行打包的时候会根据渠道名选择资源文件(可结合第6点一起看)
  2. 签名可在signingConfigs中配置多个(我将所有签名文件放在了项目跟目录的key文件夹中),这样我们就可以通过signingConfigs指定预制好的签名配置。
signingConfigs {userquhuaRelease {storeFile file("../key/xxx1.keystore")storePassword "xxxxxx"keyAlias "alias"keyPassword "xxxxxx"}quhuaRelease {storeFile file("../key/xxx2.keystore")storePassword "xxxxxx"keyAlias "alias"keyPassword "xxxxxx"}cuntubaRelease {storeFile file("../key/xxx3.keystore")storePassword "xxxxxx"keyAlias "alias"keyPassword "xxxxxx"}xemhRelease {storeFile file("../key/xxx4.keystore")storePassword "xxxxxx"keyAlias "alias"keyPassword "xxxxxx"}
}
  1. 可在build.gradle中配置动态配置java代码调用的常量数据(如:通过该方式我们可根据不同渠道动态配置第三方appid,或其他需要根据渠道而改变的数据)
  • 比如:我们在defaultConfig {} 中定义了:
buildConfigField "String", "SERVER_URL", '"http://xx.xxxx.com/"'
  • 此时,您看一下清单文件中manifest标签里的,package的值,假如是:
com.xxx.xx
  • 那么,您就可以在java代码中通过导入文件:
import com.xxx.xx.BuildConfig;
  • 然后调用
BuildConfig.SERVER_URL

它的值就是上边配置的字符串:http://xx.xxxx.com/

  • 您可以进入BuildConfig看一看,里面还包含了一些当前的包名版本号等信息。
  1. 在渠道配置那里可以配置对应的包名版本名签名等等
    如下所示:
// 省略其他配置...
android {// 省略其他配置...productFlavors {userquhua {applicationId "com.xxx.xx"versionCode 1versionName "1.0.0"signingConfig signingConfigs.userquhuaRelease // 配置签名String qq_id = '"xxxxxxxxx"' //配置qq appidbuildConfigField "String",           "QQ_ID", qq_idbuildConfigField "String",           "WX_ID", '"wxxxxxxxxxxxxxxxxx"' // 配置微信appidmanifestPlaceholders = [qq_id: qq_id,JPUSH_PKGNAME : applicationId,JPUSH_APPKEY : "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx", //JPush 上注册的包名对应的 Appkey.JPUSH_CHANNEL : "developer-default",]}}buildTypes {release {// 省略其他配置...signingConfig null  // 置空}debug {// 省略其他配置...signingConfig null // 置空}}
}
  • 这样,如果我们打包userquhua这个渠道,看第2点中介绍选择userquhuaDebug。
  • 然后,最好clean一下项目、然后我们运行项目。
  • 该app的包名就是com.xxx.xx,版本号为1,版本名为1.0.0
  • 通过BuildConfig调用QQ_ID静态常量,就是该渠道里配置的值,WX_ID同理。
  • manifestPlaceholders配置也可以这样配置。
  • 签名问题经过个人反复尝试(然后半天就过去了 ̄へ ̄),最终签名如上配置。需要注意buildTypes中的签名配置signingConfig如果不设置为null,那么打包的是有还是以内置的签名打包。
  1. 资源文件替换
    再看到第2点的介绍,我们选择运行渠道后,会默认匹配对应渠道下的资源。下面我将xemh渠道的资源目录全部展开一下。
  • 如上图这样,只需要资源名字和app目录对应的文件名字一样即可替换。
  • strings.xml里的应用名,只需要将对应app_name修改既可替换app下strings的app_name,其他不用替换的不用写就行。
  1. 打正式包的时候选好渠道,就可以打包不同配置的apk,当然您也可以使用命令的方式。

其他配置记录

获取当前时间

static def releaseTime() {return new Date().format("yyyy-MM-dd-HH.mm", TimeZone.getTimeZone("GMT+8"))
}

打包的时候,修改文件名,以方便区别渠道和版本打包时间

applicationVariants.all {variant ->variant.outputs.all {outputFileName = "${variant.productFlavors[0].name}-v${variant.productFlavors[0].versionName}-${releaseTime()}.apk"}
}
  • ${variant.productFlavors[0].name}当前渠道名
  • ${variant.productFlavors[0].versionName}当前版本名
  • ${releaseTime()}当前时间

其他需要注意事项

如果您在清单文件AndroidManifest.xml中,有那种以包名开头命名的那种。因为如果包名都改了,有些也需要动态的改变。可以用${applicationId}代替。在打包的时候,会自动替换成当前包名。

比如,类似下配置:

<permissionandroid:name="com.xxx.xx.permission.JPUSH_MESSAGE"android:protectionLevel="signature" />
<uses-permission android:name="com.xxx.xx.permission.JPUSH_MESSAGE" />
<receiverandroid:name=".push.MyJPushMessageReceiver"android:enabled="true"android:exported="false" ><intent-filter><action android:name="cn.jpush.android.intent.RECEIVE_MESSAGE" /><category android:name="com.xxx.xx" /></intent-filter>
</receiver>
<providerandroid:name="android.support.v4.content.FileProvider"android:authorities="com.xxx.xx.provider"android:exported="false"tools:replace="android:authorities"android:grantUriPermissions="true"><meta-dataandroid:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/file_paths" />
</provider>

可改为:

<permissionandroid:name="${applicationId}.permission.JPUSH_MESSAGE"android:protectionLevel="signature" />
<uses-permission android:name="${applicationId}.permission.JPUSH_MESSAGE" />
<receiverandroid:name=".push.MyJPushMessageReceiver"android:enabled="true"android:exported="false" ><intent-filter><action android:name="cn.jpush.android.intent.RECEIVE_MESSAGE" /><category android:name="${applicationId}" /></intent-filter>
</receiver>
<providerandroid:name="android.support.v4.content.FileProvider"android:authorities="${applicationId}.provider"android:exported="false"tools:replace="android:authorities"android:grantUriPermissions="true"><meta-dataandroid:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/file_paths" />
</provider>

当然值得注意的是,在代码中我们也不能把包名写死了,可通过BuildConfig得到当前包名

我的完整配置,供参考

有关隐私信息的都用xxx替换了

  1. 项目根目录的build.gradle
// Top-level build file where you can add configuration options common to all sub-projects/modules.buildscript {repositories {google()jcenter()}dependencies {classpath 'com.android.tools.build:gradle:3.0.0'classpath "io.github.prototypez:save-state:0.1.7"// NOTE: Do not place your application dependencies here; they belong// in the individual module build.gradle files}
}allprojects {repositories {google()jcenter()maven { url "https://jitpack.io" }maven { url 'http://oss.jfrog.org/artifactory/oss-snapshot-local/' }flatDir {dirs 'libs'}}
}task clean(type: Delete) {delete rootProject.buildDir
}ext{minSdkVersion               = 16targetSdkVersion            = 27compileSdkVersion           = 27buildToolsVersion           = '27.1.1'supportLibraryVersion       = '27.1.1'xmvpVersion                 = '1.2.2'retrofit2Version            = '2.3.0'okhttp3Version              = '3.8.1'butterknifeVersion          = '8.6.0'rx2Version                  = '2.0.2'CircleProgressDialogVersion = '1.0.2'smarttabVersion             = '1.6.1@aar'adapterHelperVersion        = '2.9.41'glideVersion                = '4.7.1'roundedimageviewVersion     = '2.3.0'eventbusVersion             = '3.0.0'dispatcherVersion           = '2.4.0'picture_libraryVersion      = 'v2.2.3'statusbarutilVersion        = '1.5.1'okhttpUtilsVersion          = '3.8.0'constraintVersion           = '1.1.3'flexboxVersion              = '1.0.0'
}
  1. app目录下的build.gradle
apply plugin: 'com.android.application'
apply plugin: 'save.state'static def releaseTime() {return new Date().format("yyyy-MM-dd-HH.mm", TimeZone.getTimeZone("GMT+8"))
}android {compileSdkVersion rootProject.compileSdkVersion
//    buildToolsVersion rootProject.buildToolsVersiondefaultConfig {minSdkVersion rootProject.minSdkVersiontargetSdkVersion rootProject.targetSdkVersiontestInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"multiDexEnabled true// config the JSON processing libraryjavaCompileOptions {annotationProcessorOptions {arguments = [ serializer : "gson" ]}}ndk {abiFilters "armeabi-v7a"}renderscriptTargetApi 25renderscriptSupportModeEnabled true}signingConfigs {userquhuaRelease {storeFile file("../key/xxx.keystore")storePassword "xxxxxx"keyAlias "xxx"keyPassword "xxxxxx"}quhuaRelease {storeFile file("../key/xxx.keystore")storePassword "xxxxxxx"keyAlias "xxx"keyPassword "xxxxxxx"}cuntubaRelease {storeFile file("../key/xxx.keystore")storePassword "xxxxxxx"keyAlias "xxx"keyPassword "xxxxxxx"}xemhRelease {storeFile file("../key/xxx.keystore")storePassword "xxxxxxx"keyAlias "xxx"keyPassword "xxxxxxx"}}flavorDimensions "default"productFlavors {userquhua {applicationId "com.xxx.xx"versionCode 22versionName "1.7.5"signingConfig = signingConfigs.userquhuaReleaseString qq_id = '"xxxxxx"'buildConfigField "String",           "QQ_ID", qq_id // qq appIdbuildConfigField "String",         "SINA_ID", '"xxxxxx"' // 新浪appIdbuildConfigField "String",           "WX_ID", '"xxxxxx"' // 微信 appIdbuildConfigField "String",           "UM_ID", '"xxxxxx"' // 友盟buildConfigField "String",       "WX_SECRET", '"xxxxxx"' // 微信 secretbuildConfigField "String",   "SINA_REDIRECT", '"http://open.weibo.com/apps/xxxxxx/privilege/oauth"' // 新浪buildConfigField "String",   "ADHUB_INIT_ID", '"xxxxxx"' // 广告sdk初始化idbuildConfigField "String", "ADHUB_SPLASH_ID", '"xxxxxx"' // 开屏广告idbuildConfigField "String", "ADHUB_BANNER_ID", '"xxxxxx"' // banner广告idbuildConfigField "String",      "SERVER_URL", '"http://xxx.xxx.com/"'buildConfigField "String",        "LOGO_URL", '"http://file.xxx.com/img/xxx.png"'manifestPlaceholders = [qq_id: qq_id,JPUSH_PKGNAME : applicationId,JPUSH_APPKEY : "xxxxxx", //JPush 上注册的包名对应的 Appkey.JPUSH_CHANNEL : "developer-default", //暂时填写默认值即可.]}quhua {applicationId "com.xxx.xx"versionCode 1versionName "1.0.0"signingConfig = signingConfigs.quhuaReleaseString qq_id = '"xxxxxx"'buildConfigField "String",           "QQ_ID", qq_idbuildConfigField "String",         "SINA_ID", '"xxxxxx"'buildConfigField "String",           "WX_ID", '"xxxxxx"'buildConfigField "String",           "UM_ID", '"xxxxxx"'buildConfigField "String",       "WX_SECRET", '"xxxxxx"'buildConfigField "String",   "SINA_REDIRECT", '"http://open.weibo.com/apps/xxxxxx/privilege/oauth"'buildConfigField "String",   "ADHUB_INIT_ID", '"xxxxxx"' // 广告sdk初始化idbuildConfigField "String", "ADHUB_SPLASH_ID", '"xxxxxx"' // 开屏广告idbuildConfigField "String", "ADHUB_BANNER_ID", '"xxxxxx"' // banner广告idbuildConfigField "String",      "SERVER_URL", '"http://xx.xxx.com/"'buildConfigField "String",        "LOGO_URL", '"http://file.xxx.com/img/xxx.png"'manifestPlaceholders = [qq_id: qq_id,JPUSH_PKGNAME : applicationId,JPUSH_APPKEY : "xxxxxx", //JPush 上注册的包名对应的 Appkey.JPUSH_CHANNEL : "developer-default", //暂时填写默认值即可.]}cuntuba {applicationId "com.xxx.xx"versionCode 1versionName "1.0.0"signingConfig = signingConfigs.cuntubaReleaseString qq_id = '"xxxxxx"'buildConfigField "String",           "QQ_ID", qq_idbuildConfigField "String",         "SINA_ID", '"xxxxxx"'buildConfigField "String",           "WX_ID", '"xxxxxx"'buildConfigField "String",           "UM_ID", '"xxxxxx"'buildConfigField "String",       "WX_SECRET", '"xxxxxx"'buildConfigField "String",   "SINA_REDIRECT", '"http://open.weibo.com/apps/xxxxxx/privilege/oauth"'buildConfigField "String",   "ADHUB_INIT_ID", '"xxxxxx"' // 广告sdk初始化idbuildConfigField "String", "ADHUB_SPLASH_ID", '"xxxxxx"' // 开屏广告idbuildConfigField "String", "ADHUB_BANNER_ID", '"xxxxxx"' // banner广告idbuildConfigField "String",      "SERVER_URL", '"http://xxx.xxxx.com/"'buildConfigField "String",        "LOGO_URL", '"http://file.xxx.com/img/xxx.png"'manifestPlaceholders = [qq_id: qq_id,JPUSH_PKGNAME : applicationId,JPUSH_APPKEY : "xxxxxx", //JPush 上注册的包名对应的 Appkey.JPUSH_CHANNEL : "developer-default", //暂时填写默认值即可.]}xemh {applicationId "com.xxx.xx"versionCode 1versionName "1.0.0"signingConfig = signingConfigs.xemhReleaseString qq_id = '"xxxxxx"'buildConfigField "String",           "QQ_ID", qq_idbuildConfigField "String",         "SINA_ID", '"xxxxxx"'buildConfigField "String",           "WX_ID", '"xxxxxx"'buildConfigField "String",           "UM_ID", '"xxxxxx"'buildConfigField "String",       "WX_SECRET", '"xxxxxx"'buildConfigField "String",   "SINA_REDIRECT", '"xxxxxx"'buildConfigField "String",   "ADHUB_INIT_ID", '"xxxxxx"' // 广告sdk初始化idbuildConfigField "String", "ADHUB_SPLASH_ID", '"xxxxxx"' // 开屏广告idbuildConfigField "String", "ADHUB_BANNER_ID", '"xxxxxx"' // banner广告idbuildConfigField "String",      "SERVER_URL", '"http://xx.xxx.com/"'buildConfigField "String",        "LOGO_URL", '"http://file.xxxxxx.com/img/xxxxxx.png"'manifestPlaceholders = [qq_id: qq_id,JPUSH_PKGNAME : applicationId,JPUSH_APPKEY : "xxxxxx", //JPush 上注册的包名对应的 Appkey.JPUSH_CHANNEL : "developer-default", //暂时填写默认值即可.]}}applicationVariants.all {variant ->variant.outputs.all {outputFileName = "${variant.productFlavors[0].name}-v${variant.productFlavors[0].versionName}-${releaseTime()}.apk"}}buildTypes {release {// 不显示LogbuildConfigField "boolean", "LOG_DEBUG", "false"signingConfig nullminifyEnabled truezipAlignEnabled true// 移除无用的resource文件shrinkResources trueproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}debug {// 显示LogbuildConfigField "boolean", "LOG_DEBUG", "true"signingConfig nullminifyEnabled falsezipAlignEnabled falseshrinkResources false}}packagingOptions {exclude 'META-INF/DEPENDENCIES.txt'exclude 'META-INF/NOTICE'exclude 'META-INF/NOTICE.txt'exclude 'META-INF/LICENSE'exclude 'META-INF/LICENSE.txt'}compileOptions {targetCompatibility JavaVersion.VERSION_1_8sourceCompatibility JavaVersion.VERSION_1_8}dexOptions {javaMaxHeapSize "4g" //此处可根据电脑本身配置 数值越大 当然越快preDexLibraries = false}
}repositories {flatDir {dirs 'libs', '../adpoymer/libs'}
}dependencies {implementation fileTree(include: ['*.jar'], dir: 'libs')implementation "com.android.support:appcompat-v7:$supportLibraryVersion"implementation "com.android.support:recyclerview-v7:$supportLibraryVersion"implementation "com.android.support:support-v4:$supportLibraryVersion"implementation "com.android.support:design:$supportLibraryVersion"implementation "com.android.support.constraint:constraint-layout:$constraintVersion"//添加retrofit2 的依赖 添加这个依赖就默认添加了okhttp依赖compile "com.squareup.retrofit2:retrofit:$retrofit2Version"compile "com.squareup.retrofit2:converter-gson:$retrofit2Version"compile "com.squareup.retrofit2:adapter-rxjava2:$retrofit2Version"compile "com.squareup.okhttp3:logging-interceptor:$okhttp3Version"compile "com.jakewharton:butterknife:$butterknifeVersion"annotationProcessor "com.jakewharton:butterknife-compiler:$butterknifeVersion"compile "io.reactivex.rxjava2:rxandroid:$rx2Version"compile "com.github.xujiaji:xmvp:$xmvpVersion"implementation "com.github.autume:CircleProgressDialog:$CircleProgressDialogVersion"compile "com.ogaclejapan.smarttablayout:library:$smarttabVersion"compile "com.github.CymChad:BaseRecyclerViewAdapterHelper:$adapterHelperVersion"compile "com.github.bumptech.glide:glide:$glideVersion"annotationProcessor "com.github.bumptech.glide:compiler:$glideVersion"compile "com.makeramen:roundedimageview:$roundedimageviewVersion"compile "org.greenrobot:eventbus:$eventbusVersion"annotationProcessor "com.github.hotchemi:permissionsdispatcher-processor:$dispatcherVersion"compile "com.jaeger.statusbarutil:library:$statusbarutilVersion"compile("com.github.hotchemi:permissionsdispatcher:$dispatcherVersion") {exclude module: "support-v13"}implementation "com.github.LuckSiege.PictureSelector:picture_library:$picture_libraryVersion"implementation 'me.drakeet.library:crashwoodpecker:2.1.1'implementation 'com.github.chenupt.android:springindicator:1.0.2@aar'debugImplementation 'com.amitshekhar.android:debug-db:1.0.4'implementation 'com.umeng.sdk:common:1.5.3'implementation 'com.umeng.sdk:analytics:7.5.3'implementation 'com.liulishuo.filedownloader:library:1.7.5'implementation project(':banner')implementation project(':xdialog')implementation project(':shareutil')implementation project(':update')implementation project(':pay')
//    implementation project(':adhub')implementation project(':imagewatcher')implementation files('libs/lite-orm-1.9.2.jar')implementation 'jp.wasabeef:blurry:2.1.1'implementation "com.google.android:flexbox:$flexboxVersion"implementation 'cn.jiguang.sdk:jpush:3.1.6'  // 此处以JPush 3.1.6 版本为例。implementation 'cn.jiguang.sdk:jcore:1.2.5'  // 此处以JCore 1.2.5 版本为例。compile(name: 'sdk-release', ext: 'aar')compile(name: 'open_ad_sdk', ext: 'aar')compile(name: 'adpoymer-3.4.35', ext: 'aar')implementation 'pl.droidsonroids.gif:android-gif-drawable:1.0.+'
}

Demo 地址

https://github.com/xujiaji/OneForAllApk

结束

就这样就可以解放大量劳动力啦!每次项目打包各种软件,选一下就ojbk,哈哈哈~
如果有些配置在其他渠道没有的,也可通过BuildConfig在java中判断如果是某某渠道那么屏蔽。
原文地址:https://blog.xujiaji.com/post/android-project-one-for-more
over

转载于:https://www.cnblogs.com/xujiaji/p/9930864.html

一个项目如何编译多个不同签名、包名、资源等,的apk?相关推荐

  1. 一个项目如何编译多个不同签名,包名,资源等

    简介 如题所示!本篇文章就是为了解决这种问题.方便打包和运行的时候能做到无需手动替换配置,即可打包想要的apk.打包的时候,只需选一下想打哪种配置的apk就OK啦. (^o^)/~ 先来看,有需求如下 ...

  2. android 多包名apk,一个项目如何编译多个不同签名、包名、资源等,的apk?

    4 其他需要注意的事项 如果您在清单文件AndroidManifest.xml中,有那种以包名开头命名的那种.因为如果包名都改了,有些也需要动态的改变.可以用${applicationId}代替.在打 ...

  3. Eric6最简单的应用(创建一个项目-窗体-编译-运行)

    1.创建项目 2.创建一个窗体 自动弹出Qt designer 工具,可视化窗体界面: 创建一个关闭按钮,关闭这个窗体: 保存后,选择ui文件,进行编译ui文件: 然后执行该脚本,即可: 以上是eri ...

  4. 微信申请 包名和签名

    开发环境: window 7 Egret Engine 5.0.14 Egret support 5.0.12 Eclipse 一  申请微信移动应用 二  怎么获取Android包名 三  怎么获取 ...

  5. Android Studio 修改项目包名(最后一级)

    背景: 如果新做的项目跟以前做的某一个项目十分相似,那么一个简单的方法就是把原来项目拷贝一份,然后修改代码,但是这样包名还是原来项目的包名,还有如果想在同一台手机上同时安装新做的app和原来的app会 ...

  6. 个人或团队项目怎么起包名

    转载文章 出处http://www.lxway.com/9904091.htm 在java开发的过程中,每名Java开发人员都可以编写属于自己的java package,为了在编写中保证每一个java ...

  7. (已更新)价值8000元APP封装系统 自动实现5分钟随机更换包名和签名

    PS 本源码提供给大家学习研究借鉴美工之用,请勿用于商业和非法用途,无任何技术支持! 某站8000元APP封装系统源码app误报毒app可上传+自动实现5分钟随机更换包名和签名+视频安装教程; 程序可 ...

  8. 解决APP误报毒 可打包APP可上传APK 自动实现5分钟随机更换包名和签名系统源码

    简介: 解决app误报毒 可打包APP可上传APK 自动实现5分钟随机更换包名和签名系统源码 本程序功能介绍: 程序可实现域名自动打包成app 出现误报毒并自动更换包名和签名(*时间一次) 也可以上传 ...

  9. nixyx —— 一个小巧的项目工程/编译文件生成器(构建系统?)

    恩..nixyx确实算不上是一个构建系统. 所谓构建系统,比如GNU的Autotools,那是一套很完整的构建体系,包括了程序的配置,编译和安装三大部分. 类似的软件还有:google的gyp.腾讯的 ...

最新文章

  1. 单片机要学多久才能自己开发?学单片机以后做什么?
  2. iphone连上wifi却上不了网_如何解决联通物联卡上不了网问题
  3. 【HTML】CSS基础知识
  4. java构造方法赋值内存图_java 面向对象(九):类的结构:构造器(一)简介;属性赋值顺序;JavaBean的概念...
  5. 17.立体匹配——介绍,匹配,寻找最佳匹配 Matlab实战_1
  6. Android让文本输入框默认不获取焦点
  7. shell 脚本批量安装perl包
  8. 搞定问题描述的5W2H法是什么
  9. python分布式定时任务_分布式定时任务框架——python定时任务框架APScheduler扩展...
  10. 【推荐五款ssh连接工具】
  11. 5个免费邮箱,10分钟临时邮箱,一定能用,持续更新
  12. 数据分析EXCEL常用统计函数
  13. Windows安全中心无反应,导致关闭不了病毒防护
  14. 由四位央行官员为你制作的“数字货币”说明书
  15. 关于Tungsten Fabic版本问题,这一篇文章说清了
  16. win10更改C盘下的用户文件夹名
  17. 数学建模常用算法—熵权法(EWM)
  18. 电信网上营业厅用户自服务系统的设计与实现
  19. 粉丝福利 | 秒 get 支付宝同款扫码组件
  20. leetcode 题解 904.水果成篮(Typescript)

热门文章

  1. src与href区别
  2. java实现rabbitmq任务模型(work queues), 生产者 消费者 消息队列 能者多劳
  3. Whitelabel Error Page : spring boot项目启动后,无法访问@RequestMapping标注的请求
  4. 觉得自己目前还很菜。
  5. openssl pkeyutl执行SM2椭圆曲线数字签名
  6. 2018-08-06
  7. 地理类国际顶级期刊汇总
  8. VirtualBox启动失败,The VM session was aborted.
  9. cocos2d-x中使用plist文件总结
  10. java中for 的几种常见用法