背景:
Android Gradle plugin 3.0开始(对应Gradle版本 4.1及以上),原有的依赖配置类型compile已经被废弃,开始使用implementationapiannotationProcessor类型分别替代。对应的,这三种替代配置类型针对具体的使用场景,具有不同的依赖行为。其中,implementationapi依赖又相对最为常用,对其具体含义也需要理解清,在实际项目中选择依赖配置时,也才能游刃有余。

首先看一下Android官方文档中关于依赖配置的详细介绍:Add build dependencies


为陈述方便且不容易理解歧义,先划分出如下几个术语。

  • 被引入的依赖模块,简称 依赖模块
  • 引入了被依赖模块的当前模块,简称 当前模块
  • 依赖了当前模块的上层模块,简称 其他上层模块

于是,官方文档中的描述翻译后对应的含义为:
1,implementation
此依赖配置,使Gradle意识到,当前模块引入的依赖模块,在编译期间对其他上层模块不可见,仅在运行时对其他上层模块可见。这将会加快多模块依赖的项目整体编译速度,因为通过implementation引入的依赖模块,如果依赖模块内部有进行过Api的改动,由于其对其他上层模块不可见,因此只需重新编译依赖模块自身以及使用到此改动的Api的当前模块即可。

2, api
等同于原有的compile,此依赖配置,使Gradle意识到,其引入的依赖模块,无论在编译期还是在运行时,都对其他上层模块可见,即通过api配置引入的依赖模块,在依赖关系上具有传递性。这将会使得其他上层模块可以直接使用依赖模块的Api,但这同时意味着一旦依赖模块发生Api的改动,将导致所有已经使用了依赖模块改动了的Api的上层模块都需要重新执行编译。因此,一般会加大整个项目的编译时间,如非必要,一般建议优先使用implementation依赖配置。

如此描述一般情况下还不是很容易理解。描述中最关键的几个词有:可见性依赖传递编译期运行时,和 使Gradle意识到

下面先通过一个具体的例子感性认识下implementationapi 两者的区别。
新建一个项目HappyCorn,具体项目结构如下:

  1. Root project 'HappyCorn'
  2. +--- Project ':app'
  3. +--- Project ':LibA'
  4. +--- Project ':LibB'
  5. +--- Project ':LibC'
  6. \--- Project ':LibD'
  7. 复制代码

其中,app为application类型,LibA、LibB、LibC、LibD分别是四个library类型。
LibA中新建如下类:

  1. package com.happycorn.librarya;
  2. public class LibAClass {
  3. public static String getName() {
  4. return "Library A";
  5. }
  6. }
  7. 复制代码

同样的,LibB中:

  1. package com.happycorn.libraryb;
  2. public class LibBClass {
  3. public static String getName() {
  4. return "Library B";
  5. }
  6. }
  7. 复制代码

LibC中:

  1. package com.happycorn.libraryc;
  2. public class LibCClass {
  3. public static String getName() {
  4. OkHttpClient okHttpClient = new OkHttpClient();
  5. return "Library C";
  6. }
  7. }
  8. 复制代码

LibD中:

  1. package com.happycorn.libraryd;
  2. public class LibDClass {
  3. public static String getName() {
  4. return "Library D";
  5. }
  6. }
  7. 复制代码

进行依赖配置,使得项目整体依赖类似于树形结构:

对应的依赖配置分别如下:

  • :app依赖(implementationapi):LibA和:LibB
  • :LibA implementation 依赖:LibC
  • :LibB api 依赖:LibD

执行graldew命令,查看:app对那些进行了依赖:

  1. ./gradlew :app::dependencies
  2. 复制代码

输出结果为(单元测试等不太相干信息先去掉):

  1. ...
  2. debugCompileClasspath - Resolved configuration for compilation for variant: debug
  3. +--- project :LibA
  4. \--- project :LibB
  5. \--- project :LibD
  6. debugRuntimeClasspath - Resolved configuration for runtime for variant: debug
  7. +--- project :LibA
  8. | \--- project :LibC
  9. \--- project :LibB
  10. \--- project :LibD
  11. releaseCompileClasspath - Resolved configuration for compilation for variant: release
  12. +--- project :LibA
  13. \--- project :LibB
  14. \--- project :LibD
  15. releaseRuntimeClasspath - Resolved configuration for runtime for variant: release
  16. +--- project :LibA
  17. | \--- project :LibC
  18. \--- project :LibB
  19. \--- project :LibD
  20. ...
  21. 复制代码

