进行Android Rom开发的时候通常会有好多系统预置apk需要进行开发维护。一般来讲,这些apk开发需要依赖系统码源进行编译,而编译一个apk又需要把它push到手机里,费时费力。有没有办法像普通APP开发一样,做到即时运行呢?这就是本篇文章要解决的问题。
要使用Gradle来编译整机apk需要解决的问题有两个:签名、framework的动态链接。
Android平台的签名分为四种,分别是 media、platform、shared、testkey。同的签名文件对应不同的权限,Android默认的签名文件为testkey。
通过mm
、mma
来编译整机apk时,需要确定编译的apk的签名类型,在Android.mk
文件中来确定:
LOCAL_CERTIFICATE := platform
LOCAL_CERTIFICATE
规定了将编译的apk的签名类型:platform。
mk文件规定了需要生成platform的签名文件,网上有许多生成platform签名文件的生成方法,笔者最开始生成时也是按照这些方法来做的,不过,这些方法都是错的,费了不少时间(参考)。直接给出签名命令:
1、生成platform.pem
openssl pkcs8 -in platform.pk8 -inform DER -outform PEM -out shared.priv.pem
2、生成platform.pk12
openssl pkcs12 -export -in platform.x509.pem -inkey shared.priv.pem -out shared.pk12 -name 你的签名名字(如:android)
3、生成keystore文件
keytool -importkeystore -deststorepass 你的签名文件密码(如:android) -destkeypass 你的签名文件密码(如:android) -destkeystore platform.keystore -srckeystore shared.pk12 -srcstoretype PKCS12 -srcstorepass 你的签名文件密码(如:android) -alias 你的签名名字(如:android)
网上给出的签名过程通常错在第一步:
openssl pkcs8 -in platform.pk8 -inform DER -outform PEM -out shared.priv.pem -nocrypt
-nocrypt
的意思是不需要密码,但通常每个公司都有自己的密码,去掉这个参数即可。
按以上步骤即可得到platform.keystore
这个签名文件了。
整机apk通常都要依赖framework,需要在build.gradle中动态链接到framework。需要先将framework.jar放到工程里。需要的build.gradle文件读者如果没有可以上网找一个或由IDE自动生成。链接到framework其实一句话即可解决问题:
gradle.projectsEvaluated {
tasks.withType(JavaCompile) {
options.compilerArgs << '-Xbootclasspath/p:' + rootDir + '你存放framework.jar的路径(如:/config/framework.jar)'
}
}
这样代码运行的时候就会动态链接到framework,为了增加可读性,可以在设置依赖的地方增加以下一句:
compileOnly files(你存放framework.jar的路径(如:rootProject.getRootDir().getAbsolutePath()+ '/config/framework.jar'))
如果采用gradle来编译整机代码,也需保证编译整机rom的时候能正常编译,在mk文件里需要完成的工作就是触发gradle编译。 先给出一个编译脚本build.sh:
……
GRADLE_SYNC_ENV=0
GRADLE_TASK_CLEAN=0
GRADLE_TASK_ALL=0
GRADLE_TASK_RELEASE=0
GRADLE_TASK_BETA=0
GRADLE_TASK_DEBUG=0
GRADLE_MAKEFILE=0
GRADLE_PATH=.
GRADLE_INSTALL_APK=0
# Add executable permissions
chmod +x ${GRADLE_PATH}/gradlew
# Do gradle build
if [[ ${GRADLE_TASK_CLEAN} == 1 ]]
then
${GRADLE_PATH}/gradlew -p ${GRADLE_PATH} clean
elif [[ ${GRADLE_TASK_ALL} == 1 ]]
then
${GRADLE_PATH}/gradlew -p ${GRADLE_PATH} clean assemble
elif [[ ${GRADLE_TASK_RELEASE} == 1 ]] && [[ ${GRADLE_INSTALL_APK} == 1 ]]
then
${GRADLE_PATH}/gradlew -p ${GRADLE_PATH} clean installRelease
elif [[ ${GRADLE_TASK_RELEASE} == 1 ]]
then
${GRADLE_PATH}/gradlew -p ${GRADLE_PATH} clean assembleRelease
elif [[ ${GRADLE_TASK_BETA} == 1 ]] && [[ ${GRADLE_INSTALL_APK} == 1 ]]
then
${GRADLE_PATH}/gradlew -p ${GRADLE_PATH} clean installBeta
elif [[ ${GRADLE_TASK_BETA} == 1 ]]
then
${GRADLE_PATH}/gradlew -p ${GRADLE_PATH} clean assembleBeta
elif [[ ${GRADLE_TASK_DEBUG} == 1 ]] && [[ ${GRADLE_INSTALL_APK} == 1 ]]
then
${GRADLE_PATH}/gradlew -p ${GRADLE_PATH} installDebug
elif [[ ${GRADLE_TASK_DEBUG} == 1 ]]
then
${GRADLE_PATH}/gradlew -p ${GRADLE_PATH} assembleDebug
# Makefile compile do not need version name flag
if [[ ${GRADLE_MAKEFILE} == 1 ]]
then
# This scene will do clean, so do not need to deal with multiple apk scene.
for APK_PATH in $(find ${GRADLE_PATH}/app/build/outputs/apk -name "*.apk")
do
if [[ ${APK_PATH} != $(dirname ${APK_PATH})/你的apk.apk ]]
then
mv ${APK_PATH} $(dirname ${APK_PATH})/你的apk.apk.apk
checkCMDStatus $?
fi
done
fi
……
# Launch built apk
……
需要注意的是,minSdkVersion
、targetSdkVersion
也需要和gradle版本配置好,编译如果有错按提示修改即可。
接下来修改mk文件。mk文件中只需要触发gralde编译即可:
LOCAL_PATH := $(call my-dir)
YOURAPK_GRADLE_PATH := $(LOCAL_PATH)
YOURAPK_CHECK_CMD := $(YOURAPK_GRADLE_PATH)/build.sh -e -m $(YOURAPK_GRADLE_PATH)
YOURAPK_CLEAN_CMD := $(YOURAPK_GRADLE_PATH)/build.sh -c -m $(YOURAPK_GRADLE_PATH)
ifeq ($(TARGET_BUILD_VARIANT), user)
YOURAPK_ASSEMBLE_CMD := $(YOURAPK_GRADLE_PATH)/build.sh -r -m $(YOURAPK_GRADLE_PATH)
YOURAPK_APK_PATH := app/build/outputs/apk/release
else
YOURAPK_ASSEMBLE_CMD := $(YOURAPK_GRADLE_PATH)/build.sh -b -m $(YOURAPK_GRADLE_PATH)
YOURAPK_APK_PATH := app/build/outputs/apk/beta
endif
# If there is no YOURAPK_APK_PATH directory, the APK Build will not be trigger.
ifeq (, $(wildcard $(YOURAPK_APK_PATH)))
$(shell mkdir -p $(YOURAPK_APK_PATH))
endif
# Add executable permissions
$(shell chmod +x $(YOURAPK_GRADLE_PATH)/build.sh)
.PHONY: APK Build
filemanagerBuild:
@echo "Run gradlew assemble command"
@rm -rf $(YOURAPK_GRADLE_PATH)/app/.externalNativeBuild
@$(YOURAPK_CHECK_CMD)
@$(YOURAPK_ASSEMBLE_CMD)
$(YOURAPK_GRADLE_PATH)/$(YOURAPK_APK_PATH)/%.apk: APK Build
@echo "FileManager APK file is ready for $@"
.PHONY: clean
clean:
@echo "Run APK gradlew clean command"
@rm -rf $(YOURAPK_GRADLE_PATH)/app/.externalNativeBuild
@$(YOURAPK_CHECK_CMD)
@$(YOURAPK_CLEAN_CMD)
include $(CLEAR_VARS)
LOCAL_MODULE := YOUR_MODULE
LOCAL_OVERRIDES_PACKAGES := YOUR_ OVERRIDES_PACKAGES
LOCAL_SRC_FILES := $(YOURAPK_APK_PATH)/你的apk.apk
LOCAL_MODULE_CLASS := APPS
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_PRIVILEGED_MODULE := false
LOCAL_MODULE_TAGS := optional
LOCAL_CERTIFICATE := platform
include $(BUILD_PREBUILT)
通过以上步骤即完成了在mk文件中触发gradle编译apk,而不影响整机rom的编译。
完成了以上步骤后,就实现了用Android Studio + Gradle来编译整机apk。还差最后一步就是apk的混淆。发布的apk进行混淆后的好处显而易见,除了提高安全性以外,最大的好处就是缩小apk的体积。给出一个通用的混淆模板,如果有不需要混淆的类在上边加上就好。
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Code compression level
-optimizationpasses 5
# Do not use case mixing
-dontusemixedcaseclassnames
# Do not ignore non-public library classes
-dontskipnonpubliclibraryclasses
# Do not ignore members of the library class visible to the package
-dontskipnonpubliclibraryclassmembers
# Specifies not to optimize the input class files.
# By default, optimization is enabled; all methods are optimized at a bytecode level.
# If optimization is disabled, other optimized option like 'optimizationpasses'/'optimizations' will not work.
#-dontoptimize
# No pre-verification,speed up obfuscating
-dontpreverify
# Block warning
-ignorewarnings
# Specify the algorithm used for obfuscating
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
# Keep Annotation
-keepattributes *Annotation*
# Keep generics, for JSON
-keepattributes Signature
# Retain the code line number when an exception is thrown
-keepattributes SourceFile,LineNumberTable
# Allow access to and modify members of classes and classes with modifiers
-allowaccessmodification
# Only for -repackageclasses
-repackageclasses ''
# Keep obfuscating compilation log
-verbose
# Optional, do not shrink useless code
#-dontshrink
# Optional, do not obfuscate code
#-dontobfuscate
# Default keep activity/application/service/broadcastReceiver/contentprovider...
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.support.multidex.MultiDexApplication
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class * extends android.view.View
-keep class android.support.** {*;}
# Keep ILicensingService/ILicensingService for google service
-keep public class com.google.vending.licensing.ILicensingService
-keep public class com.android.vending.licensing.ILicensingService
# Keep basic jars from Android
-keep public class * extends android.support.v4.**
-keep public class * extends android.support.v7.**
-keep public class * extends android.support.annotation.**
# Keep native method
-keepclasseswithmembernames class * {
native <methods>;
}
# Keep 'android:onclick="onClick"' in XML
-keepclassmembers class * extends android.app.Activity{
public void *(android.view.View);
}
# Keep values()/valueOf() in enum
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
# Keep setXxx()/getXxx() in View
-keep public class * extends android.view.View{
*** get*();
void set*(***);
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
}
# Keep classes extends from Parcelable
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
# Keep classes extends from Serizalizable
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
#-keep class **.R$* {
# *;
#}
# Keep resource static
-keepclassmembers class **.R$* {
public static <fields>;
}
# Keep onXXEvent/**On*Listener
-keepclassmembers class * {
void *(**On*Event);
void *(**On*Listener);
}
# Keep customize views
-keep public class * extends android.view.View{
*** get*();
void set*(***);
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
}
# Keep entity model
-dontwarn com.suchengkeji.android.confusiondemo.md.**
# Processing with reflections
-keep class com.suchengkeji.android.confusiondemo.md.** { *; }
# Keep testing related code
-dontnote junit.framework.**
-dontnote junit.runner.**
-dontwarn android.test.**
-dontwarn android.support.test.**
-dontwarn org.junit.**
#
# ----------------------------- other 3rd jars -----------------------------
#
在进行了上面所有步骤后,需要对apk做全量遍历,防止由混淆编译及Gradle编译引入的功能回退,保证apk质量。
相关参考:https://www.jianshu.com/p/63d699cffa1a
相关参考:https://www.jianshu.com/p/9df3c3b6067a
相关参考:https://www.jianshu.com/p/e9d3c57ab92f?utm_campaign=haruki&utm_content=note&utm_medium=reader_share&utm_source=qq
相关参考:https://blog.csdn.net/zhangfeng5909/article/details/80856095
相关参考:https://blog.csdn.net/xu_coding/article/details/80819494
相关参考:https://blog.csdn.net/chzphoenix/article/details/75470846
相关参考:https://blog.csdn.net/sunquan1992/article/details/64918727
相关参考:https://www.jianshu.com/p/b471db6a01af
相关参考:https://www.guardsquare.com/en/products/proguard/manual/usage
相关参考:https://blog.csdn.net/cauchyweierstrass/article/details/53261581
相关参考:https://blog.csdn.net/ccpat/article/details/52059344
相关参考:https://blog.csdn.net/geekqian/article/details/78961835