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打印調試。