从输出信息中可以看出,无论是debug还是release变体,在编译时与运行时所依赖的依赖模块是不同的。对于:LibC,在编译时对:app不可见,但在运行时对:app是可见的。

执行./gradlew :LibA:dependencies,确认下:LibA的依赖。 对应输出结果:

  1. debugCompileClasspath - Resolved configuration for compilation for variant: debug
  2. \--- project :LibC
  3. debugRuntimeClasspath - Resolved configuration for runtime for variant: debug
  4. \--- project :LibC
  5. releaseCompileClasspath - Resolved configuration for compilation for variant: release
  6. \--- project :LibC
  7. releaseRuntimeClasspath - Resolved configuration for runtime for variant: release
  8. \--- project :LibC
  9. 复制代码

可见,:LibA确实已经依赖了:LibC。

进一步,如果此时在:app中分别调用:LibA、:LibB、:LibC、:LibD模块的Api,发现:app中是无法直接调用:LibC的方法的。

因此,可以证实,通过 implementation引入的依赖模块,在编译期对其他上层模块是不可见的,对应的依赖关系不具有传递性。

接下来继续看依赖关系与模块编译之间的关系。 先执行命令清理掉历史构建结果:

  1. ./gradlew clean
  2. 复制代码

执行build task assembleDebug 或 :app:compileDebugJavaWithJavac:

  1. ./gradlew :app:compileDebugJavaWithJavac --info
  2. 复制代码

编译成功,其中,关键信息输出记录为:

  1. :LibC:compileDebugJavaWithJavac (Thread[Task worker for ':' Thread 3,5,main]) started.
  2. > Task :LibC:compileDebugJavaWithJavac
  3. ...
  4. Compiling with JDK Java compiler API.
  5. Class dependency analysis for incremental compilation took 0.003 secs.
  6. Created jar classpath snapshot for incremental compilation in 0.0 secs.
  7. Written jar classpath snapshot for incremental compilation in 0.0 secs.
  8. :LibC:compileDebugJavaWithJavac (Thread[Task worker for ':' Thread 3,5,main]) completed. Took 0.023 secs.
  9. ...
  10. :LibA:compileDebugJavaWithJavac (Thread[Task worker for ':' Thread 3,5,main]) started.
  11. > Task :LibA:compileDebugJavaWithJavac
  12. ...
  13. Compiling with JDK Java compiler API.
  14. Class dependency analysis for incremental compilation took 0.001 secs.
  15. Created jar classpath snapshot for incremental compilation in 0.001 secs.
  16. Written jar classpath snapshot for incremental compilation in 0.0 secs.
  17. :LibA:compileDebugJavaWithJavac (Thread[Task worker for ':' Thread 3,5,main]) completed. Took 0.024 secs.
  18. ...
  19. :LibD:compileDebugJavaWithJavac (Thread[Task worker for ':' Thread 3,5,main]) started.
  20. > Task :LibD:compileDebugJavaWithJavac
  21. ...
  22. Compiling with JDK Java compiler API.
  23. Class dependency analysis for incremental compilation took 0.0 secs.
  24. Created jar classpath snapshot for incremental compilation in 0.0 secs.
  25. Written jar classpath snapshot for incremental compilation in 0.0 secs.
  26. :LibD:compileDebugJavaWithJavac (Thread[Task worker for ':' Thread 3,5,main]) completed. Took 0.018 secs.
  27. ...
  28. :LibB:compileDebugJavaWithJavac (Thread[Task worker for ':' Thread 3,5,main]) started.
  29. > Task :LibB:compileDebugJavaWithJavac
  30. ...
  31. Compiling with JDK Java compiler API.
  32. Class dependency analysis for incremental compilation took 0.002 secs.
  33. Created jar classpath snapshot for incremental compilation in 0.0 secs.
  34. Written jar classpath snapshot for incremental compilation in 0.0 secs.
  35. :LibB:compileDebugJavaWithJavac (Thread[Task worker for ':' Thread 3,5,main]) completed. Took 0.033 secs.
  36. ...
  37. :app:compileDebugJavaWithJavac (Thread[Task worker for ':' Thread 3,5,main]) started.
  38. > Task :app:compileDebugJavaWithJavac
  39. ...
  40. Compiling with JDK Java compiler API.
  41. Class dependency analysis for incremental compilation took 0.004 secs.
  42. Created jar classpath snapshot for incremental compilation in 0.0 secs.
  43. Written jar classpath snapshot for incremental compilation in 0.0 secs.
  44. :app:compileDebugJavaWithJavac (Thread[Task worker for ':' Thread 3,5,main]) completed. Took 0.099 secs.
  45. ...
  46. 复制代码

