add Gradle 权威指南笔记二

master
Omooo 6 years ago
parent 9d55efe6ec
commit 005f33f3b4
  1. 14
      README.md
  2. 64
      blogs/Android/Framework/深入理解 Android 卷一/深入理解 JNI.md
  3. 173
      blogs/Android/Gradle/Android Gradle 权威指南/读书笔记之二.md

@ -54,17 +54,15 @@ Android Notes
《Android Gradle 权威指南读书笔记》
[基础知识相关](https://github.com/Omooo/Android-Notes/blob/master/blogs/Android/Gradle/Android%20Gradle%20%E6%9D%83%E5%A8%81%E6%8C%87%E5%8D%97/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0%E4%B9%8B%E4%B8%80.md)
- [基础知识相关](https://github.com/Omooo/Android-Notes/blob/master/blogs/Android/Gradle/Android%20Gradle%20%E6%9D%83%E5%A8%81%E6%8C%87%E5%8D%97/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0%E4%B9%8B%E4%B8%80.md)
- [进阶知识相关](https://github.com/Omooo/Android-Notes/blob/master/blogs/Android/Gradle/Android%20Gradle%20%E6%9D%83%E5%A8%81%E6%8C%87%E5%8D%97/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0%E4%B9%8B%E4%BA%8C.md)
##### JVM、ART 相关
Class 文件格式
[Class 文件格式总览](https://github.com/Omooo/Android-Notes/blob/master/blogs/JVM/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%20Class%20%E6%96%87%E4%BB%B6%E6%A0%BC%E5%BC%8F/Class%20%E6%96%87%E4%BB%B6%E6%A0%BC%E5%BC%8F%E6%80%BB%E8%A7%88.md)
[常量池及相关内容](https://github.com/Omooo/Android-Notes/blob/master/blogs/JVM/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%20Class%20%E6%96%87%E4%BB%B6%E6%A0%BC%E5%BC%8F/%E5%B8%B8%E9%87%8F%E6%B1%A0%E5%8F%8A%E7%9B%B8%E5%85%B3%E5%86%85%E5%AE%B9.md)
[属性介绍]
- Class 文件格式
- [Class 文件格式总览](https://github.com/Omooo/Android-Notes/blob/master/blogs/JVM/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%20Class%20%E6%96%87%E4%BB%B6%E6%A0%BC%E5%BC%8F/Class%20%E6%96%87%E4%BB%B6%E6%A0%BC%E5%BC%8F%E6%80%BB%E8%A7%88.md)
- [常量池及相关内容](https://github.com/Omooo/Android-Notes/blob/master/blogs/JVM/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%20Class%20%E6%96%87%E4%BB%B6%E6%A0%BC%E5%BC%8F/%E5%B8%B8%E9%87%8F%E6%B1%A0%E5%8F%8A%E7%9B%B8%E5%85%B3%E5%86%85%E5%AE%B9.md)
- [属性介绍]
##### 性能优化

@ -57,63 +57,39 @@ public class MediaScanner implements AutoCloseable {
看下 android.media.MediaScanner.cpp 源码:
```c++
//这个函数是 native_init 的 JNI 层实现
static void
android_media_MediaScanner_native_init(JNIEnv *env)
{
ALOGV("native_init");
jclass clazz = env->FindClass(kClassMediaScanner);
if (clazz == NULL) {
return;
}
fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
if (fields.context == NULL) {
return;
}
//...
}
//这个函数是 processFile 的 JNI 层实现
static jboolean
android_media_MediaScanner_processFile(
JNIEnv *env, jobject thiz, jstring path,
jstring mimeType, jobject client)
{
ALOGV("processFile");
//...
}
```
// Lock already hold by processDirectory
MediaScanner *mp = getNativeScanner_l(env, thiz);
if (mp == NULL) {
jniThrowException(env, kRunTimeException, "No scanner available");
return false;
}
Java 层,native_init 函数位于 android.media 包中,它的全路径名应该是 android.media.MediaScanner.native_init,而 JNI 层函数的名字就是把 "." 替换成了 "_",通过这样,就可以把 Java 中的 Native 函数和 JNI 层的函数关联起来了。
if (path == NULL) {
jniThrowException(env, kIllegalArgumentException, NULL);
return false;
}
其实上面说的就是 JNI 函数的注册问题。注册,即将 Java 层的 native 函数和 JNI 层对应的实现函数关联起来。JNI 函数的注册方法实际上有以下两种:
const char *pathStr = env->GetStringUTFChars(path, NULL);
if (pathStr == NULL) { // Out of memory
return false;
}
1. 静态注册
const char *mimeTypeStr =
(mimeType ? env->GetStringUTFChars(mimeType, NULL) : NULL);
if (mimeType && mimeTypeStr == NULL) { // Out of memory
// ReleaseStringUTFChars can be called with an exception pending.
env->ReleaseStringUTFChars(path, pathStr);
return false;
}
以这种注册方式就是根据函数名来找对应的 JNI 函数,它需要 Java 的工具程序 javah 参与。javah 会根据 class 文件生成一个叫 output.h 的 JNI 层头文件,在生成的 output.h 文件里,声明了对应的 JNI 层函数,只要实现里面的函数即可。
这个头文件的名字一般都会使用 packagename_class.h 的样式,例如 MediaScanner 对应的 JNI 层头文件就是 android_media_MediaScanner.h。
当 Java 层调用 native_init 函数时,它会从对应的 JNI 库中寻找 Java_android_media_MediaScanner_native_init 函数,如果没有,就会报错。如果找到,则会把这个 native_init 和 Java_android_media_MediaScanner_native_init 建立一个关联关系,其实就是保存 JNI 层函数的函数指针。以后在调用 native_init 函数时,直接使用这个函数指针就可以了,当然这项工作是由虚拟机完成的。
这样做会很麻烦,初次调用 native 函数时还要根据函数名搜索对应的 JNI 层函数来建立关联关系,这样会影响运行效率。根据上面介绍可知,Java native 函数是通过函数指针来和 JNI 层函数建立关联关系的,如果直接让 native 函数知道 JNI 层对应函数的函数指针,不就万事大吉啦吗?这就要介绍动态注册了。
2. 动态注册
Java native 函数和 JNI 函数是一一对应的,在 JNI 中,用一个叫 JNINativeMethod 的结构来保存这种关联关系。
MyMediaScannerClient myClient(env, client);
MediaScanResult result = mp->processFile(pathStr, mimeTypeStr, myClient);
if (result == MEDIA_SCAN_RESULT_ERROR) {
ALOGE("An error occurred while scanning file '%s'.", pathStr);
}
env->ReleaseStringUTFChars(path, pathStr);
if (mimeType) {
env->ReleaseStringUTFChars(mimeType, mimeTypeStr);
}
return result != MEDIA_SCAN_RESULT_ERROR;
}
```

@ -4,6 +4,175 @@
#### 目录
1. 思维导图
2.
1. 修改生成的 apk 文件名
2. 动态配置 AndroidManifest 文件
3. 自定义 BuildConfig
4. 动态添加自定义的资源
5. Java 编译选项
#### 修改生成的 apk 文件名
既然要修改生成的 apk 文件名,那么就要修改 Android Gradle 打包的输出。为了解决这个问题,Android 对象为我们提供了三个熟悉:applicationVariants(仅仅适用于 Android 应用 Gradle 插件)、libraryVariants(仅仅适用于 Android 库 Gradle 插件)、testVariants(以上两种 Gradle 插件都适用)。
以上三个属性返回的都是 DomainObjectSet 对象集合,里面的元素分别是 ApplicationVariant、LibraryVariant 和 TestVariant。这三个元素的名字直译来意思是变体,通俗的讲它们就是 Android 构建的产物。它们基于 BuildType 和 ProductFlavor 生成的产物。BuildType 即构建类型,Android 已经内置了 debug 和 release 两种构建类型。ProductFlavor 允许我们根据不同的情况生成多个不同的 APK 包,如果不针对我们自定义的 ProductFlavor 单独配置的话,会为这个 ProductFlavor 使用默认的 defaultConfig 的配置。defaultConfig 里面有我们熟悉的 verisonCode、versionName 等等。
```groovy
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.example.demoproject"
minSdkVersion 26
targetSdkVersion 28
versionCode 1
versionName "1.0"
flavorDimensions "versionCode"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
zipAlignEnabled true
}
debug {
}
}
productFlavors {
google {
}
}
applicationVariants.all { variant ->
variant.outputs.each { output ->
def timeNow = new Date().format("yyyyMMdd")
def newName = 'app' + '_' + variant.buildType.name + '_' + variant.versionCode + "_" + timeNow + '.apk'
output.outputFileName = newName
}
}
}
```
build debug 包生成的 apk 文件名为 app_debug_1_20190521.apk。
#### 动态配置 AndroidManifest 文件
动态配置 AndroidManifest 文件,顾名思义就是可以在构建的过程中,动态修改 AndroidManifest 文件中的一些内容。Android Gradle 提供了非常便捷的办法,那就是使用 manifestPlaceholder,即 Manifest 占位符。
ManifestPlaceholders 是 ProductFlavor 的一个属性,是一个 Map 类型,所以我们可以同时配置很多个占位符。
比如想把 application 下的 meta-data 的 name 替换掉:
```
<meta-data android:name="CHANNEL_ID" android:value="${CHANNEL}"/>
```
```groovy
productFlavors {
google {
manifestPlaceholders.put("CHANNEL", "google")
}
baidu {
manifestPlaceholders.put("CHANNEL","baidu")
}
}
```
但是如果,渠道很多的情况下,一个一个添加还是很繁琐的,那就需要遍历添加了:
```groovy
productFlavors {
google {
}
baidu {
}
}
productFlavors.all { flavor ->
manifestPlaceholders.put("CHANNEL", name)
}
```
#### 自定义 BuildConfig
```java
/**
* Automatically generated file. DO NOT MODIFY
*/
package com.example.demoproject;
public final class BuildConfig {
public static final boolean DEBUG = Boolean.parseBoolean("true");
public static final String APPLICATION_ID = "com.example.demoproject";
public static final String BUILD_TYPE = "debug";
public static final String FLAVOR = "google";
public static final int VERSION_CODE = 1;
public static final String VERSION_NAME = "1.0";
}
```
BuildConfig 这个类是自动生成的,不可以修改,但是我们可以往里面添加我们自定义的常量,Android Gradle 提供了 buildConfigField。
```groovy
productFlavors {
google {
buildConfigField('String', 'name', '"Omooo"')
}
baidu {
buildConfigField('String', 'name', '"Omooo"')
}
}
```
**注意:**值是放在单引号里面的,所以是 '"Omooo"'!
上面都是在渠道(ProductFlavor)中配置的,其实在构建类型(BuildType)里面也是可以配置的:
```groovy
buildTypes {
debug {
buildConfigField('String', 'name', '"Omooo"')
}
}
```
#### 动态添加自定义的资源
这里讲的自定义资源,是专门针对 res/values 类型资源的,它们不光可以在 res/values 文件夹里使用 xml 的方式定义,还可以在我们的 Android Gradle 中定义,大大增加了构建的灵活性。
实现这一功能的正是 resValue 方法,它在 BuildType 和 ProductFlavor 这两个对象中都存在。
```groovy
productFlavors {
google {
resValue('string','name','Omooo_Google')
}
baidu {
resValue('string','name','Omooo_Baidu')
}
}
```
然后我们在 app/build/generated/res/resValues/google/debug/values 下查看自动生成的资源文件。
上面演示的是 string 类型,当然也可以使用 id、bool、demin、color 等类型,和 buildConfigField 一样,它也是可以在 BuildType 中使用的。
#### Java 编译选项
```groovy
compileOptions {
encoding = 'utf-8'
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_1_8
}
```
Android 对象提供了一个 compileOptions 方法,它接受一个闭包作为参数,来对 Java 编译选项进行配置。
compileOptions 是编译配置,它提供了三个属性,分别是 encoding、sourceCompatibility、targetCompatibility。sourceCompatibility 是配置 Java 源代码的编译级别,targetCompatibility 是配置生成的 Java 字节码的版本。
Loading…
Cancel
Save