第三篇:gradle 编译 Android app 概览
引言:经过上两篇的论述,我们已经从代码到架构都简单的熟悉了一遍,理论上,只要知道android app的编译过程,我们大可以自己写一份用gradle编译app的插件,插件内将将整个流程用Task的依赖串联起来。现在我们看看gradle是怎么做的。
复习,android 编译流程
面试中经常出现的问题,android的编译流程。这个问题十分简单和好记。android是用java来开发的,所以最基本的流程和java一致:.java -> .class。android有着自己独特的虚拟机环境dalvik,毕竟arm架构寄存器比较多,而是内存较小,所以要转换一下.class -> .dex。再说资源那条线,第一件事当然是生成R.java文件了,这个java文件和我们一般的java文件并没有区别,不然我们代码怎么用呢,然后是将资源文件和dex文件打包成apk:rec+.dex=.apk。有了.apk,签名.signed.apk,压缩.aligned.signed.apk。当然还有其他的分支,比如 .aidl,可以理解成一种特殊的资源文件,同样生成.java文件和我们的代码对接,jni也是同样,只是生成顺序是我们写java端的,当然这无关紧要,符合接口约定就可以。
主线: .java --(java)-->.class--(dex)-->.dex +rec --(apkbuild)-->.apk--(jarSigne)-->.Singed.apk--(zipalign)-->.aligned.signed.apk
设置属性即可
我们用android studio 创建一个android app程序,打开他的build.gradle。
apply plugin: 'com.android.application' android {
compileSdkVersion 21
buildToolsVersion "23.0.0 rc2" defaultConfig {
applicationId "suning.com.myapplication"
minSdkVersion 15
targetSdkVersion 21
versionCode 1
versionName "1.0"
}
sourceSets{ } buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
} dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.2.0'
}
android studio的好处是我们能方便的看到源码,鉴于上两篇把整个代码组织流程已经推敲过,所以整个脚本并没有什么难以理解的地方。
开头第一句导入 com.android.application 的编译插件,下面则是对整个编译的参数进行设置(和第二篇的最后一个例子如出一辙,不过例子上的代码很简化)。我们按住ctr,鼠标左击第三行的android,就可以进入这个属性的源码:AppExtension.groovy,它继承于TestedExtension.groovy,又继承于BaseExtension.groovy。看这一长串的extension,结合我们上一篇的例子,不难猜出这是需要绑定在project里的extension 属性。我们看下BaseExtension的源码:
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.build.gradle import com.android.SdkConstants
import com.android.annotations.NonNull
import com.android.annotations.Nullable
import com.android.build.gradle.api.AndroidSourceSet
import com.android.build.gradle.api.BaseVariant
import com.android.build.gradle.api.TestVariant
import com.android.build.gradle.api.VariantFilter
import com.android.build.gradle.internal.CompileOptions
import com.android.build.gradle.internal.ExtraModelInfo
import com.android.build.gradle.internal.LoggingUtil
import com.android.build.gradle.internal.SdkHandler
import com.android.build.gradle.internal.SourceSetSourceProviderWrapper
import com.android.build.gradle.internal.coverage.JacocoExtension
import com.android.build.gradle.internal.dsl.AaptOptions
import com.android.build.gradle.internal.dsl.AdbOptions
import com.android.build.gradle.internal.dsl.AndroidSourceSetFactory
import com.android.build.gradle.internal.dsl.BuildType
import com.android.build.gradle.internal.dsl.DexOptions
import com.android.build.gradle.internal.dsl.GroupableProductFlavor
import com.android.build.gradle.internal.dsl.LintOptions
import com.android.build.gradle.internal.dsl.PackagingOptions
import com.android.build.gradle.internal.dsl.ProductFlavor
import com.android.build.gradle.internal.dsl.SigningConfig
import com.android.build.gradle.internal.dsl.Splits
import com.android.build.gradle.internal.dsl.TestOptions
import com.android.builder.core.AndroidBuilder
import com.android.builder.core.BuilderConstants
import com.android.builder.model.SourceProvider
import com.android.builder.sdk.TargetInfo
import com.android.builder.testing.api.DeviceProvider
import com.android.builder.testing.api.TestServer
import com.android.sdklib.repository.FullRevision
import com.google.common.collect.Lists
import groovy.transform.CompileStatic
import org.gradle.api.Action
import org.gradle.api.GradleException
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.ConfigurationContainer
import org.gradle.api.internal.project.ProjectInternal
import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging
import org.gradle.api.tasks.SourceSet
import org.gradle.internal.reflect.Instantiator /**
* Base 'android' extension for all android plugins.
*
* <p>This is never used directly. Instead,
*<ul>
* <li>Plugin <code>com.android.application</code> uses {@link AppExtension}</li>
* <li>Plugin <code>com.android.library</code> uses {@link LibraryExtension}</li>
* </ul>
*/
public abstract class BaseExtension { private String target
private FullRevision buildToolsRevision /** Default config, shared by all flavors. */
final ProductFlavor defaultConfig /** Options for aapt, tool for packaging resources. */
final AaptOptions aaptOptions /** Lint options. */
final LintOptions lintOptions /** Dex options. */
final DexOptions dexOptions /** Options for running tests. */
final TestOptions testOptions /** Compile options */
final CompileOptions compileOptions /** Packaging options. */
final PackagingOptions packagingOptions /** JaCoCo options. */
final JacocoExtension jacoco /**
* APK splits options.
*
* <p>See <a href="http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits">APK Splits</a>.
*/
final Splits splits /** All product flavors used by this project. */
final NamedDomainObjectContainer<GroupableProductFlavor> productFlavors /** Build types used by this project. */
final NamedDomainObjectContainer<BuildType> buildTypes /** Signing configs used by this project. */
final NamedDomainObjectContainer<SigningConfig> signingConfigs private ExtraModelInfo extraModelInfo protected Project project /** Adb options */
final AdbOptions adbOptions; /** A prefix to be used when creating new resources. Used by Studio */
String resourcePrefix List<String> flavorDimensionList private String defaultPublishConfig = "release"
private boolean publishNonDefault = false private Closure<Void> variantFilter private final List<DeviceProvider> deviceProviderList = Lists.newArrayList();
private final List<TestServer> testServerList = Lists.newArrayList(); private final AndroidBuilder androidBuilder private final SdkHandler sdkHandler protected Logger logger private boolean isWritable = true; /**
* The source sets container.
*/
final NamedDomainObjectContainer<AndroidSourceSet> sourceSetsContainer BaseExtension(
@NonNull ProjectInternal project,
@NonNull Instantiator instantiator,
@NonNull AndroidBuilder androidBuilder,
@NonNull SdkHandler sdkHandler,
@NonNull NamedDomainObjectContainer<BuildType> buildTypes,
@NonNull NamedDomainObjectContainer<GroupableProductFlavor> productFlavors,
@NonNull NamedDomainObjectContainer<SigningConfig> signingConfigs,
@NonNull ExtraModelInfo extraModelInfo,
boolean isLibrary) {
this.androidBuilder = androidBuilder
this.sdkHandler = sdkHandler
this.buildTypes = buildTypes
this.productFlavors = productFlavors
this.signingConfigs = signingConfigs
this.extraModelInfo = extraModelInfo
this.project = project logger = Logging.getLogger(this.class) defaultConfig = instantiator.newInstance(ProductFlavor, BuilderConstants.MAIN,
project, instantiator, project.getLogger()) aaptOptions = instantiator.newInstance(AaptOptions)
dexOptions = instantiator.newInstance(DexOptions)
lintOptions = instantiator.newInstance(LintOptions)
testOptions = instantiator.newInstance(TestOptions)
compileOptions = instantiator.newInstance(CompileOptions)
packagingOptions = instantiator.newInstance(PackagingOptions)
jacoco = instantiator.newInstance(JacocoExtension)
adbOptions = instantiator.newInstance(AdbOptions)
splits = instantiator.newInstance(Splits, instantiator) sourceSetsContainer = project.container(AndroidSourceSet,
new AndroidSourceSetFactory(instantiator, project, isLibrary)) sourceSetsContainer.whenObjectAdded { AndroidSourceSet sourceSet ->
ConfigurationContainer configurations = project.getConfigurations() createConfiguration(
configurations,
sourceSet.getCompileConfigurationName(),
"Classpath for compiling the ${sourceSet.name} sources.") String packageConfigDescription
if (isLibrary) {
packageConfigDescription = "Classpath only used when publishing '${sourceSet.name}'."
} else {
packageConfigDescription = "Classpath packaged with the compiled '${sourceSet.name}' classes."
}
createConfiguration(
configurations,
sourceSet.getPackageConfigurationName(),
packageConfigDescription) createConfiguration(
configurations,
sourceSet.getProvidedConfigurationName(),
"Classpath for only compiling the ${sourceSet.name} sources.") createConfiguration(
configurations,
sourceSet.getWearAppConfigurationName(),
"Link to a wear app to embed for object '${sourceSet.name}'.") sourceSet.setRoot(String.format("src/%s", sourceSet.getName()))
} sourceSetsContainer.create(defaultConfig.name)
} /**
* Disallow further modification on the extension.
*/
public void disableWrite() {
isWritable = false
} protected checkWritability() {
if (!isWritable) {
throw new GradleException(
"Android tasks have already been created.\n" +
"This happens when calling android.applicationVariants,\n" +
"android.libraryVariants or android.testVariants.\n" +
"Once these methods are called, it is not possible to\n" +
"continue configuring the model.")
}
} protected void createConfiguration(
@NonNull ConfigurationContainer configurations,
@NonNull String configurationName,
@NonNull String configurationDescription) {
logger.info("Creating configuration ${configurationName}.") Configuration configuration = configurations.findByName(configurationName)
if (configuration == null) {
configuration = configurations.create(configurationName)
}
configuration.setVisible(false);
configuration.setDescription(configurationDescription)
} /**
* Sets the compile SDK version, based on full SDK version string, e.g.
* <code>android-21</code> for Lollipop.
*/
void compileSdkVersion(String version) {
checkWritability()
this.target = version
} /**
* Sets the compile SDK version, based on API level, e.g. 21 for Lollipop.
*/
void compileSdkVersion(int apiLevel) {
compileSdkVersion("android-" + apiLevel)
} void setCompileSdkVersion(int apiLevel) {
compileSdkVersion(apiLevel)
} void setCompileSdkVersion(String target) {
compileSdkVersion(target)
} void buildToolsVersion(String version) {
checkWritability()
buildToolsRevision = FullRevision.parseRevision(version)
} /**
* <strong>Required.</strong> Version of the build tools to use.
*
* <p>Value assigned to this property is parsed and stored in a normalized form, so reading it
* back may give a slightly different string.
*/
String getBuildToolsVersion() {
return buildToolsRevision.toString()
} void setBuildToolsVersion(String version) {
buildToolsVersion(version)
} /**
* Configures the build types.
*/
void buildTypes(Action<? super NamedDomainObjectContainer<BuildType>> action) {
checkWritability()
action.execute(buildTypes)
} /**
* Configures the product flavors.
*/
void productFlavors(Action<? super NamedDomainObjectContainer<GroupableProductFlavor>> action) {
checkWritability()
action.execute(productFlavors)
} /**
* Configures the signing configs.
*/
void signingConfigs(Action<? super NamedDomainObjectContainer<SigningConfig>> action) {
checkWritability()
action.execute(signingConfigs)
} public void flavorDimensions(String... dimensions) {
checkWritability()
flavorDimensionList = Arrays.asList(dimensions)
} /**
* Configures the source sets. Note that the Android plugin uses its own implementation of
* source sets, {@link AndroidSourceSet}.
*/
void sourceSets(Action<NamedDomainObjectContainer<AndroidSourceSet>> action) {
checkWritability()
action.execute(sourceSetsContainer)
} /**
* All source sets. Note that the Android plugin uses its own implementation of
* source sets, {@link AndroidSourceSet}.
*/
NamedDomainObjectContainer<AndroidSourceSet> getSourceSets() {
sourceSetsContainer
} /**
* The default configuration, inherited by all build flavors (if any are defined).
*/
void defaultConfig(Action<ProductFlavor> action) {
checkWritability()
action.execute(defaultConfig)
} /**
* Configures aapt options.
*/
void aaptOptions(Action<AaptOptions> action) {
checkWritability()
action.execute(aaptOptions)
} /**
* Configures dex options.
* @param action
*/
void dexOptions(Action<DexOptions> action) {
checkWritability()
action.execute(dexOptions)
} /**
* Configure lint options.
*/
void lintOptions(Action<LintOptions> action) {
checkWritability()
action.execute(lintOptions)
} /** Configures the test options. */
void testOptions(Action<TestOptions> action) {
checkWritability()
action.execute(testOptions)
} /**
* Configures compile options.
*/
void compileOptions(Action<CompileOptions> action) {
checkWritability()
action.execute(compileOptions)
} /**
* Configures packaging options.
*/
void packagingOptions(Action<PackagingOptions> action) {
checkWritability()
action.execute(packagingOptions)
} /**
* Configures JaCoCo options.
*/
void jacoco(Action<JacocoExtension> action) {
checkWritability()
action.execute(jacoco)
} /**
* Configures adb options.
*/
void adbOptions(Action<AdbOptions> action) {
checkWritability()
action.execute(adbOptions)
} /**
* Configures APK splits.
*/
void splits(Action<Splits> action) {
checkWritability()
action.execute(splits)
} void deviceProvider(DeviceProvider deviceProvider) {
checkWritability()
deviceProviderList.add(deviceProvider)
} @NonNull
List<DeviceProvider> getDeviceProviders() {
return deviceProviderList
} void testServer(TestServer testServer) {
checkWritability()
testServerList.add(testServer)
} @NonNull
List<TestServer> getTestServers() {
return testServerList
} public void defaultPublishConfig(String value) {
setDefaultPublishConfig(value)
} public void publishNonDefault(boolean value) {
publishNonDefault = value
} /**
* Name of the configuration used to build the default artifact of this project.
*
* <p>See <a href="http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Referencing-a-Library">
* Referencing a Library</a>
*/
public String getDefaultPublishConfig() {
return defaultPublishConfig
} public void setDefaultPublishConfig(String value) {
defaultPublishConfig = value
} /**
* Whether to publish artifacts for all configurations, not just the default one.
*
* <p>See <a href="http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Referencing-a-Library">
* Referencing a Library</a>
*/
public boolean getPublishNonDefault() {
return publishNonDefault
} void variantFilter(Closure<Void> filter) {
setVariantFilter(filter)
} void setVariantFilter(Closure<Void> filter) {
variantFilter = filter
} /**
* A variant filter to control which variants are excluded.
* <p>The filter is a closure which is passed a single object of type
* {@link com.android.build.gradle.internal.api.VariantFilter}. It should set the
* {@link VariantFilter#setIgnore(boolean)} flag to filter out the given variant.
*/
public Closure<Void> getVariantFilter() {
return variantFilter;
} void resourcePrefix(String prefix) {
resourcePrefix = prefix
} abstract void addVariant(BaseVariant variant) public void registerArtifactType(@NonNull String name,
boolean isTest,
int artifactType) {
extraModelInfo.registerArtifactType(name, isTest, artifactType)
} public void registerBuildTypeSourceProvider(
@NonNull String name,
@NonNull BuildType buildType,
@NonNull SourceProvider sourceProvider) {
extraModelInfo.registerBuildTypeSourceProvider(name, buildType, sourceProvider)
} public void registerProductFlavorSourceProvider(
@NonNull String name,
@NonNull ProductFlavor productFlavor,
@NonNull SourceProvider sourceProvider) {
extraModelInfo.registerProductFlavorSourceProvider(name, productFlavor, sourceProvider)
} @CompileStatic
public void registerJavaArtifact(
@NonNull String name,
@NonNull BaseVariant variant,
@NonNull String assembleTaskName,
@NonNull String javaCompileTaskName,
@NonNull Collection<File> generatedSourceFolders,
@NonNull Iterable<String> ideSetupTaskNames,
@NonNull Configuration configuration,
@NonNull File classesFolder,
@NonNull File javaResourceFolder,
@Nullable SourceProvider sourceProvider) {
extraModelInfo.registerJavaArtifact(name, variant, assembleTaskName,
javaCompileTaskName, generatedSourceFolders, ideSetupTaskNames,
configuration, classesFolder, javaResourceFolder, sourceProvider)
} public void registerMultiFlavorSourceProvider(
@NonNull String name,
@NonNull String flavorName,
@NonNull SourceProvider sourceProvider) {
extraModelInfo.registerMultiFlavorSourceProvider(name, flavorName, sourceProvider)
} @NonNull
public SourceProvider wrapJavaSourceSet(@NonNull SourceSet sourceSet) {
return new SourceSetSourceProviderWrapper(sourceSet)
} /**
* <strong>Required.</strong> Compile SDK version.
*
* <p>Your code will be compiled against the android.jar from this API level. You should
* generally use the most up-to-date SDK version here. Use the Lint tool to make sure you don't
* use APIs not available in earlier platform version without checking.
*
* <p>Setter can be called with a string like "android-21" or a number.
*
* <p>Value assigned to this property is parsed and stored in a normalized form, so reading it
* back may give a slightly different string.
*/
public String getCompileSdkVersion() {
return target
} public FullRevision getBuildToolsRevision() {
return buildToolsRevision
} public File getSdkDirectory() {
return sdkHandler.getSdkFolder()
} public File getNdkDirectory() {
return sdkHandler.getNdkFolder()
} public List<File> getBootClasspath() {
ensureTargetSetup()
return androidBuilder.getBootClasspath()
} public File getAdbExe() {
return sdkHandler.getSdkInfo().adb
} public File getDefaultProguardFile(String name) {
File sdkDir = sdkHandler.getAndCheckSdkFolder()
return new File(sdkDir,
SdkConstants.FD_TOOLS + File.separatorChar
+ SdkConstants.FD_PROGUARD + File.separatorChar
+ name);
} // ---------------
// TEMP for compatibility
// STOPSHIP Remove in 1.0 // by default, we do not generate pure splits
boolean generatePureSplits = false; void generatePureSplits(boolean flag) {
if (flag) {
logger.warn("Pure splits are not supported by PlayStore yet.")
}
this.generatePureSplits = flag;
} private boolean enforceUniquePackageName = true public void enforceUniquePackageName(boolean value) {
if (!value) {
LoggingUtil.displayDeprecationWarning(logger, project, "Support for libraries with same package name is deprecated and will be removed in 1.0")
}
enforceUniquePackageName = value
} public void setEnforceUniquePackageName(boolean value) {
enforceUniquePackageName(value)
} public getEnforceUniquePackageName() {
return enforceUniquePackageName
} private void ensureTargetSetup() {
// check if the target has been set.
TargetInfo targetInfo = androidBuilder.getTargetInfo()
if (targetInfo == null) {
sdkHandler.initTarget(
getCompileSdkVersion(),
buildToolsRevision,
androidBuilder)
}
}
}
在整个android app的编译流程用的工具,在这个类中基本都可以设置属性,设置的方式当然是我们一直用的闭包。随便举个例子,我们想设置下apk签名,工具当然是jarSign,搜索下jarSign,没有,换个sign,就能找到import com.android.build.gradle.internal.dsl.SigningConfig,进入SigningConfig类,所有和签名有关的代码一目了然了。用闭包的方法设置就是:
android{
signingConfigs {
releaseConfig {
keyAlias 'stone'
keyPassword 'mypwd'
storeFile file('/Users/stone/Documents/project_AS/myapplication/stone.keystore')
storePassword 'mypwd'
}
}
}
建议把BaseExtension的代码大致浏览下,基本的编译设置都能找到。
我认为学习技术开始无需太过关注细节,主流程通了,实现技术了然于心,下面的事查下帮助文档就可以了。额,贴个帮助文档的地址把:
http://avatarqing.github.io/Gradle-Plugin-User-Guide-Chinese-Verision/
第三篇:gradle 编译 Android app 概览的更多相关文章
- 深入理解gradle编译-Android基础篇
深入理解gradle编译-Android基础篇 导读 Gradle基于Groovy的特定领域语言(DSL)编写的一种自动化建构工具,Groovy作为一种高级语言由Java代码实现,本文将对Gradle ...
- [置顶] android利用jni调用第三方库——第三篇——编写库android程序整合第三方库libhello.so到自己的库libhelloword.so
0:前言: 在第二篇中,我们主要介绍了丙方android公司利用乙方C++公司给的动态库,直接调用库中的方法,但是这样方式受限于: 乙方C++公司开发的动态库是否符合jni的规范,如果不规范,则不能直 ...
- 《Android进阶》之第三篇 深入理解android的消息处理机制
Android 异步消息处理机制 让你深入理解 Looper.Handler.Message三者关系 android的消息处理机制(图+源码分析)——Looper,Handler,Message an ...
- 第四篇:Eclipse Android app 工程迁移到 Android Studio
前言:这种问题当然在所难免,所幸android studio的project 工程目录远比 Eclipse 要了然. 目录对比 我们在Eclipse中创建一个EclipseDemo的Android项目 ...
- 【我的Android进阶之旅】解决Center OS 64位系统编译Android APP报错error=2和finished with non-zero exit value 127
一.错误描述 1.问题 java.io.IOException: error=2, 没有那个文件或目录 今天在刚重新搭建好的64位的Center OS上安装好了Android SDK,Jenkins, ...
- [翻译]Go与C#对比 第三篇:编译、运行时、类型系统、模块和其它的一切
Go vs C#, Part 3: Compiler, Runtime, Type System, Modules, and Everything Else | by Alex Yakunin | S ...
- 【第三篇】学习 android 事件总线androidEventbus之发布事件,子线程中接收
发送和接收消息的方式类似其他的发送和接收消息的事件总线一样,不同的点或者应该注意的地方: 1,比如在子线程构造方法里面进行实现总线的注册操作: 2,要想子线程中接收消息的功能执行,必须启动线程. 3, ...
- 【第三篇】学习 android 事件总线androidEventbus之list数据事件的传递,发送list数据事件到另外一个Activity
这个和普通的事件总线的发送接收一样. package com.example.mysimpleeventbus; import java.util.ArrayList; import java.uti ...
- Gradle for Android 第三篇( 依赖管理 )
依赖管理 这会是一个系列,所以如果你还没有看我之前的几篇文章,请先查看以下文章: Gradle for Android 第一篇( 从 Gradle 和 AS 开始 ) Gradle for Andro ...
随机推荐
- POJ -- 3233 求“等比矩阵”前n(n <=10^9)项和
Matrix Power Series Description Given a n × n matrix A and a positive integer k, find the sum S = ...
- [摘]selenium-ide编辑命令
----//编辑命令 selenium为我们录制的脚本不是100%符合我们的需求的,所以,编辑录制的脚本是必不可少的工作. 1. 编辑一行命令或注释. 在Table标签下选中某一行命令,命令由com ...
- bzoj 2285 [Sdoi2011]保密(二分,spfa + 最大流)
Description 现在,保密成为一个很重要也很困难的问题.如果没有做好,后果是严重的.比如,有个人没有自己去修电脑,又没有拆硬盘,后来的事大家都知道了. 当然,对保密最需求的当然是军方,其次才是 ...
- HDU 1117 免费馅饼 二维动态规划
思路:a[i][j]表示j秒在i位置的数目,dp[i][j]表示j秒在i位置最大可以收到的数目. 转移方程:d[i][j]=max(dp[i-1][j],dp[i-1][j-1],dp[i-1][j+ ...
- 判断是否已安装.net framework
1.检测 %SystemRoot%\System 目录下的MSCorEE.dll文件,如果存在,则表明.net framework 已安装. 2.检测一下注册表项的子项: KEY_LOCAL_MACH ...
- 获取所有组合算法、获取全排列算法(java)
转载声明:原文转自:http://www.cnblogs.com/xiezie/p/5574516.html 受到ACM1015的影响,个人感觉,有必要对统计学上的 全组合和全排列 进行一个简单的总结 ...
- 15个实用的Linux find命令示例
妈咪,我找到了! -- 15个实用的Linux find命令示例 http://www.oschina.net/translate/15-practical-linux-find-command-ex ...
- Kooboo中主要的几个关键词中的关系
Kooboo中主要的几个关键词中的关系 Content Type //相当于数据库表 Content //相当于数据 View //部分View 她可以使用Content ...
- hdoj 1465 不容易系列之一
转 原文网址 http://blog.csdn.net/liwen_7/article/details/7646451 错排问题 错排问题 就是一种递推式,不过它比较著名且常用,所以要熟记! 方法 ...
- 在COM接口中不要使用同时出现只是大小写不同的名字作为属性名、函数名或者参数名
博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:在COM接口中不要使用同时出现只是大小写不同的名字作为属性名.函数名或者参数名.