Android Studio Apk 打包 混淆


最近打包公司的项目中遇到一些问题,在此记录一下。

Android Studio下打包APK

  • 本文只讲AS打包Apk的方式,eclispe的打包方式可以自行百度。

  • 打包Apk的两种方式:

    • 第一种方式
      使用Android Studio自带的工具生成Apk。具体步骤为 Build >Generate Signed APK,则会出现如图所示的界面,如果是第一次生成APK,则需要创建新的签名文件(图在下二)。
Android Studio Apk 打包 混淆_第1张图片
生成APK界面.png
Android Studio Apk 打包 混淆_第2张图片
新建key.png
  • 下面来一一解释其中方框需要填写的东西

    • 1.选择新建签名文件保存的路径,选择好路径并新建好签名文件的名称
    • 2.输入签名文件密码(生成Apk 导入签名文件需要这个密码)
    • 3.确认密码
    • 4.签名文件别名
    • 5--6 别名密码和确认密码
    • 7.你的名字或者代号
    • 接下来的8---12 分别是公司、组织、城市、地区、国家(不是必须填写,如果是公司项目可以填写公司名称等)
  • 填写完成后就新建好了本项目的签名文件,而如果以前已经打包了本项目,则直接导入以前生成的签名文件并输入密码就可以了(签名文件的密码一定要记住,下面会解释为什么)。如图所示填入刚刚设置的密码,点击下一步。


    Android Studio Apk 打包 混淆_第3张图片
    选择好签名文件并输入密码.png
  • 最后可以选择生成Apk的输出路径,Apk名称,其中有两个选项Signature Versions,他的含义为打包一个文件的签名版本,选V1打包出来的app是jar的(一般这种就是当做第三方导入项目来用的),选v2打包出来的APP是apk版本的(也就是可以直接在手机上安装的,可是上线的),一般两个都勾选上不会有什么问题。好了,到此第一种生成Apk的方式就弄完了,是不是很简单。


    Android Studio Apk 打包 混淆_第4张图片
    配置完成打包Apk.png
  • 第二种方式,在控制台使用命令的方式打包

首先配置gradle签名文件信息,如图所示:

Android Studio Apk 打包 混淆_第5张图片
配置签名文件信息.png

然后在Build Types中选择刚刚配置好的release(即签名配置信息),如图所示

Android Studio Apk 打包 混淆_第6张图片
选择刚刚配置好的release .png

这时候我们可以去gradle中查看我们刚刚配置好的签名文件信息,并且已经在 buildTypes有引用,说明签名文件已经配置好了,如图所示。

Android Studio Apk 打包 混淆_第7张图片
gradle中签名文件信息.png

但是我们直接将签名文件的密码放在Gradle中会不安全,可能会被他人反编译看到,这时候我们可以在Gradle这样设置,如图

Android Studio Apk 打包 混淆_第8张图片
签名信息设置读取密码.png
  • 这时候我们就可以在AS中的控制台用命令gradlew assembleRelease来生成我们的Apk(如果使用读取密码的方式,输入完命令之后会被要求输入签名文件的密码),如图所示输入完命令,构建完成生成的Apk在 app >build>outputs>apk 中(名称有debug的Apk是未签名的APK,名称中有release则是我们签名打包好的APK)。
Android Studio Apk 打包 混淆_第9张图片
控制台输入生成Apk命令.png

