Android.mk中APT使用

1. 註解工具

註解工具是java代碼生成及對象注入的強大工具。註解包括源碼可見、字節碼可見及運行時可見。目前比較流行的APT技術,也即時編譯成字節碼時可見。下面以Dagger2及ButterKnife兩款註解工具講解如何在Android.mk中使用這兩款工具。
通過gradle編譯時,在build.gradle文件中通過如下方式引入

    api 'com.google.dagger:dagger:2.14.1'
    annotationProcessor "com.google.dagger:dagger-compiler:2.14.1"
    api 'com.jakewharton:butterknife:8.8.1'
    annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'

2. Android.mk APT支持

2.1 Android.mk配置

LOCAL_PATH:= $(call my-dir)

#######################################
# Create references to prebuilt libraries.
include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional
LOCAL_PROGUARD_ENABLED := disabled
LOCAL_CERTIFICATE := platform

LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := \
    demo-dagger2-compiler:libs/dagger-compiler-2.14.1.jar \
    demo-dagger2:libs/dagger-2.14.1.jar \
    demo-dagger2-producers:libs/dagger-producers-2.14.1.jar \
    demo-dagger2-spi:libs/dagger-spi-2.14.1.jar \
    demo-butterknife:libs/butterknife-8.8.1.aar \
    demo-butterknife-anno:libs/butterknife-annotations-8.8.1.jar \
    demo-butterknife-compiler:libs/butterknife-compiler-8.8.1.jar \
    demo-support-anno:libs/support-annotations-28.0.0.jar \
    demo-javax-inject:libs/javax.inject-1.jar \
    demo-guava:libs/guava-27.1-jre.jar \
    demo-java-format:libs/google-java-format-1.4-all-deps.jar \
    demo-javax-api:libs/jsr250-api-1.0.jar \
    demo-javapoet:libs/javapoet-1.8.0.jar \
    demo-auto-common:libs/auto-common-0.8.jar

include $(BUILD_MULTI_PREBUILT)


include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional
LOCAL_PROGUARD_ENABLED := disabled
LOCAL_CERTIFICATE := platform

LOCAL_SRC_FILES := $(call all-java-files-under, java)

LOCAL_STATIC_JAVA_LIBRARIES := demo-dagger2 demo-butterknife demo-butterknife-anno \
                demo-support-anno demo-javax-inject

LOCAL_JAVA_LIBRARIES := gsframework

# Libraries needed by the compiler (JACK) to generate code.
PROCESSOR_LIBRARIES_TARGET := \
	demo-dagger2 \
	demo-dagger2-compiler \
	demo-dagger2-producers \
	demo-butterknife \
	demo-butterknife-anno \
	demo-butterknife-compiler \
	demo-javax-inject \
	demo-guava \
	demo-java-format \
	demo-javax-api \
	demo-dagger2-spi \
	demo-javapoet \
	demo-auto-common

# Libraries needed by the compiler (JACK) to generate code.
# Resolve the jar paths.
PROCESSOR_JARS := $(call java-lib-deps, $(PROCESSOR_LIBRARIES_TARGET))
# Necessary for annotation processors to work correctly.
LOCAL_ADDITIONAL_DEPENDENCIES += $(PROCESSOR_JARS)

LOCAL_JAVACFLAGS += -processorpath $(call normalize-path-list,$(PROCESSOR_JARS))

LOCAL_ANNOTATION_PROCESSOR_CLASSES := \
butterknife.compiler.ButterKnifeProcessor dagger.internal.codegen.ComponentProcessor

#LOCAL_JAVACFLAGS += $(foreach class,$(LOCAL_ANNOTATION_PROCESSOR_CLASSES),-processor $(class))

LOCAL_PACKAGE_NAME := InjectDemo

include $(BUILD_PACKAGE)

# Use the folloing include to make our test apk.

#include $(call all-makefiles-under,$(LOCAL_PATH))

可以看出,編譯需要引入一堆jar包,包括編譯時包和靜態jar包,編譯包只在編譯時依賴,不會打入app中,而靜態包則會打入APP中。

2.2 Android build系統patch

