androidStudio 打包与混淆
在gradle中通过makeJar打包
不同模块的gradle都支持打包功能,application module的build.gradle中引入的是com.android.application
插件来打包,而library module的build.gradle中引入的是com.android.library
插件进行打包。
一. 基本概念(Project 和 Task)
Gradle中有两个基本的概念:project和task。每个Gradle的构建由一个project构成,它代表着需要被构建的组件或者构建的整个项目。每个project由一个或者多个task组成。task代表着Gradle构建过程中可执行的最小单元。例如当构建一个组件时,可能需要先编译、打包、然后再生成文档或者发布等,这其中的每个步骤都可以定义成一个task,gradle会创建一个脚本来执行这些task
Task实例:
在app目录的gradle中新建一个任务:
task hello {
doLast{
println "hello world"
}
}
通过 gradlew hello 就可以在终端运行这个 task
“doLast” : 为定义的一个映射,意思为在这个task的最后运行。同样还可以在定义doFirst:在task的开始执行
Task中的依赖: dependsOn
如果B.dependOn A ; B的执行必须在A执行,在二:项目中在Task中打Jar包 代码中,
dependsOn:['compileReleaseJavaWithJavac'] 表示在编译完release的版本之后再执行该任务
二:项目中在Task中打Jar包
task makeJar(dependsOn:['compileReleaseJavaWithJavac'], type: Jar) {
archiveName = 'AssistSDK.jar';
delete 'build/libs/AssistSDK.jar'
from('build/intermediates/classes/release')
from(project.zipTree("libs/liteProtobuf.jar"));
destinationDir = file('sbuild/lib')
exclude('xxx/BuildConfig.class')
exclude('xxx/BuildConfig\$*.class')
exclude('**/R.class')
exclude('**/R\$*.class')
include('xxx/**/*.class')
// include('com/google/protobuf/**/*.class')
}
上述代码中
exclude('xxx/BuildConfig.class')
exclude('xxx/BuildConfig\$*.class')
两行代码的目的是为了 去掉jar中的 buildconfig.class (记录的一些参数)
exclude('**/R.class')
exclude('**/R\$*.class')
这两行代码的效果目前还不是很清楚,
将第三方库的依赖代码 包含到Jar包中,这样 在包含本Jar的时候就不用再去包含第三方的Jar
from(project.zipTree("libs/liteProtobuf.jar"));
最后在命令行窗口 输入命令: gradlew makeJar
三、在Task中打混淆的Jar包
task proguardJar(dependsOn: ['makeJar'], type: proguard.gradle.ProGuardTask) {
configuration 'proguard-rules.pro'
String inJar = makeJar.archivePath.getAbsolutePath()
//输入 jar
injars inJar
println "lixiang->>"+inJar
//输出 jar 的位置和名称
String outJar = inJar.substring(0, inJar.lastIndexOf(File.separator)) + "/proguard-${makeJar.archiveName}"
outjars outJar
println "lixiang->>"+outJar
//设置不删除未引用的资源(类,方法等)
dontshrink
}
混淆proguard-rules.pro文件与混淆规则
# 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 *; #} # 指定代码的压缩级别 -optimizationpasses 5 # 是否使用大小写混合 -dontusemixedcaseclassnames # 是否混淆第三方jar -dontskipnonpubliclibraryclasses -dontoptimize # 预校验 -dontpreverify # 混淆时是否记录日志 -verbose # 混淆时所采用的算法 -optimizations !code/simplification/arithmetic,!field/*,!class/merging/* # 保护注解 #-keepattributes *Annotation* #避免混淆泛型 如果混淆报错建议关掉 #-keepattributes Signature # 保持哪些类不被混淆 -keep public class * extends android.app.Activity -keep public class * extends android.app.Application -keep public class * extends android.app.Service -keep public class * extends android.app.IntentService -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 com.android.vending.licensing.ILicensingService -keep public class * extends android.support.v4.app.Fragment #忽略警告 -ignorewarning ##记录生成的日志数据,gradle build时在本项目根目录输出## #apk 包内所有 class 的内部结构 -dump class_files.txt #未混淆的类和成员 -printseeds seeds.txt #列出从 apk 中删除的代码 -printusage unused.txt #混淆前后的映射 #-printmapping mapping.txt ########记录生成的日志数据,gradle build时 在本项目根目录输出-end###### #####混淆保护自己项目的部分代码以及引用的第三方jar包library####### #-libraryjars libs/umeng-analytics-v5.2.4.jar #三星应用市场需要添加:sdk-v1.0.0.jar,look-v1.0.1.jar #-libraryjars libs/sdk-v1.0.0.jar #-libraryjars libs/look-v1.0.1.jar #如果引用了v4或者v7包 -dontwarn android.support.** -keep class android.support.** {*; } ####混淆保护自己项目的部分代码以及引用的第三方jar包library-end#### -keep public class * extends android.view.View { public <init>(android.content.Context); public <init>(android.content.Context, android.util.AttributeSet); public <init>(android.content.Context, android.util.AttributeSet, int); public void set*(...); } #保持 native 方法不被混淆 -keepclasseswithmembernames class * { native <methods>; } #保持自定义控件类不被混淆 -keepclasseswithmembers class * { public <init>(android.content.Context); } -keepclasseswithmembernames class * { public <init>(android.content.Context, android.util.AttributeSet); } -keepclasseswithmembernames class * { public <init>(android.content.Context, android.util.AttributeSet, int); } #保持 Parcelable 不被混淆 -keep class * implements android.os.Parcelable { public static final android.os.Parcelable$Creator *; } #保持 Serializable 不被混淆 -keepnames class * implements java.io.Serializable #保持 Serializable 不被混淆并且enum 类也不被混淆 -keepclassmembers class * implements java.io.Serializable { static final long serialVersionUID; private static final java.io.ObjectStreamField[] serialPersistentFields; !static !transient <fields>; !private <fields>; !private <methods>; private void writeObject(java.io.ObjectOutputStream); private void readObject(java.io.ObjectInputStream); java.lang.Object writeReplace(); java.lang.Object readResolve(); public void set*(***); public *** get*(); } #保持枚举 enum 类不被混淆 如果混淆报错,建议直接使用上面的 -keepclassmembers class * implements java.io.Serializable即可 -keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); } -keepclassmembers class * { public void *ButtonClicked(android.view.View); } #不混淆资源类 -keepclassmembers class **.R$* { public static <fields>; } # Gson uses generic type information stored in a class file when working with fields. Proguard # removes such information by default, so configure it to keep all of it. -keepattributes Signature # For using GSON @Expose annotation -keepattributes *Annotation* # Gson specific classes -keep class sun.misc.Unsafe { *; } #-keep class com.google.gson.stream.** { *; } # Application classes that will be serialized/deserialized over Gson #-keep class com.google.gson.examples.android.model.** { *; } #第三方jar包 -keep class com.nostra13.universalimageloader.** { *; } -keep class com.android.volley.** {*; } -keep class org.apache.http.** {*; }
2,内嵌类(尤为注意)
内嵌类经常会被混淆,结果在调用的时候为空就崩溃了,最好的解决方法就是把这个内嵌类拿出来,单独成为一个类。如果一定要内置,那么这个类就必须在混淆的时候保留,比如如下:
# 保留内嵌类不被混淆 -keep class com.example.xxx.MainActivity$* { *; }
这个$符号就是用来分割内嵌类与其母体的标志。
(实际遇到匿名内部类被混淆,调用时找不到接口。 通过把匿名内部类设置为 内部类,然后保留不混淆)
四、 dependencies 中六种依赖
Android Studio引用第三方库很方便,只需要一句代码就可以搞定,几种引用第三方库的方式,总结一下:
方式:1:它就会自动把这个包下载下来,并且引用它。节省git空间,而且修改版本也很方便。
compile 'com.android.support:support-v4:23.3.0' // 实际使用中经常会用 xxxx:+ insteadof version number
方式2:引用libs下所有jar包
compile fileTree(dir: 'libs', include: ['*.jar'])
方式3:引用一个jar
compile files('libs/fastjson-1.1.53.android.jar')
方式4:引用一个aar文件,注意并不能像 方式2 那样自动引用全部的aar,而需要对每个aar分别进行引用。
compile(name: 'aar_file_name', ext: 'aar')
方式5:引用库类型的项目
compile project(':xxxsdk')
方式6:仅仅在编译时使用,但最终不会被编译到apk或aar里
provided files('libs/glide-3.7.0.jar')
Compile
compile是对所有的build type以及favlors都会参与编译并且打包到最终的apk文件中。
Provided
Provided是对所有的build type以及favlors只在编译时使用,类似eclipse中的external-libs,只参与编译,不打包到最终apk。
APK
只会打包到apk文件中,而不参与编译,所以不能再代码中直接调用jar中的类或方法,否则在编译时会报错
Test compile
Test compile 仅仅是针对单元测试代码的编译编译以及最终打包测试apk时有效,而对正常的debug或者release apk包不起作用。
Debug compile
Debug compile 仅仅针对debug模式的编译和最终的debug apk打包。
Release compile
Release compile 仅仅针对Release 模式的编译和最终的Release apk打包。
查看依赖结构命令 ./gradlew -q app:dependencies
gredle 命令说明:
https://docs.gradle.org/current/userguide/command_line_interface.html#para:commandline_dependency_report
混淆规则参考:
http://blog.csdn.net/nature_day/article/details/51545849
http://www.jianshu.com/p/158aa484da13
打包
http://unclechen.github.io/2015/10/25/Gradle%E5%AE%9E%E8%B7%B5%E4%B9%8B%E6%89%93%E5%8C%85jar+Log%E5%BC%80%E5%85%B3%E8%87%AA%E5%8A%A8%E5%85%B3%E9%97%AD/