每个模块都进行了对应的compile过程。且对应的顺序为:LibC >> :LibA >> :LibD >> :LibB >> :app

再次执行build task compileDebugJavaWithJavac:

  1. ./gradlew :app:compileDebugJavaWithJavac --info
  2. 复制代码

编译成功,此时,关键信息输出记录为:

  1. :LibC:compileDebugJavaWithJavac (Thread[Task worker for ':' Thread 2,5,main]) started.
  2. > Task :LibC:compileDebugJavaWithJavac UP-TO-DATE
  3. Skipping task ':LibC:compileDebugJavaWithJavac' as it is up-to-date.
  4. :LibC:compileDebugJavaWithJavac (Thread[Task worker for ':' Thread 2,5,main]) completed. Took 0.003 secs.
  5. ...
  6. :LibA:compileDebugJavaWithJavac (Thread[Task worker for ':' Thread 2,5,main]) started.
  7. > Task :LibA:compileDebugJavaWithJavac UP-TO-DATE
  8. Skipping task ':LibA:compileDebugJavaWithJavac' as it is up-to-date.
  9. :LibA:compileDebugJavaWithJavac (Thread[Task worker for ':' Thread 2,5,main]) completed. Took 0.003 secs.
  10. ...
  11. :LibD:compileDebugJavaWithJavac (Thread[Task worker for ':' Thread 2,5,main]) started.
  12. > Task :LibD:compileDebugJavaWithJavac UP-TO-DATE
  13. Skipping task ':LibD:compileDebugJavaWithJavac' as it is up-to-date.
  14. :LibD:compileDebugJavaWithJavac (Thread[Task worker for ':' Thread 2,5,main]) completed. Took 0.002 secs.
  15. ...
  16. :LibB:compileDebugJavaWithJavac (Thread[Task worker for ':' Thread 2,5,main]) started.
  17. > Task :LibB:compileDebugJavaWithJavac UP-TO-DATE
  18. Skipping task ':LibB:compileDebugJavaWithJavac' as it is up-to-date.
  19. :LibB:compileDebugJavaWithJavac (Thread[Task worker for ':' Thread 2,5,main]) completed. Took 0.003 secs.
  20. ...
  21. :app:compileDebugJavaWithJavac (Thread[Task worker for ':' Thread 2,5,main]) started.
  22. > Task :app:compileDebugJavaWithJavac UP-TO-DATE
  23. Skipping task ':app:compileDebugJavaWithJavac' as it is up-to-date.
  24. :app:compileDebugJavaWithJavac (Thread[Task worker for ':' Thread 2,5,main]) completed. Took 0.024 secs.
  25. 复制代码

我们发现,对应的compileDebugJavaWithJavac task都直接Skip掉了,因为此时代码没有更新,无需重新编译。

修改:libD中LibDClass类中的代码,先修改方法内的代码:

  1. public class LibDClass {
  2. public static String getName() {
  3. return "Library D ... change code";
  4. }
  5. }
  6. 复制代码

再次执行build task compileDebugJavaWithJavac:

  1. ./gradlew :app:compileDebugJavaWithJavac --info
  2. 复制代码