下面是針對Android6.0編譯系統添加支持APT的patch,請注意 mm 模塊編譯時,請將JDK版本切換到1.8版本,否則無法生成註解代碼。
在這裏插入圖片描述
Android build系統相關patch如下所示:
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

3. 編譯生成源碼

執行mm命令後,在編譯之前會處理註解生成java源碼,生成的源碼路徑:

out/target/common/obj/APPS/InjectDemo_intermediates/anno

在編譯前處理註解的主要是通過—processorpath選項完成的,如下是javac選項使用幫助文檔:

$ javac
用法: javac <options> <source files>
其中, 可能的選項包括:
  -g                         生成所有調試信息
  -g:none                    不生成任何調試信息
  -g:{lines,vars,source}     只生成某些調試信息
  -nowarn                    不生成任何警告
  -verbose                   輸出有關編譯器正在執行的操作的消息
  -deprecation               輸出使用已過時的 API 的源位置
  -classpath <路徑>            指定查找用戶類文件和註釋處理程序的位置
  -cp <路徑>                   指定查找用戶類文件和註釋處理程序的位置
  -sourcepath <路徑>           指定查找輸入源文件的位置
  -bootclasspath <路徑>        覆蓋引導類文件的位置
  -extdirs <目錄>              覆蓋所安裝擴展的位置
  -endorseddirs <目錄>         覆蓋簽名的標準路徑的位置
  -proc:{none,only}          控制是否執行註釋處理和/或編譯。
  -processor <class1>[,<class2>,<class3>...] 要運行的註釋處理程序的名稱; 繞過默認的搜索進程
  -processorpath <路徑>        指定查找註釋處理程序的位置
  -parameters                生成元數據以用於方法參數的反射
  -d <目錄>                    指定放置生成的類文件的位置
  -s <目錄>                    指定放置生成的源文件的位置
  -h <目錄>                    指定放置生成的本機標頭文件的位置
  -implicit:{none,class}     指定是否爲隱式引用文件生成類文件
  -encoding <編碼>             指定源文件使用的字符編碼
  -source <發行版>              提供與指定發行版的源兼容性
  -target <發行版>              生成特定 VM 版本的類文件
  -profile <配置文件>            請確保使用的 API 在指定的配置文件中可用
  -version                   版本信息
  -help                      輸出標準選項的提要
  -A關鍵字[=值]                  傳遞給註釋處理程序的選項
  -X                         輸出非標準選項的提要
  -J<標記>                     直接將 <標記> 傳遞給運行時系統
  -Werror                    出現警告時終止編譯
  @<文件名>                     從文件讀取選項和文件名

在Android源碼build系統中,javac編譯是通過一個函數處理的,代碼路徑在

/build/core/definitions.mk