最后,有可能遇到的问题

  • 我们写好代码测试的时候将应用安装到到手机上,然后我们打包好的应用再次安装到手机上就会出现“已存在签名冲突的同名数据包”,导致应用安装失败,这就是我们签名文件出现了冲突,测试安装的应用使用的签名文件是IDE工具的测试签名文件(默认为debug,使用的签名文在$HOME/.android/debug.keystore),而我们打包的APK使用的签名文件是我们自己创建的签名文件,所以有冲突是肯定的,所以我们上面说一定要保存好我们自己生成的签名文件和密码,否则当你做版本更新的时候是无法进行覆盖安装的。
  • 选择已经存在的签名文件,如果密码打错,也是可以继续进行打包的,只是最后会显示打包失败,错误日志显示无法读取这个日志文件,如果使用控制台输入命令生成Apk则没有这个问题。
  • app 包下build.gradle,如果开启混淆(minifyEnabled true),但是混淆文件规则没有配置,也会打包失败,如果不开启混淆,则minifyEnabled false,开启混淆的同时也是配合去除无用资源(shrinkResources true)一起使用的,如果,关闭混淆,也要相应设置shrinkResources false,如果为true也会打包失败;如果单独设置minifyEnabled true,并且混淆文件中的规则设置正确,也是可以成功打包的。
 buildTypes {
        release {
          ......
          minifyEnabled true
          shrinkResources true
          ......
   }
}
  • 混淆配置
    混淆配置一般用于发布上线的Apk中,混淆Apk有两个好处:
    • 可以防止你的项目被他人反编译获取源码(如何反编译Apk这里不做讨论,反编译用到的工具一般有apktool(反编译Apk 得到资源文件,其中smile 文件是 .java 文件编译后的文件,如果想破解Apk,需要熟悉smile语法,有兴趣大家可以去研究) ;dex2jar 将解压的Apk 中的dex文件转换成.jar文件;jd-gui 这个用具查看前面生成的.jar文件,到此就可以看到反编译Apk的代码,如果混淆,则会看到包名和类名用a b c等字符代替,让你无法阅读源码,如下图)
    • 可以减小Apk的体积,混淆代码,打包出来的Apk体积肯定会减小,这也是Apk瘦身的一种方案。
Android Studio Apk 打包 混淆_第10张图片
jd-gui查看反编译Apk源码(源码被混淆).png
  • 如何混淆

    • 首先在app目录下的build.gradle中做混淆配置,配置debug和release都使用proguard-rules.pro这个混淆规则文件,我们可以先debug测试混淆规则通过后才release打包正式版,如下图:
Android Studio Apk 打包 混淆_第11张图片
混淆配置 gradle.png
  • 其次,配置混淆规则文件,要配置混淆文件规则,首先要了解规则是什么,了解规则,具体的细节这里就不细说了,可以看这篇文章,传送门(http://www.jianshu.com/p/b471db6a01af)
 参数:

-include {filename}    从给定的文件中读取配置参数   
-basedirectory {directoryname}    指定基础目录为以后相对的档案名称   
-injars {class_path}    指定要处理的应用程序jar,war,ear和目录   
-outjars {class_path}    指定处理完后要输出的jar,war,ear和目录的名称   
-libraryjars {classpath}    指定要处理的应用程序jar,war,ear和目录所需要的程序库文件   
-dontskipnonpubliclibraryclasses    指定不去忽略非公共的库类。   
-dontskipnonpubliclibraryclassmembers    指定不去忽略包可见的库类的成员。

保留选项

-keep {Modifier} {class_specification}    保护指定的类文件和类的成员   
-keepclassmembers {modifier} {class_specification}    保护指定类的成员,如果此类受到保护他们会保护的更好   
-keepclasseswithmembers {class_specification}    保护指定的类和类的成员,但条件是所有指定的类和类成员是要存在。   
-keepnames {class_specification}    保护指定的类和类的成员的名称(如果他们不会压缩步骤中删除)   
-keepclassmembernames {class_specification}    保护指定的类的成员的名称(如果他们不会压缩步骤中删除)   
-keepclasseswithmembernames {class_specification}    保护指定的类和类的成员的名称,如果所有指定的类成员出席(在压缩步骤之后)   
-printseeds {filename}    列出类和类的成员-keep选项的清单,标准输出到给定的文件  

压缩

-dontshrink    不压缩输入的类文件   
-printusage {filename}   
-whyareyoukeeping {class_specification}  

优化

-dontoptimize    不优化输入的类文件   
-assumenosideeffects {class_specification}    优化时假设指定的方法,没有任何副作用   
-allowaccessmodification    优化时允许访问并修改有修饰符的类和类的成员  

混淆

-dontobfuscate    不混淆输入的类文件   
-printmapping {filename}   
-applymapping {filename}    重用映射增加混淆   
-obfuscationdictionary {filename}    使用给定文件中的关键字作为要混淆方法的名称   
-overloadaggressively    混淆时应用侵入式重载   
-useuniqueclassmembernames    确定统一的混淆类的成员名称来增加混淆   
-flattenpackagehierarchy {package_name}    重新包装所有重命名的包并放在给定的单一包中   
-repackageclass {package_name}    重新包装所有重命名的类文件中放在给定的单一包中   
-dontusemixedcaseclassnames    混淆时不会产生形形色色的类名   
-keepattributes {attribute_name,...}    保护给定的可选属性,例如LineNumberTable, LocalVariableTable, SourceFile, Deprecated, Synthetic, Signature, and InnerClasses.  
-renamesourcefileattribute {string}    设置源文件中给定的字符串常量  
  • 最后,贴出我打包项目的混淆配置规则文件,仅供参考,如果直接复制进你项目,可能会打包出错,具体需要根据自己的项目来酌情配置, 如果项目中使用了第三方的包,一般可以去官网找到相应的混淆配置。到此,混淆配置完成,这样打包出来的Apk的代码就已经实现混淆。
#指定代码的压缩级别
-optimizationpasses 5

#包明不混合大小写
-dontusemixedcaseclassnames

#不去忽略非公共的库类
-dontskipnonpubliclibraryclasses

 #优化  不优化输入的类文件
-dontoptimize

 #预校验
-dontpreverify

 #混淆时是否记录日志
-verbose

 # 混淆时所采用的算法
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*

#保护注解
-keepattributes *Annotation*

# 保持哪些类不被混淆
-keep public class * extends android.app.Fragment
-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.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
#如果有引用v4包可以添加下面这行
-keep public class * extends android.support.v4.app.Fragment


#忽略警告
-ignorewarning

##记录生成的日志数据,gradle build时在本项目根目录输出##
#apk 包内所有 class 的内部结构
-dump proguard/class_files.txt
#未混淆的类和成员
-printseeds proguard/seeds.txt
#列出从 apk 中删除的代码
-printusage proguard/unused.txt
#混淆前后的映射
-printmapping proguard/mapping.txt
########记录生成的日志数据,gradle build时 在本项目根目录输出-end######

#如果引用了v4或者v7包
-dontwarn android.support.**

####混淆保护自己项目的部分代码以及引用的第三方jar包library-end####



#保持 native 方法不被混淆
-keepclasseswithmembernames class * {
    native ;
}

#保持自定义控件类不被混淆
-keepclasseswithmembers class * {
    public (android.content.Context, android.util.AttributeSet);
}

#保持自定义控件类不被混淆
-keepclassmembers class * extends android.app.Activity {
   public void *(android.view.View);
}

-keep public class * extends android.view.View {
    public (android.content.Context);
    public (android.content.Context, android.util.AttributeSet);
    public (android.content.Context, android.util.AttributeSet, int);
    public void set*(...);
}

#保持 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 ;
    !private ;
    !private ;
    private void writeObject(java.io.ObjectOutputStream);
    private void readObject(java.io.ObjectInputStream);
    java.lang.Object writeReplace();
    java.lang.Object readResolve();
}