对应关键编译信息为:

  1. Skipping task ':LibC:compileDebugJavaWithJavac' as it is up-to-date.
  2. ...
  3. Skipping task ':LibA:compileDebugJavaWithJavac' as it is up-to-date.
  4. ...
  5. :LibB:compileDebugJavaWithJavac (Thread[Task worker for ':',5,main]) started.
  6. > Task :LibB:compileDebugJavaWithJavac UP-TO-DATE
  7. Skipping task ':LibB:compileDebugJavaWithJavac' as it is up-to-date.
  8. :LibB:compileDebugJavaWithJavac (Thread[Task worker for ':',5,main]) completed. Took 0.004 secs.
  9. ...
  10. Skipping task ':LibB:compileDebugJavaWithJavac' as it is up-to-date.
  11. ...
  12. Skipping task ':app:compileDebugJavaWithJavac' as it is up-to-date.
  13. 复制代码

我们发现,修改:LibD中的方法中的代码,task compileDebugJavaWithJavac只是重新编译了:LibD。其他模块,包括依赖此模块的各上层模块,都没有重新执行编译task。

接下来,修改:LibD中的方法名,对应如下:

  1. public class LibDClass {
  2. public static String getNewName() {
  3. return "Library D";
  4. }
  5. }
  6. 复制代码

执行:

  1. ./gradlew :app:compileDebugJavaWithJavac --info
  2. 复制代码

关键信息输出为:

  1. Skipping task ':LibC:compileDebugJavaWithJavac' as it is up-to-date.
  2. ...
  3. Skipping task ':LibA:compileDebugJavaWithJavac' as it is up-to-date.
  4. ...
  5. :LibD:compileDebugJavaWithJavac (Thread[Task worker for ':',5,main]) started.
  6. > Task :LibD:compileDebugJavaWithJavac
  7. Task ':LibD:compileDebugJavaWithJavac' is not up-to-date because:
  8. Input property 'source' file /Users/corn/AndroidStudioProjects/HappyCorn/LibD/src/main/java/com/happycorn/libraryd/LibDClass.java has changed.
  9. Compiling with source level 1.7 and target level 1.7.
  10. Created jar classpath snapshot for incremental compilation in 0.0 secs.
  11. Compiling with JDK Java compiler API.
  12. Incremental compilation of 1 classes completed in 0.008 secs.
  13. Class dependency analysis for incremental compilation took 0.001 secs.
  14. Written jar classpath snapshot for incremental compilation in 0.0 secs.
  15. :LibD:compileDebugJavaWithJavac (Thread[Task worker for ':',5,main]) completed. Took 0.013 secs.
  16. ...
  17. > Task :LibB:javaPreCompileDebug
  18. Task ':LibB:javaPreCompileDebug' is not up-to-date because:
  19. Input property 'compileClasspaths' file /Users/corn/AndroidStudioProjects/HappyCorn/LibD/build/intermediates/intermediate-jars/debug/classes.jar has changed.
  20. :LibB:javaPreCompileDebug (Thread[Task worker for ':',5,main]) completed. Took 0.002 secs.
  21. :LibB:compileDebugJavaWithJavac (Thread[Task worker for ':',5,main]) started.
  22. > Task :LibB:compileDebugJavaWithJavac UP-TO-DATE
  23. Task ':LibB:compileDebugJavaWithJavac' is not up-to-date because:
  24. Input property 'classpath' file /Users/corn/AndroidStudioProjects/HappyCorn/LibD/build/intermediates/intermediate-jars/debug/classes.jar has changed.
  25. Compiling with source level 1.7 and target level 1.7.
  26. Created jar classpath snapshot for incremental compilation in 0.0 secs.
  27. None of the classes needs to be compiled! Analysis took 0.001 secs.
  28. Written jar classpath snapshot for incremental compilation in 0.0 secs.
  29. :LibB:compileDebugJavaWithJavac (Thread[Task worker for ':',5,main]) completed. Took 0.005 secs.
  30. ...
  31. > Task :app:javaPreCompileDebug
  32. Task ':app:javaPreCompileDebug' is not up-to-date because:
  33. Input property 'compileClasspaths' file /Users/corn/AndroidStudioProjects/HappyCorn/LibD/build/intermediates/intermediate-jars/debug/classes.jar has changed.
  34. :app:javaPreCompileDebug (Thread[Task worker for ':',5,main]) completed. Took 0.002 secs.
  35. :app:compileDebugJavaWithJavac (Thread[Task worker for ':',5,main]) started.
  36. > Task :app:compileDebugJavaWithJavac UP-TO-DATE
  37. Task ':app:compileDebugJavaWithJavac' is not up-to-date because:
  38. Input property 'classpath' file /Users/corn/AndroidStudioProjects/HappyCorn/LibD/build/intermediates/intermediate-jars/debug/classes.jar has changed.
  39. Compiling with source level 1.7 and target level 1.7.
  40. Created jar classpath snapshot for incremental compilation in 0.0 secs.
  41. None of the classes needs to be compiled! Analysis took 0.0 secs.
  42. Written jar classpath snapshot for incremental compilation in 0.0 secs.
  43. :app:compileDebugJavaWithJavac (Thread[Task worker for ':',5,main]) completed. Took 0.004 secs.
  44. 复制代码

