美文网首页
Gradle多渠道打包以及混淆配置总结

Gradle多渠道打包以及混淆配置总结

作者: 秉心说 | 来源:发表于2016-12-05 16:35 被阅读1417次

    一. 多渠道打包

    当我们想了解产品在不同的应用市场的表现时,会在项目中加入一些统计服务,例如友盟,Bugly等等,这就需要我们给分发到不同应用市场的apk加个特定的标示——渠道名。每个应用市场的apk有自己特定的渠道名,这样这些第三方统计服务就可以统计到相应的数据了。

    1.给一个渠道打包

    这里以Bugly为例,我们在AndroidManifest.xml文件中添加一个标签如下:

    <meta-data android:name="BUGLY_APP_CHANNEL" android:value="xiaomi" />

    不同的统计服务只是属性的name不一样,友盟统计对应的name是UMENG_CHANNEL。通过改变value值,就给每个apk打上了不同的“标签”。当然,这肯定不是我们想要的,当你面临十几个,甚至几十个渠道的时候,难道要这个打包几十次吗?目前大多数项目都是通过Gradle构建的,通过Gradle我们可以轻松的完成多渠道打包。

    2.多渠道打包

    首先既然是多渠道打包,清单文件中的标签值肯定就不是一个固定值,更改AndroidManifest.xml如下:

    <meta-data android:name="BUGLY_APP_CHANNEL" android:value="${BUGLY_APP_CHANNEL_VALUE}" />

    在项目的gradle文件添加如下代码:

    productFlavors {
        xiaomi {}
        baidu {}
        huawei {}
    }
    
    productFlavors.all {
        flavor -> flavor.manifestPlaceholders = [BUGLY_APP_CHANNEL_VALUE: name]
    }
    

    通过配置productFlavors就可以实现多渠道打包了。简单说下几种打包方式:

    1.Build-->Generate Signed Apk-->选择签名文件-->选择需要打包的渠道-->Finish

    2.在Android Studio自带命令行工具Terminal中输入gradlew assembleRelease即可。

    3.单击Android Studio右侧的Gradle任务栏,如下所示:

    单击assemble即可为所有渠道打包,单击下面的assembleBaidu assembleHuawei assembleXiaomi可以为单个渠道打包。

    二.混淆配置

    代码混淆(Obfuscated code)是将计算机程序的代码,转换成一种功能上等价,但是难于阅读和理解的形式的行为。代码混淆可以用于程序源代码,也可以用于程序编译而成的中间代码。执行代码混淆的程序被称作代码混淆器。目前已经存在许多种功能各异的代码混淆器。————维基百科

    1.代码混淆配置

    代码混淆是我们为apk穿上的第一层盔甲,也许并不能完全的保障我们的源代码安全,但是无疑给他人理解我们的代码增添了很大的难度。同样在gradle文件中进行相应的配置。

    signingConfigs {
        debug {
            // No debug config
        }
    
        release {
            storeFile file("../yourapp.keystore")
            storePassword "your password"
            keyAlias "your alias"
            keyPassword "your password"
        }
    }
    

    这段代码配置了debug模式下使用默认的签名文件,release包使用指定的签名文件。

    buildTypes {
        debug {
            versionNameSuffix "_debug"
            minifyEnabled false
            zipAlignEnabled false
            shrinkResources false
            signingConfig signingConfigs.debug
        }
        release {
            minifyEnabled true
            zipAlignEnabled true
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release
    
            applicationVariants.all { variant ->
                variant.outputs.each { output ->
                    def outputFile = output.outputFile
                    if (outputFile != null && outputFile.name.endsWith('.apk')) {
                        // 输出apk名称为Everything_v1.0_2015-01-15_wandoujia.apk
                        def fileName = "Everything_v${defaultConfig.versionName}_${releaseTime()}_${variant.productFlavors[0].name}.apk"
                        output.outputFile = new File(outputFile.parent, fileName)
                    }
                }
            }
        }
    }
    

    这段同样给debug包和release包分别设置了不同的属性,这里解释一下这几个属性的含义:

    • minifyEnabled 是否开启混淆
    • zipAlignEnabled 是否优化apk文件,将apk文件中未压缩的数据在4个字节边界上对齐,具体见改善android性能工具:Zipalign
    • shrinkResources 是否去除无用资源,任何在编译过程中没有用到的资源或者代码都会被删除,可以有效减小apk体积
    • proguardFiles 指定混淆规则文件

    2.示例Gradle文件

    附上一个完整的Gradle文件:

    apply plugin: 'com.android.application'
    apply plugin: 'android-apt'
    
    android {
    compileSdkVersion 25
    buildToolsVersion "25.0.0"
    defaultConfig {
        applicationId "your package name"
        minSdkVersion 15
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    
    buildTypes {
        debug {
            versionNameSuffix "_debug"
            minifyEnabled false
            zipAlignEnabled false
            shrinkResources false
            signingConfig signingConfigs.debug
        }
        release {
            minifyEnabled true
            zipAlignEnabled true
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.debug
    
            applicationVariants.all { variant ->
                variant.outputs.each { output ->
                    def outputFile = output.outputFile
                    if (outputFile != null && outputFile.name.endsWith('.apk')) {
                        // 输出apk名称为Everything_v1.0_2015-01-15_huawei.apk
                        def fileName = "Everything_v${defaultConfig.versionName}_${releaseTime()}_${variant.productFlavors[0].name}.apk"
                        output.outputFile = new File(outputFile.parent, fileName)
                    }
                }
            }
        }
    }
    
    signingConfigs {
        debug {
            // No debug config
        }
    
        release {
            storeFile file("../yourapp.keystore")
            storePassword "your password"
            keyAlias "your alias"
            keyPassword "your password"
        }
    }
    
    productFlavors {
        xiaomi {}
        baidu {}
        huawei {}
    }
    
    productFlavors.all {
        flavor -> flavor.manifestPlaceholders = [BUGLY_APP_CHANNEL_VALUE: name]
    }
    }
    
    def releaseTime() {
      return new Date().format("yyyyMMdd", TimeZone.getTimeZone("UTC"))
    }
    
    dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:25.0.0'
    testCompile 'junit:junit:4.12'
    compile 'com.jakewharton:butterknife:8.4.0'
    apt 'com.jakewharton:butterknife-compiler:8.4.0'
    compile 'com.android.support:recyclerview-v7:25.0.0'
    compile 'com.squareup.retrofit2:retrofit:2.1.0'
    compile 'com.squareup.retrofit2:converter-gson:2.1.0'
    compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
    compile 'io.reactivex:rxjava:1.2.2'
    compile 'io.reactivex:rxandroid:1.2.1'
    compile 'com.android.support:design:25.0.0'
    compile files('libs/AMap_Location_V3.1.0_20161027.jar')
    compile 'org.greenrobot:eventbus:3.0.0'
    compile 'cn.aigestudio.datepicker:DatePicker:2.2.0'
    compile 'com.squareup.okhttp3:logging-interceptor:3.3.1'
    compile 'com.tencent.bugly:crashreport_upgrade:latest.release'
    
    debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5'
    releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
    testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
    }
    

    3.混淆规则示例

    直接上示例吧,

    -ignorewarnings                     # 忽略警告,避免打包时某些警告出现
    -optimizationpasses 5               # 指定代码的压缩级别
    -dontusemixedcaseclassnames         # 是否使用大小写混合
    -dontskipnonpubliclibraryclasses    # 是否混淆第三方jar
    -dontpreverify                      # 混淆时是否做预校验
    -verbose                            # 混淆时是否记录日志
    -optimizations !code/simplification/arithmetic,!field/*,!class/merging/*    # 混淆时所采用的算法
    
    # 保留了继承自Activity、Application这些类的子类
    # 因为这些子类有可能被外部调用
    # 比如第一行就保证了所有Activity的子类不要被混淆
    -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 * extends android.view.View
    -keep public class com.android.vending.licensing.ILicensingService
    -keep public class * extends android.support.v4.**
    
    # 保留Activity中的方法参数是view的方法,
    # 从而我们在layout里面编写onClick就不会影响
    -keepclassmembers class * extends android.app.Activity {
    public void * (android.view.View);
    }
    
    # 枚举类不能被混淆
    -keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
    }
    
    # 保留自定义控件(继承自View)不能被混淆
    -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*(***);
    *** get* ();
    }
    
    # 保留Parcelable序列化的类不能被混淆
    -keep class * implements android.os.Parcelable{
    public static final android.os.Parcelable$Creator *;
    }
    
    # 保留Serializable 序列化的类不被混淆
    -keepclassmembers class * implements java.io.Serializable {
       static final long serialVersionUID;
       private static final java.io.ObjectStreamField[] serialPersistentFields;
       !static !transient <fields>;
       private void writeObject(java.io.ObjectOutputStream);
       private void readObject(java.io.ObjectInputStream);
       java.lang.Object writeReplace();
       java.lang.Object readResolve();
    }
    
    # 对R文件下的所有类及其方法,都不能被混淆
    -keep public class com.foresee.R$*{
    public static final int *;
    }
    
    # 对于带有回调函数onXXEvent的,不能混淆
    -keepclassmembers class * {
    void *(**On*Event);
    }
    
    #实体类不混淆
    -keep class luyao.everything.enity.** {*;}
    
    #自定义控件不混淆
    -keep class luyao.everything.view.** {*;}
    

    以上是一些通用的混淆配置,当然也不是说可以完全复制过去用。最后两行中实体类和自定义View的相关代码就需要换成自己的包名,所以在开发过程中,建议将类按功能分包。除了这些配置以外,对于我们引用的依赖或者jar包,也需要进行相应的混淆配置,可以参考我的另一篇文章 收集一些Android常用混淆代码。

    有任何疑问,欢迎加群讨论:261386924

    相关文章

      网友评论

          本文标题:Gradle多渠道打包以及混淆配置总结

          本文链接:https://www.haomeiwen.com/subject/hmgkmttx.html