#保持枚举 enum 类不被混淆
-keepclassmembers enum * {
  public static **[] values();
  public static ** valueOf(java.lang.String);
}

-keepclassmembers class * {
    public void *ButtonClicked(android.view.View);
}

#不混淆资源类
-keepclassmembers class **.R$* {
    public static ;
}

#避免混淆泛型 如果混淆报错建议关掉
-keepattributes Signature

#移除Log类打印各个等级日志的代码,打正式包的时候可以做为禁log使用,这里可以作为禁止log打印的功能使用,另外的一种实现方案是通过BuildConfig.DEBUG的变量来控制
-assumenosideeffects class android.util.Log {
    public static *** v(...);
    public static *** i(...);
    public static *** d(...);
    public static *** w(...);
    public static *** e(...);
}

#gson
#如果用用到Gson解析包的,直接添加下面这几行就能成功混淆,不然会报错。
-keepattributes Signature
# Gson specific classes
-keep class sun.misc.Unsafe { *; }
# Application classes that will be serialized/deserialized over Gson
-keep class com.google.gson.** { *; }
-keep class com.google.gson.stream.** { *; }

#mob
-keep class android.net.http.SslError
-keep class android.webkit.**{*;}
-keep class cn.sharesdk.**{*;}
-keep class com.sina.**{*;}
-keep class m.framework.**{*;}
-keep class **.R$* {*;}
-keep class **.R{*;}
-dontwarn cn.sharesdk.**
-dontwarn **.R$*

#butterknife
-keep class butterknife.** { *; }
-dontwarn butterknife.internal.**
-keep class **$$ViewBinder { *; }

-keepclasseswithmembernames class * {
    @butterknife.* ;
}

-keepclasseswithmembernames class * {
    @butterknife.* ;
}

# 如果使用了Gson之类的工具要使被它解析的JavaBean类即实体类不被混淆。
-keep class com.lecloud.base.** { *; }
-keep class com.lecloud.imlib.** { *; }
-keep class com.lecloud.leblockmodelmanager.** { *; }