可见,此时,:LiD,:LiB,:app依次都重新进行了编译task。

新增类,新增或修改非private的对外方法名等,在api引入的方式下,都会使得上层模块重新编译,因为上层模块可能会直接用到此类方法,但在上层模块的实际编译过程中,并不会对模块内的类都进行重新编译,而是只会编译确实已经使用了依赖模块的API的类。

这也正是文档中提到的:
if an api dependency changes its external API, Gradle recompiles all modules that have access to that dependency at compile time.

同样的,我们改变:LibC中的getName()方法实现,方便编译信息跟改变:LibD中的getName()方法实现一样,其他上层模块都没有重新执行编译task。

同样的,改变:LibC中的方法名,再次执行:

  1. ./gradlew :app:compileDebugJavaWithJavac --info
  2. 复制代码

关键信息输出为:

  1. :LibC:compileDebugJavaWithJavac (Thread[Task worker for ':' Thread 3,5,main]) started.
  2. > Task :LibC:compileDebugJavaWithJavac
  3. Task ':LibC:compileDebugJavaWithJavac' is not up-to-date because:
  4. Input property 'source' file /Users/corn/AndroidStudioProjects/HappyCorn/LibC/src/main/java/com/happycorn/libraryc/LibCClass.java has changed.
  5. Compiling with source level 1.7 and target level 1.7.
  6. Created jar classpath snapshot for incremental compilation in 0.0 secs.
  7. file or directory '/Users/corn/AndroidStudioProjects/HappyCorn/LibC/src/debug/java', not found
  8. Compiling with JDK Java compiler API.
  9. Incremental compilation of 1 classes completed in 0.009 secs.
  10. Class dependency analysis for incremental compilation took 0.004 secs.
  11. Written jar classpath snapshot for incremental compilation in 0.0 secs.
  12. :LibC:compileDebugJavaWithJavac (Thread[Task worker for ':' Thread 3,5,main]) completed. Took 0.02 secs.
  13. ...
  14. > Task :LibA:javaPreCompileDebug
  15. Task ':LibA:javaPreCompileDebug' is not up-to-date because:
  16. Input property 'compileClasspaths' file /Users/corn/AndroidStudioProjects/HappyCorn/LibC/build/intermediates/intermediate-jars/debug/classes.jar has changed.
  17. :LibA:javaPreCompileDebug (Thread[Task worker for ':' Thread 3,5,main]) completed. Took 0.018 secs.
  18. :LibA:compileDebugJavaWithJavac (Thread[Task worker for ':' Thread 3,5,main]) started.
  19. > Task :LibA:compileDebugJavaWithJavac UP-TO-DATE
  20. Task ':LibA:compileDebugJavaWithJavac' is not up-to-date because:
  21. Input property 'classpath' file /Users/corn/AndroidStudioProjects/HappyCorn/LibC/build/intermediates/intermediate-jars/debug/classes.jar has changed.
  22. Compiling with source level 1.7 and target level 1.7.
  23. Created jar classpath snapshot for incremental compilation in 0.001 secs.
  24. None of the classes needs to be compiled! Analysis took 0.001 secs.
  25. Written jar classpath snapshot for incremental compilation in 0.0 secs.
  26. :LibA:compileDebugJavaWithJavac (Thread[Task worker for ':' Thread 3,5,main]) completed. Took 0.009 secs.
  27. ...
  28. > Task :LibD:javaPreCompileDebug UP-TO-DATE
  29. Skipping task ':LibD:javaPreCompileDebug' as it is up-to-date.
  30. ...
  31. > Task :LibB:compileDebugJavaWithJavac UP-TO-DATE
  32. Skipping task ':LibB:compileDebugJavaWithJavac' as it is up-to-date.
  33. ...
  34. :app:javaPreCompileDebug (Thread[Task worker for ':' Thread 3,5,main]) started.
  35. > Task :app:javaPreCompileDebug UP-TO-DATE
  36. Skipping task ':app:javaPreCompileDebug' as it is up-to-date.
  37. :app:javaPreCompileDebug (Thread[Task worker for ':' Thread 3,5,main]) completed. Took 0.008 secs.
  38. :app:compileDebugJavaWithJavac (Thread[Task worker for ':' Thread 3,5,main]) started.
  39. > Task :app:compileDebugJavaWithJavac UP-TO-DATE
  40. Skipping task ':app:compileDebugJavaWithJavac' as it is up-to-date.
  41. 复制代码

