Android 4.0 中由ProGuard引发的一场血案
案件还原:
修改Android 4.0源码中的Setting,添加一项功能之后,在eng模式下编译,一切正常,遂提交代码到服务器。第二天,传来噩耗,Setting上新添加的功能无法使用,一点击则报错。
案件分析:
上传代码之前,已经在本地编译测试过,咋会有错呢??!!管它三七二十一,操起adb logcat抓取log进行分析。不看不知道,一看吓一跳,log中显示的错误信息竟然是ClassNotFound,也就是找不到相应的类,难道我上传的时候遗漏了吗(有这种可能)?马上查看提交记录,逐个检查提交的文件,全部提交了啊!咋会出现ClassNotFound呢?这时候咋办呢?冷静下来分析后可以知道,本地测试一般是在eng模式下编译的,而这次报错的是服务器在usr模式下编译处出来的。难道eng编译和usr编译出来的结果不一致??!!
案件进一步分析:
在本地选择usr编译模式,编译代码进行测试,果然出现了与服务器一样的错误,即ClassNotFound。
1.既然eng模式编译的代码能够正常运行,证明代码逻辑以及功能都是OK的。
2.usr编译出来出现ClassNotFound的问题,证明usr和eng编译模式不同导致编译结果差异。
3.比较eng模式和usr模式编译出来的Settings.apk,发现大小竟然不一致,用apktool反解后对比发现,usr模式下编译的Settings.apk中的确少了一个类(后文用lostClass.java代替)。
案件调查:
假设一:
难道是编译过程中没有将lostClass.java编译进去?
验证一:
查看编译过程中是否有对lostClass.java进行编译。
1.第一种查看方法。
打开源码中Settings文件夹下的Android.mk文件我们可以发现以下代码:
LOCAL_SRC_FILES := $(call all-java-files-under, src)
我们在这句代码后添加以下内容:
$(warning SevenTest LOCAL_SRC_FILES : $(LOCAL_SRC_FILES))
保存后终端中执行编译,我们可以看到src文件信息输出在终端上。复制到文件中查找lostClass.java,可以找到,因此可以推翻4的假设。
2.第二种查看方法。
查看APK的编译打包流程,如图1:
根据以上流程,我们可以找到每一步对应的mk文件,这里我们找到AndroidSouceCode/build/core/definitons.mk中注释以下代码:
[plain]
define compile-java
$(hide) rm -f $@
$(hide) rm -rf $(PRIVATE_CLASS_INTERMEDIATES_DIR)
$(hide) mkdir -p $(dir $@)
$(hide) mkdir -p $(PRIVATE_CLASS_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) $(1) -encoding UTF-8 \
$(strip $(PRIVATE_JAVAC_DEBUG_FLAGS)) \
$(if $(findstring true,$(LOCAL_WARNINGS_ENABLE)),$(xlint_unchecked),) \
$(2) \
$(addprefix -classpath ,$(strip \
$(call normalize-path-list,$(PRIVATE_ALL_JAVA_LIBRARIES)))) \
$(if $(findstring true,$(LOCAL_WARNINGS_ENABLE)),$(xlint_unchecked),) \
-extdirs "" -d $(PRIVATE_CLASS_INTERMEDIATES_DIR) \
$(PRIVATE_JAVACFLAGS) \
\@$(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list-uniq \
|| ( rm -rf $(PRIVATE_CLASS_INTERMEDIATES_DIR) ; exit 41 )
#Seven注释
#$(hide) rm -f $(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list
#$(hide) rm -f $(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list-uniq
$(hide) jar $(if $(strip $(PRIVATE_JAR_MANIFEST)),-cfm,-cf) \
$@ $(PRIVATE_JAR_MANIFEST) -C $(PRIVATE_CLASS_INTERMEDIATES_DIR) .
endef
define transform-java-to-classes.jar
@echo "target Java: $(PRIVATE_MODULE) ($(PRIVATE_CLASS_INTERMEDIATES_DIR))"
$(call compile-java,$(TARGET_JAVAC),$(PRIVATE_BOOTCLASSPATH))
#Seven注释
#$(hide) rm -rf $(PRIVATE_CLASS_INTERMEDIATES_DIR)
endef
define compile-java
$(hide) rm -f $@
$(hide) rm -rf $(PRIVATE_CLASS_INTERMEDIATES_DIR)
$(hide) mkdir -p $(dir $@)
$(hide) mkdir -p $(PRIVATE_CLASS_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) $(1) -encoding UTF-8 \
$(strip $(PRIVATE_JAVAC_DEBUG_FLAGS)) \
$(if $(findstring true,$(LOCAL_WARNINGS_ENABLE)),$(xlint_unchecked),) \
$(2) \
$(addprefix -classpath ,$(strip \
$(call normalize-path-list,$(PRIVATE_ALL_JAVA_LIBRARIES)))) \
$(if $(findstring true,$(LOCAL_WARNINGS_ENABLE)),$(xlint_unchecked),) \
-extdirs "" -d $(PRIVATE_CLASS_INTERMEDIATES_DIR) \
$(PRIVATE_JAVACFLAGS) \
\@$(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list-uniq \
|| ( rm -rf $(PRIVATE_CLASS_INTERMEDIATES_DIR) ; exit 41 )
#Seven注释
#$(hide) rm -f $(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list
#$(hide) rm -f $(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list-uniq
$(hide) jar $(if $(strip $(PRIVATE_JAR_MANIFEST)),-cfm,-cf) \
$@ $(PRIVATE_JAR_MANIFEST) -C $(PRIVATE_CLASS_INTERMEDIATES_DIR) .
endef
define transform-java-to-classes.jar
@echo "target Java: $(PRIVATE_MODULE) ($(PRIVATE_CLASS_INTERMEDIATES_DIR))"
$(call compile-java,$(TARGET_JAVAC),$(PRIVATE_BOOTCLASSPATH))
#Seven注释
#$(hide) rm -rf $(PRIVATE_CLASS_INTERMEDIATES_DIR)
endef
这样我们在终端中执行编译命令后,可以在路径:AndroidSourceCode/out/target/common/obj/APPS/Settings_intermediates/classes找到以下两个文件:
java-source-list和java-source-list-uniq,在其中搜索lostClass.java即可以找到。
通过以上查看可以知道,lostClass.java在是被编译了的,最后为了验证,我们可以通过jd-gui这个工具来查看AndroidSourceCode/out/target/common/obj/APPS/Settings_intermediates/classes.jar
补充:移动开发 , Android ,