# --------------- 友盟统计避免混淆 -------------------------
-dontwarn Android.support.v4.**
-dontwarn org.apache.commons.net.**
-dontwarn com.tencent.**
-keepclasseswithmembernames class * {
    native ;
}
-keepclasseswithmembernames class * {
    public (Android.content.Context, Android.util.AttributeSet);
}
-keepclasseswithmembernames class * {
    public (Android.content.Context, Android.util.AttributeSet, int);
}
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}
-keep class * implements Android.os.Parcelable {
  public static final Android.os.Parcelable$Creator *;
}
-keepclasseswithmembers class * {
    public (Android.content.Context);
}
-dontshrink
-dontoptimize
-dontwarn com.google.Android.maps.**
-dontwarn Android.webkit.WebView
-dontwarn com.umeng.**
-dontwarn com.tencent.weibo.sdk.**
-dontwarn com.facebook.**
-keep enum com.facebook.**
-keepattributes Exceptions,InnerClasses,Signature
-keepattributes *Annotation*
-keepattributes SourceFile,LineNumberTable
-keep public interface com.facebook.**
-keep public interface com.tencent.**
-keep public interface com.umeng.socialize.**
-keep public interface com.umeng.socialize.sensor.**
-keep public interface com.umeng.scrshot.**
-keep public class com.umeng.socialize.* {*;}
-keep public class javax.**
-keep public class Android.webkit.**
-keep class com.facebook.**
-keep class com.umeng.scrshot.**
-keep public class com.tencent.** {*;}
-keep class com.umeng.socialize.sensor.**
-keep class com.tencent.mm.sdk.openapi.WXMediaMessage {*;}
-keep class com.tencent.mm.sdk.openapi.** implements com.tencent.mm.sdk.openapi.WXMediaMessage$IMediaObject {*;}
-keep class im.yixin.sdk.api.YXMessage {*;}
-keep class im.yixin.sdk.api.** implements im.yixin.sdk.api.YXMessage$YXMessageData{*;}
-keep public class [your_pkg].R$*{
    public static final int *;
}

#极光推送
-dontoptimize
-dontpreverify

-dontwarn cn.jpush.**
-keep class cn.jpush.** { *; }

#retroift
-dontwarn retrofit2.**
-keep class retrofit2.** { *; }
-keepattributes Signature
-keepattributes Exceptions

#ButterKnife
-keep class butterknife.** { *; }
-dontwarn butterknife.internal.**
-keep class **$$ViewBinder { *; }
-keepclasseswithmembernames class * {
    @butterknife.* ;
}

-keepclasseswithmembernames class * {
    @butterknife.* ;
}
#fastjson
-dontwarn com.alibaba.fastjson.**
-keep class com.alibaba.fastjson.** { *; }

#webView处理,项目中没有使用到webView忽略即可
-keepclassmembers class fqcn.of.javascript.interface.for.webview {
   public *;
}
-keepclassmembers class * extends android.webkit.webViewClient {
    public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
    public boolean *(android.webkit.WebView, java.lang.String);
}
-keepclassmembers class * extends android.webkit.webViewClient {
    public void *(android.webkit.webView, jav.lang.String);
}

#融云
-keepattributes Exceptions,InnerClasses

-keepattributes Signature

# RongCloud SDK
-keep class io.rong.** {*;}
-keep class * implements io.rong.imlib.model.MessageContent {*;}
-dontwarn io.rong.push.**
-dontnote com.xiaomi.**
-dontnote com.google.android.gms.gcm.**
-dontnote io.rong.**

# VoIP
-keep class io.agora.rtc.** {*;}

# Location
-keep class com.amap.api.**{*;}
-keep class com.amap.api.services.**{*;}

# 红包
-keep class com.google.gson.** { *; }
-keep class com.uuhelper.Application.** {*;}
-keep class net.sourceforge.zbar.** { *; }
-keep class com.google.android.gms.** { *; }
-keep class com.alipay.** {*;}
-keep class com.jrmf360.rylib.** {*;}

-ignorewarnings

#播放器框架
-keep class tv.danmaku.ijk.media.player.** {*;}

好了,本次记录就到这里了,如果大家有发现不对的地方,欢迎给我指出,大家一起学习进步。如果觉得文章对你有帮助,也请给我一个喜欢。

参考文章:http://blog.csdn.net/u012124438/article/details/54958757

你可能感兴趣的:(Android Studio Apk 打包 混淆)