可见,此时:LibA重新执行了编译task,但:LibA的上层模块:app并没有重新执行编译task。因为:app的依赖关系在编译期并不包含:LibC相吻合。

至此,相信对implementationapi的含义已经有了一定的理解。也已经对上文中的最关键的几个词有:可见性依赖传递编译期运行时有了一定的认知。

下面继续阐述下使Gradle意识到具体含义。

实际项目开发中,对于第三方的功能模块,或者项目中抽取出去的独立的功能模块,往往形成独立的git库,进行单独的维护和管理,并生成对应的jar包或aar文件,上传到marven库。主工程中的各模块通过依赖配置去引入对应marven库上的构件。其引入的构件有时又往往通过引入了其他的marven库上的构件。此时,通过marven引入的构件内部,不论是通过implementation还是api的依赖配置去依赖了其他的marven构件,效果对于当前模块来说,都是等同的。因为implementation还是api的依赖传递关系也好,可见性也罢,都是针对当前项目的Gradle而言的。引入的marven上的构件,不论是jar包还是aar文件,都已经是通过自身编译之后的构件,其内部的依赖配置对当前项目的Gradle已经失效。

项目中引入的marven库中的构件,其内部的依赖配置对当前项目的Gradle是失效的。

例如:

  • :app api 依赖 :LibB
  • :LiB api依赖 :LibD
  • :LibD api依赖了 marven库中的构件 :LibX
  • :LibX项目内部implementation依赖了marven库中的另一构件 :LibY

此时,LibD依然可以直接使用LibY中的对外Api,也就是说,此时即使:LibX项目通过implementation引入的:LibY,但:LibY对:LibD 依然具有依赖传递,具有可见性。

此即官方文档中提及的 it's letting Gradle know that的内在含义。

总结:
implementationapi依赖配置主要是控制依赖模块对上层模块的依赖关系传递及可见性,在实际进行项目构建时,编译期和运行时,又可能具有不同的依赖传递关系。理解不同的依赖配置,对具体的编译期和运行时的依赖关系具有重要意义,也是解决依赖冲突等问题的关键。