# Common definition to invoke javac on the host and target.
#
# Some historical notes:
# - below we write the list of java files to java-source-list to avoid argument
#   list length problems with Cygwin
# - we filter out duplicate java file names because eclipse's compiler
#   doesn't like them.
#
# $(1): javac
# $(2): bootclasspath
define compile-java
$(hide) rm -f $@
$(hide) rm -rf $(PRIVATE_CLASS_INTERMEDIATES_DIR) $(PRIVATE_ANNO_INTERMEDIATES_DIR)
$(hide) mkdir -p $(dir $@)
$(hide) mkdir -p $(PRIVATE_CLASS_INTERMEDIATES_DIR) $(PRIVATE_ANNO_INTERMEDIATES_DIR)
$(call unzip-jar-files,$(PRIVATE_STATIC_JAVA_LIBRARIES),$(PRIVATE_CLASS_INTERMEDIATES_DIR))
$(call dump-words-to-file,$(PRIVATE_JAVA_SOURCES),$(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list)
$(hide) if [ -d "$(PRIVATE_SOURCE_INTERMEDIATES_DIR)" ]; then \
          find $(PRIVATE_SOURCE_INTERMEDIATES_DIR) -name '*.java' >> $(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list; \
fi
$(hide) tr ' ' '\n' < $(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list \
    | sort -u > $(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list-uniq
$(hide) if [ -s $(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list-uniq ] ; then \
    $(1) -encoding UTF-8 \
    $(strip $(PRIVATE_JAVAC_DEBUG_FLAGS)) \
    $(if $(findstring true,$(PRIVATE_WARNINGS_ENABLE)),$(xlint_unchecked),) \
    $(2) \
    $(addprefix -classpath ,$(strip \
        $(call normalize-path-list,$(PRIVATE_ALL_JAVA_LIBRARIES)))) \
    $(if $(findstring true,$(PRIVATE_WARNINGS_ENABLE)),$(xlint_unchecked),) \
    -extdirs "" -d $(PRIVATE_CLASS_INTERMEDIATES_DIR) -s $(PRIVATE_ANNO_INTERMEDIATES_DIR) \
    $(PRIVATE_JAVACFLAGS) \
    \@$(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list-uniq \
    || ( rm -rf $(PRIVATE_CLASS_INTERMEDIATES_DIR) ; exit 41 ) \
fi
$(if $(PRIVATE_JAVA_LAYERS_FILE), $(hide) build/tools/java-layers.py \
    $(PRIVATE_JAVA_LAYERS_FILE) \@$(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list-uniq,)
#$(hide) rm -f $(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list
#$(hide) rm -f $(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list-uniq
$(if $(PRIVATE_JAR_EXCLUDE_FILES), $(hide) find $(PRIVATE_CLASS_INTERMEDIATES_DIR) \
    -name $(word 1, $(PRIVATE_JAR_EXCLUDE_FILES)) \
    $(addprefix -o -name , $(wordlist 2, 999, $(PRIVATE_JAR_EXCLUDE_FILES))) \
    | xargs rm -rf)
$(if $(PRIVATE_JAR_PACKAGES), \
    $(hide) find $(PRIVATE_CLASS_INTERMEDIATES_DIR) -mindepth 1 -type f \
        $(foreach pkg, $(PRIVATE_JAR_PACKAGES), \
            -not -path $(PRIVATE_CLASS_INTERMEDIATES_DIR)/$(subst .,/,$(pkg))/\*) -delete ; \
        find $(PRIVATE_CLASS_INTERMEDIATES_DIR) -empty -delete)
$(if $(PRIVATE_JAR_EXCLUDE_PACKAGES), $(hide) rm -rf \
    $(foreach pkg, $(PRIVATE_JAR_EXCLUDE_PACKAGES), \
        $(PRIVATE_CLASS_INTERMEDIATES_DIR)/$(subst .,/,$(pkg))))
$(if $(PRIVATE_RMTYPEDEFS), $(hide) $(RMTYPEDEFS) -v $(PRIVATE_CLASS_INTERMEDIATES_DIR))
$(if $(PRIVATE_JAR_MANIFEST), \
    $(hide) sed -e 's/%BUILD_NUMBER%/$(BUILD_NUMBER)/' \
            $(PRIVATE_JAR_MANIFEST) > $(dir $@)/manifest.mf && \
        jar -cfm $@ $(dir $@)/manifest.mf \
            -C $(PRIVATE_CLASS_INTERMEDIATES_DIR) ., \
    $(hide) jar -cf $@ -C $(PRIVATE_CLASS_INTERMEDIATES_DIR) .)
$(if $(PRIVATE_EXTRA_JAR_ARGS),$(call add-java-resources-to,$@))
endef

define transform-java-to-classes.jar
@echo "--------------------start compile-java-------------------------"
@echo "class path = $(PRIVATE_CLASS_INTERMEDIATES_DIR), anno src path = $(PRIVATE_ANNO_INTERMEDIATES_DIR)"
@echo "private java libs = $(PRIVATE_ALL_JAVA_LIBRARIES)"
@echo "java layer files = $(PRIVATE_JAVA_LAYERS_FILE)"
@echo "private jar pkg = $(PRIVATE_JAR_MANIFEST)"

@echo -e ${CL_GRN}"target Java:"${CL_RST}" $(PRIVATE_MODULE) ($(PRIVATE_CLASS_INTERMEDIATES_DIR))"
$(call compile-java,$(TARGET_JAVAC),$(PRIVATE_BOOTCLASSPATH))
endef

有興趣的話,可以通過echo打印調試。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章