作者:HappyCorn
链接:https://juejin.im/post/5c35df566fb9a04a01648512
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Android Gradle 依赖配置:implementation & api的更多相关文章

  1. Android Gradle 依赖方式

    Android Gradle 依赖方式有以下6种: Compile compile是对所有的build type以及favlors都会参与编译并且打包到最终的apk文件中. Provided Prov ...

  2. Gradle系列之Android Gradle高级配置

    本篇文章主要在之前学习的基础上,从实际开发的角度学习如何对 Android Gradle 来进行自定义以满足不同的开发需求,下面是 Gradle 系列的几篇文章: Gradle系列之初识Gradle ...

  3. Gradle系列之Android Gradle基础配置

    原文发于微信公众号 jzman-blog,欢迎关注交流. 通过前面几篇文章学习了 Gradle 基础知识以及 Gradle 插件相关的知识,关于 Gradle 及其插件相关知识请先阅读下面几篇文章: ...

  4. Android gradle provided、implementation等指令注意点

    其实这类文章博客网上一搜一大堆,但有些地方可能说的不太清楚(都一样的内容,抄袭太严重),这里只是做个精简的总结和一些其他地方没提到的点. 一.Android Studio 3.0开始使用了新的指令,原 ...

  5. Android gradle 相关配置

    有时候我们需要重命名输出apk文件名,在Android studio 3.0以前我们是这样写的: applicationVariants.all { variant -> variant.out ...

  6. Android Gradle 常用配置

    Gradle:multiDexEnabled之DEX 方法超过64K限制和gradle编译OOM问题解决DEX 方法超过64K限制 UNEXPECTED TOP-LEVEL EXCEPTION: co ...

  7. Android Gradle基于参数化配置实现差异化构建

    一.背景: 项目中有一些特殊的需求,如个别渠道集成腾讯bugly,个别渠道集成易观统计,不同的渠道集成不同的推送策略(如Oppo渠道优先Opush推送),不同的渠道拥有不同的第三方登录集成等等.这些需 ...

  8. 一文彻底搞清 Gradle 依赖【转】

    来源:曾是放牛娃 www.jianshu.com/p/59fd653a54d2 转自:https://mp.weixin.qq.com/s?__biz=MzA3MDMyMjkzNg==&mid ...

  9. Android Gradle 学习笔记(七):Android Gradle 插件

    我们知道Android Gradle其实就是一个Gradle的一个第三方插件,它是由Google的Android团队开发的,基于Gradle构建的,和Android Studio完美搭配.相比于旧的构 ...

随机推荐

  1. Spring Boot实战笔记(七)-- Spring高级话题(计划任务)

    一.计划任务 从Spring3.1开始,计划任务在Spring中的实现变得异常的简单.首先通过在配置类注解@EnableScheduling来开启对计划任务的支持,然后在执行计划任务的方法上注解@Sc ...

  2. Struts标签库详解【3】

    struts2标签库详解 要在jsp中使用Struts2的标志,先要指明标志的引入.通过jsp的代码的顶部加入以下的代码: <%@taglib prefix="s" uri= ...

  3. sqlserver聚合索引(clustered index) / 非聚合索引(nonclustered index)的理解

    1. 什么是聚合索引(clustered index) / 什么是非聚合索引(nonclustered index)? 可以把索引理解为一种特殊的目录.微软的SQL SERVER提供了两种索引:聚集索 ...

  4. XamarinForm Effects 调用事件

    原文地址 在Xamarin.Forms控件中实现底层多点触控跟踪. 一个effect可以定义和调用一个事件,在底层本地视图中发出信号的变化.这篇文章演示如何实现底层多点触控跟踪,以及如何生成信号触摸活 ...

  5. SSM-SpringMVC-30:SpringMVC中InitBinder的骇客级优化

     ------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 上篇博客利用initbinder做了局部的日期类型转换,但是兼容性不要,只支持yyyy-MM-dd这种,所以 ...

  6. 基于Go的websocket消息服务

    3个月没写PHP了,这是我的第一个中小型go的websocket微服务.那么问题来了,github上那么多轮子,我为什么要自己造轮子呢? Why 造轮子? 因为这样不仅能锻炼自己的技术能力,而且能帮助 ...

  7. VirtualBox报错:不能为虚拟电脑XXX打开一个新任务

    报错产生的背景 今天在这里下载了一个用于VirtualBox的Kali Linux虚拟机文件(使用VirtualBox可以直接打开使用,不用执行安装过程).但是将该文件导入到VirtualBox中之后 ...

  8. gen_server terminate与trap_exit

    不论是新手还是熟手,写gen_server时常会遇到terminate/2,有时执行,有时却不执行的困惑. 比如stackoverflow中的Handling the cleanup of the g ...

  9. syncer.go

    package ) ) ) ].Key,)) )) }

  10. 【Troywar love Maths】——莫比乌斯反演

    2816. Troywar loves Maths ★★☆   输入文件:Troy_1.in   输出文件:Troy_1.out   简单对比 时间限制:1 s   内存限制:256 MB [题目描述 ...