开发一个优秀的Android应用你必须要加入的代码及配置

1 . 开发一个Android App我们一般需要继承Application来初始化一些配置,如下所示:

a. Application中有一个registerActivityLifecycleCallbacks()方法,可以在回调中把整个应用打开的Activity保存在集合中、销毁的Activity重集合中删除。个人觉得这种方式比BaseActivity的方式或者每打开一个Activity把当前的引用加入到集合中的方式优雅多了,推荐大家用这种方式。关闭应用也非常方便,遍历保存Activity的集合,�执行finish()就好。

/**
 * 注册ActivityListener
 */
private void registerActivityListener() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
        registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
                if (null == mActivitys) {
                    return;
                }
                mActivitys.add(activity);
            }
            @Override
            public void onActivityStarted(Activity activity) {}
            @Override
            public void onActivityResumed(Activity activity) {}
            @Override
            public void onActivityPaused(Activity activity) {}
            @Override
            public void onActivityStopped(Activity activity) {}
            @Override
            public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}
            @Override
            public void onActivityDestroyed(Activity activity) {
                if (null == activity && mActivitys.isEmpty()) {
                    return;
                }
                if (mActivitys.contains(activity)) {
                    mActivitys.remove(activity);
                }
            }
        });
    }
}

b. 严苛模式StrictMode:建议大家在Debug版本打开严苛模式。 例如:严苛模式下可以检测到你应用中没有关闭的流,可以减少OOM,如下所示,可以看到在在控制台严苛模式模式下的Log输出。

image.png

ThreadPolicy线程策略检测
自定义的耗时调用 使用detectCustomSlowCalls()开启
磁盘读取操作 使用detectDiskReads()开启
磁盘写入操作 使用detectDiskWrites()开启
网络操作 使用detectNetwork()开启

VmPolicy虚拟机策略检测
Activity泄露 使用detectActivityLeaks()开启
未关闭的Closable对象泄露 使用detectLeakedClosableObjects()开启
泄露的Sqlite对象 使用detectLeakedSqlLiteObjects()开启
检测实例数量 使用setClassInstanceLimit()开启

/**严苛模式主要检测两大问题,一个是线程策略,即TreadPolicy,另一个是VM策略,即VmPolicy。*/
if (AppConfig.IS_DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
    StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
    StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build());
}

c .在onCreate中,检测当前进程名称是否为应用包名,否则return (像百度地图等sdk需要在单独的进程中执行,会多次执行Application的onCreate()方法,所以为了只初始化一次应用配置,作此判断),代码如下:

 /**检测当前进程名称是否为应用包名,否则return (像百度地图等sdk需要在单独的进程中执行,会多次执行Application的onCreate()方法,所以为了只初始化一次应用配置,作此判断)*/
if (!CommonUtil.getCurProcessName(this).equals(getPackageName())) {
  return;
}

d . �Crash全局异常捕获,用到了一个第三方库,使用方法如下:

compile 'cat.ereza:customactivityoncrash:1.5.0'
CustomActivityOnCrash.install(this);

应用发生异常时customactivityoncrash库�会打开一个如下图所示的Activity页面,可以查看错误。


image.png

e .其他配置初始化

 private void init() {

    /**注册ActivityListener*/
    registerActivityListener();

    /**crash异常捕获*/
    if (AppConfig.IS_DEBUG) {
        CustomActivityOnCrash.install(this);
    }

    /**初始化base url*/
    if (AppConfig.IS_DEBUG) {
        final String base_url = SPUtils.getString(SPUtils.DEFAULT_FILE_NAME, SharePrefsConstant.SP_BASE_URL, "");
        if (TextUtils.isEmpty(base_url)) return;
        AppConfig.updateAllUrl(base_url);
    }

    /**初始化LeakCanary */
    if (AppConfig.IS_DEBUG) {
        if (LeakCanary.isInAnalyzerProcess(this)) {
            return;
        }
        LeakCanary.install(this);
    }
}

2 .�完整MyApplication代码,如下所示:

public class MyApplication extends Application {

    private static MyApplication application;

    /**
     * 维护Activity的List
     */
    private static List<Activity> mActivitys = Collections.synchronizedList(new LinkedList<Activity>());


    @Override
    public void onCreate() {
        /**严苛模式主要检测两大问题,一个是线程策略,即TreadPolicy,另一个是VM策略,即VmPolicy。*/
        if (AppConfig.IS_DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
            StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
            StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build());
        }
        super.onCreate();

        application = this;

        /**检测当前进程名称是否为应用包名,否则return (像百度地图等sdk需要在单独的进程中执行,会多次执行Application的onCreate()方法,所以为了只初始化一次应用配置,作此判断)*/
        if (!CommonUtil.getCurProcessName(this).equals(getPackageName())) {
            return;
        }

        /**初始化一些应用配置*/
        init();
    }

    private void init() {

        /**注册ActivityListener*/
        registerActivityListener();

        /**crash异常捕获*/
        if (AppConfig.IS_DEBUG) {
            CustomActivityOnCrash.install(this);
        }

        /**初始化base url*/
        if (AppConfig.IS_DEBUG) {
            final String base_url = SPUtils.getString(SPUtils.DEFAULT_FILE_NAME, SharePrefsConstant.SP_BASE_URL, "");
            if (TextUtils.isEmpty(base_url)) return;
            AppConfig.updateAllUrl(base_url);
        }

        /**初始化LeakCanary */
        if (AppConfig.IS_DEBUG) {
            if (LeakCanary.isInAnalyzerProcess(this)) {
                return;
            }
            LeakCanary.install(this);
        }
    }

    public static MyApplication getApplication() {
        return application;
    }


    public static void finishAllActivity() {
        if (mActivitys == null || mActivitys.isEmpty()) {
            return;
        }
        for (Activity activity : mActivitys) {
            activity.finish();
        }
    }


    /**
     * 获取当前Activity(栈中最后一个压入得)
     */
    public static Activity getCurrentActivity() {
        if (mActivitys == null || mActivitys.isEmpty()) {
            return null;
        }
        Activity activity = mActivitys.get(mActivitys.size() - 1);
        return activity;
    }

    /**
     * 注册ActivityListener
     */
    private void registerActivityListener() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
                @Override
                public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
                    if (null == mActivitys) {
                        return;
                    }
                    mActivitys.add(activity);
                }
                @Override
                public void onActivityStarted(Activity activity) {}
                @Override
                public void onActivityResumed(Activity activity) {}
                @Override
                public void onActivityPaused(Activity activity) {}
                @Override
                public void onActivityStopped(Activity activity) {}
                @Override
                public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}
                @Override
                public void onActivityDestroyed(Activity activity) {
                    if (null == activity && mActivitys.isEmpty()) {
                        return;
                    }
                    if (mActivitys.contains(activity)) {
                        mActivitys.remove(activity);
                    }
                }
            });
        }
    }
}

3 . 应用的入口SplashActivity的onCreate方法中要加入如下代码,解决首次安装应用,点击应用图标打开应用,点击home健回到桌面,再次点击应用图标,进入应用时多次初始化SplashActivity的问题

/**在应用的入口activity加入以下代码,解决首次安装应用,点击应用图标打开应用,点击home健回到桌面,再次点击应用图标,进入应用时多次初始化SplashActivity的问题*/
if ((getIntent().getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) {
    finish();
    return;
}
if (!isTaskRoot()) {
    finish();
    return;
}

4 . �app下的build.gradle配置,代码如下

apply plugin: 'com.android.application'
apply plugin: 'com.antfortune.freeline'

android {
    //国内镜像地址来下载  $ ./gradlew initFreeline -Pmirror
    freeline {
        hack true
        productFlavor 'zyp'
    }

    compileSdkVersion rootProject.ext.compileSdkVersion
    buildToolsVersion rootProject.ext.buildToolsVersion
    defaultConfig {
        applicationId "com.zyp"
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        versionCode rootProject.ext.versionCode
        versionName rootProject.ext.versionName
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        vectorDrawables.useSupportLibrary = true
    }

    signingConfigs {
        debug {
            storeFile file("debug.keystore")
        }
        release {
            storeFile file("release.keystore")
//            storeFile file("release.jks")
            keyAlias 'androiddebugkey'
            storePassword 'android'
            keyPassword 'android'
        }
    }

    buildTypes {
        debug {
            applicationIdSuffix ".debug"
            manifestPlaceholders = [app_label: "@string/app_name_debug"]
            manifestPlaceholders = [UMENG_CHANNEL_VALUE: "zyp"]
            manifestPlaceholders = [BAIDU_API_KEY_VALUE: "CC939499621bc8191c63799999b1f987", UMENG_APPKEY_VALUE: "4ef289045270156bd6999998"]

            buildConfigField "String", "BASE_URL", "\"https://api.test\""
            buildConfigField "String[]", "BASE_TEST_URL", "{\"https://api.test\", \"https://api.test1\", \"https://api.test2\", \"https://api.test3\", \"https://api.test4\"}"
            buildConfigField "boolean", "IS_LOG", "true"
            buildConfigField "boolean", "IS_DEBUG", "true"

            debuggable true
            zipAlignEnabled false
            shrinkResources false
            minifyEnabled false
            signingConfig signingConfigs.debug
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }

        release {
            manifestPlaceholders = [app_label: "@string/app_name"]
            manifestPlaceholders = [UMENG_CHANNEL_VALUE: "zyp"]
            manifestPlaceholders = [BAIDU_API_KEY_VALUE: "CC939499621bc8191c63799999b1f987", UMENG_APPKEY_VALUE: "4ef289045270156bd6999998"]

            buildConfigField "String", "BASE_URL", "\"https://api.release\""
            buildConfigField "String[]", "BASE_TEST_URL", "{\"https://api.release1\", \"https://api.release2\"}"
            buildConfigField "boolean", "IS_LOG", "false"
            buildConfigField "boolean", "IS_DEBUG", "false"


            debuggable false
            zipAlignEnabled true
            shrinkResources true
            minifyEnabled true
            signingConfig signingConfigs.release
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    applicationVariants.all { variant ->
        variant.outputs.each { output ->
            def outputFile = output.outputFile
            def fileName
            if (outputFile != null && outputFile.name.endsWith('.apk')) {
                // 输出apk名称为zyp_v1.0.0_wandoujia.apk
                if (variant.buildType.name.equals('release')) {
                    fileName = "zyp_v${defaultConfig.versionName}_${variant.productFlavors[0].name}_release.apk"
                } else if (variant.buildType.name.equals('debug')) {
                    fileName = "zyp_v${defaultConfig.versionName}_${variant.productFlavors[0].name}_debug.apk"
                }
                output.outputFile = new File(outputFile.parent, fileName)
            }
        }
    }

    // 友盟多渠道打包
    productFlavors {
        zyp {}
        //      _360yingyong {}
        //      yingyongbao {}
        //      baidushouzhu {}
        //      xiaomi {}
        //      wandoujia {}
        //      anzhi {}
        //      jifeng {}
        //      liantongwo {}
        //      yidongmm {}
        //      huawei {}
        //      lianxiang {}
        //      meizu {}
        //      oppo {}
        //      leshi {}
        //      mumayi {}
        //      nduowang {}
        //      wangyi {}
        //      sinaweibo {}
        //      sougoushouzhu {}
        //      google {}
        //      samsung {}
        //      vivo{}
    }

    productFlavors.all { flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]

    }

    dexOptions {
        preDexLibraries = false
        jumboMode = false
        javaMaxHeapSize "4g"
    }

    lintOptions {
        checkReleaseBuilds false
        abortOnError false
    }
    packagingOptions {
        exclude 'META-INF/LICENSE.txt'
        exclude 'LICENSE.txt'
        exclude 'META-INF/NOTICE.txt'
        exclude 'META-INF/DEPENDENCIES.txt'
        exclude 'META-INF/LICENSE.txt'
        exclude 'META-INF/NOTICE'
        exclude 'META-INF/LICENSE'
        exclude 'META-INF/DEPENDENCIES'
        exclude 'META-INF/notice.txt'
        exclude 'META-INF/license.txt'
        exclude 'META-INF/dependencies.txt'
        exclude 'META-INF/LGPL2.1'
        exclude 'META-INF/rxjava.properties'
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_7
        targetCompatibility JavaVersion.VERSION_1_7
    }

    sourceSets.main {
//        jniLibs.srcDirs = ['libs/jniLibs']
    }


}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile("com.android.support.test.espresso:espresso-core:${espressoCore}", {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    testCompile "junit:junit:${junit}"

    compile "com.android.support:appcompat-v7:${appcompatV7}"
    compile "com.android.support.constraint:constraint-layout:${constraintLayout}"
    compile "com.android.support:design:${design}"
    compile "com.android.support:support-vector-drawable:${supportVectorDrawable}"

    compile "cat.ereza:customactivityoncrash:${customactivityoncrash}"

    debugCompile "com.squareup.leakcanary:leakcanary-android:${leakcanaryVersion}"
    releaseCompile "com.squareup.leakcanary:leakcanary-android-no-op:${leakcanaryVersion}"
    testCompile "com.squareup.leakcanary:leakcanary-android-no-op:${leakcanaryVersion}"
  
}

5 . 应用根目录下得build.gradle文件配置,代码如下:

// Top-level build file where you can add configuration options common to all sub-projects/modules.

ext {
    compileSdkVersion = 25
    buildToolsVersion = "25.0.3"
    minSdkVersion = 15
    targetSdkVersion = 22
    versionCode = 1
    versionName = "1.0.0"

    espressoCore = "2.2.2"
    junit = "4.12"
    constraintLayout = "1.0.2"
    customactivityoncrash = "1.5.0"
    leakcanaryVersion = "1.5.1"
    appcompatV7 = "25.3.1"
    design = "25.3.1"
    supportVectorDrawable = "25.3.1"

}

buildscript {
    repositories {
        jcenter()
        mavenCentral()
        maven { url 'https://jitpack.io' }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.3.0'
        classpath 'com.antfortune.freeline:gradle:0.8.7'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

6 . 应用的混淆配置文件,代码如下:

-keepclassmembers class fqcn.of.javascript.interface.for.webview {
   public *;
}
#webView js调用不混淆,否则取不到数据
-keepclassmembers class com.zyp.ui.WebActivity$* {
    <methods>;
}

#shrink,测试后发现会将一些无效代码给移除,即没有被显示调用的代码,该选项 表示 不启用 shrink。
#-dontshrink
#指定重新打包,所有包重命名,这个选项会进一步模糊包名,将包里的类混淆成n个再重新打包到一个个的package中
#-flattenpackagehierarchy
#优化时允许访问并修改有修饰符的类和类的成员
-allowaccessmodification
#不跳过(混淆) jars中的 非public classes   默认选项
-dontskipnonpubliclibraryclassmembers
#忽略警告
-ignorewarnings
#指定代码的压缩级别
-optimizationpasses 5
#不使用大小写混合类名
-dontusemixedcaseclassnames
#不去忽略非公共的库类
-dontskipnonpubliclibraryclasses
#不启用优化  不优化输入的类文件
-dontoptimize
#不预校验
-dontpreverify
#混淆时是否记录日志
-verbose
#混淆时所采用的算法
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
#保护注解
-renamesourcefileattribute SourceFile
#保持源文件和行号的信息,用于混淆后定位错误位置
-keepattributes SourceFile,LineNumberTable
#保持含有Annotation字符串的 attributes
-keepattributes *Annotation*
#过滤泛型
-keepattributes Signature
-keepattributes Exceptions,InnerClasses

-dontwarn org.apache.**
-dontwarn android.support.**

#基础配置
# 保持哪些类不被混淆
# 系统组件
-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
#自定义View
-keep public class * extends android.view.View
# V4,V7
-keep class android.support.constraint.**{ *; }
-keep class android.support.v4.**{ *; }
-keep class android.support.v7.**{ *; }
-keep class android.webkit.**{*;}
-keep interface android.support.v4.app.** { *; }
#保持 本化方法及其类声明
-keepclasseswithmembers class * {
    native <methods>;
}
#保持view的子类成员: getter setter
-keepclassmembers public class * extends android.view.View {
   void set*(***);
   *** get*();
}
#保持Activity的子类成员:参数为一个View类型的方法   如setContentView(View v)
-keepclassmembers class * extends android.app.Activity {
   public void *(android.view.View);
}
#保持枚举类的成员:values方法和valueOf  (每个enum 类都默认有这两个方法)
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}
#保持Parcelable的实现类和它的成员:类型为android.os.Parcelable$Creator 名字任意的 属性
-keep class * implements android.os.Parcelable {
  public static final android.os.Parcelable$Creator *;
}
#保持 任意包名.R类的类成员属性。  即保护R文件中的属性名不变
-keepclassmembers class **.R$* {
    public static <fields>;
}

# 记录生成的日志数据,在 proguard 目录下

-dump class_files.txt

-printseeds seeds.txt

-printusage unused.txt

-printmapping mapping.txt


-keepclasseswithmembernames class * {
    native <methods>;
}

-keepclasseswithmembers class * {
    public <init>(android.content.Context, android.util.AttributeSet);
}

-keepclasseswithmembers class * {
    public <init>(android.content.Context, android.util.AttributeSet, int);
}

-keepclassmembers class * extends android.app.Activity {
   public void *(android.view.View);
}

-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

-keep class * implements android.os.Parcelable {
  public static final android.os.Parcelable$Creator *;
}
-keepnames class * implements android.os.Parcelable {
    public static final ** CREATOR;
}

-dontwarn java.util.**
-keep class java.util.** {*; }

-dontwarn org.apache.http.**
-keep class org.apache.http.** {*; }

-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 public class * implements java.io.Serializable {*;}

-keepclassmembers class **.R$* {
    public static <fields>;
}

# ======================= 以上是混淆的基础配置================================
# ======================= 以下是混淆的第三方包的配置================================


# ======================= Gson 混淆================================
-keepattributes *Annotation*
-keep class sun.misc.Unsafe { *; }
-keep class com.idea.fifaalarmclock.entity.***
-keep class com.google.gson.stream.** { *; }

# ======================= Umeng 混淆================================
-keepclassmembers class * {
   public <init> (org.json.JSONObject);
}
#这是由于SDK中的部分代码使用反射来调用构造函数, 如果被混淆掉, 在运行时会提示"NoSuchMethod"错误。
#另外,由于SDK需要引用导入工程的资源文件,通过了反射机制得到资源引用文件R.java,但是在开发者通过proguard等混淆/优化工具处理apk时,proguard可能会将R.java删除,如果遇到这个问题,请在proguard配置文件中添加keep命令如:
-keep public class com.zyp.R$*{
    public static final int *;
}
-keep class com.umeng.**
-keep public class com.idea.fifaalarmclock.app.R$*{
    public static final int *;
}

-keep public class com.umeng.fb.ui.ThreadView {
}
-dontwarn com.umeng.**
-dontwarn org.apache.commons.**
-keep public class * extends com.umeng.**
-keep class com.umeng.** {*; }


# ======================= pinying4j 混淆================================
-dontwarn com.hp.hpl.sparta.**
-keep class com.hp.hpl.sparta.**{*;}
-dontwarn net.sourceforge.pinyin4j.**
-keep class net.sourceforge.pinyin4j.**{*;}
-dontwarn demo.**
-keep class demo.**{*;}

# ======================= volley 混淆================================
-dontwarn com.android.volley.**
-keep class com.android.volley.**{*;}

# ======================= fastjson 混淆================================
-dontwarn com.alibaba.fastjson.**
-keep class com.alibaba.fastjson.** { *; }
# ======================= alipay 混淆================================
-keep class com.alipay.android.app.IAlixPay{*;}
-keep class com.alipay.android.app.IAlixPay$Stub{*;}
-keep class com.alipay.android.app.IRemoteServiceCallback{*;}
-keep class com.alipay.android.app.IRemoteServiceCallback$Stub{*;}
-keep class com.alipay.sdk.app.PayTask{ public *;}
-keep class com.alipay.sdk.app.AuthTask{ public *;}
-keep class com.alipay.mobilesecuritysdk.*
-keep class com.ut.*

# ======================= weixin 混淆================================
-keep class com.tencent.mm.** { *; }

# ======================= okio ================================
-dontwarn okio.**
-keep class okio.**{*;}
-keep interface okio.**{*;}


# ======================= activation.jar ================================
-dontwarn javax.activation.**
-keep class javax.activation.**{*;}
-dontwarn com.sun.activation.registries.**
-keep class com.sun.activation.registries.**{*;}

# ======================= additionnal.jar ================================
-dontwarn myjava.awt.datatransfer.**
-keep class myjava.awt.datatransfer.**{*;}
-dontwarn org.apache.harmony.**
-keep class org.apache.harmony.**{*;}


# ======================= RxAndroid,RxJava ================================
-dontwarn sun.misc.**
-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
   long producerIndex;
   long consumerIndex;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
    rx.internal.util.atomic.LinkedQueueNode producerNode;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef {
    rx.internal.util.atomic.LinkedQueueNode consumerNode;
}

#======================= httpmime =======================
-keep class org.apache.http.entity.mime.** {*;}
#======================= zxing =======================
-keep class com.google.zxing.** { *; }

#======================= simplelatlng =======================
-keep class com.javadocmd.simplelatlng.** {*;}

# =======================universal-image-loader ================================
-keep class com.nostra13.universalimageloader.** {*;}

# ======================= 百度地图  ================================
-keep class com.baidu.** {*;}
-keep class vi.com.** {*;}
-dontwarn com.baidu.**

# ======================= 高德地图  ================================
# 3D 地图
-keep   class com.amap.api.maps.**{*;}
-keep   class com.autonavi.amap.mapcore.*{*;}
-keep   class com.amap.api.trace.**{*;}

# 定位
-keep class com.amap.api.location.**{*;}
-keep class com.amap.api.fence.**{*;}
-keep class com.autonavi.aps.amapapi.model.**{*;}

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

#2D地图
-keep class com.amap.api.maps2d.**{*;}
-keep class com.amap.api.mapcore2d.**{*;}

#导航
-keep class com.amap.api.navi.**{*;}
-keep class com.autonavi.**{*;}

#讯飞
-keep class com.iflytek.**{*;}


# ======================= app javabean 混淆================================
-keep class com.zyp.bean.**{*;}


##---------------Begin:retrofit+rxjava----------------------
-dontwarn javax.annotation.**
-dontwarn javax.inject.**
# OkHttp3
-dontwarn com.squareup.okhttp.**
-keep class com.squareup.okhttp.** { *;}
-keep interface okhttp3.**{*;}
-dontwarn okhttp3.logging.**
-keep class okhttp3.internal.**{*;}
-dontwarn okhttp3.**
-keep class okhttp3.** { *;}
-dontwarn okio.**
# Retrofit
-dontwarn retrofit2.**
-keep class retrofit2.** { *; }
-keepattributes Signature
-keepattributes Exceptions
# RxJava RxAndroid
-dontwarn sun.misc.**
-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
    long producerIndex;
    long consumerIndex;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
    rx.internal.util.atomic.LinkedQueueNode producerNode;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef {
    rx.internal.util.atomic.LinkedQueueNode consumerNode;
}


-dontnote android.webkit.**
-dontnote java.util.**

##---------------Inmobi----------------------

-keepattributes SourceFile,LineNumberTable -keep class com.inmobi.** { *; }
-keep public class com.google.android.gms.**
-dontwarn com.google.android.gms.**
-dontwarn com.squareup.picasso.**
-keep class com.google.android.gms.ads.identifier.AdvertisingIdClient{
public *;
}
# skip the Picasso library classes
-keep class com.squareup.picasso.** {*;}
-dontwarn com.squareup.picasso.**
-dontwarn com.squareup.okhttp.**
# skip Moat classes
-keep class com.moat.** {*;}
-dontwarn com.moat.**

7 . 此Demo的功能,可以在应用内切换Base_Url,退出登录时还原默认的Base_Url ,导出应用的私有数据到SD卡上,个人觉得这几个功能,是我们开发app时很需要的功能,如下所示:

image.png

8 . 混淆前后文件目录变化如下图所示,其中mapping.txt文件为混淆的映射文件,每个版本要注意保存起来(Umeng上需要导入这个文件的)。

image.png

9 . 对系统Log类的封装Logger类,可以在控制台打印出如下图所示的功能,主要实现代码如下:

StackTraceElement stackTrace = Thread.currentThread().getStackTrace()[4];
StringBuilder sb = new StringBuilder();
sb.append("(")
        .append(stackTrace.getFileName())
        .append(":")
        .append(stackTrace.getLineNumber())
        .append(")")
        .append("#")
        .append(stackTrace.getMethodName())
        .append(":")
        .append(CommonUtil.unicode2GBK(sb1.toString()));//服务器返回的汉字可能是unicode编码,所以在此转成GBK
image.png

10 . 最后还有一个小Tips要送大家,大家其实发现了我应用中使用的全是AppConfig.IS_DEBUG而不是BuildConfig.DEBUG,这是为什么呢?主要是为了避免导错BuildConfig的包名,如下图所示:

image.png

看看随便进入一个android.support.design的BuildConfig,BuildConfig.DEBUG其实是为false的,其实我们希望BuildConfig.DEBUG为true,�但是事与愿违,而且这个错误还不容易发现。


image.png

为什么用AppConfig.IS_DEBUG就不会出现这样的问题呢,因为这是在我们自己的app下定义的,在那定义的?如下图所示,其实是在build.gradle中定义的,build.gradle可以定义很多类型的变量,可以很方便的达到我们需要的效果:

image.png

而在AppConfig我又封装了一层,避免了其他library中的build.gradle也定义了IS_DEBUG字段,所以只要AppConfig中的导包正确,就不会出现错误了,所以使用build.gradle中定义的字段时候,请务必再封装一层,代码如下图所示:

import com.zyp.BuildConfig;

public class AppConfig {
    /**在build.gradle中配置的变量*/
    public static String BASE_URL = BuildConfig.BASE_URL;
    /** 默认的base url */
    public final static String DEFAULT_BASE_URL = BuildConfig.BASE_URL;
    public final static String APPLICATION_ID = BuildConfig.APPLICATION_ID;
    public final static int VERSION_CODE = BuildConfig.VERSION_CODE;
    public final static boolean IS_LOG = BuildConfig.IS_LOG;
    public final static boolean IS_DEBUG = BuildConfig.IS_DEBUG;
    public final static String[] BASE_TEST_URL = BuildConfig.BASE_TEST_URL;
}

Demo下载地址
GitHub地址

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,569评论 25 707
  • 1.介绍 如果你正在查阅build.gradle文件的所有可选项,请点击这里进行查阅:DSL参考 1.1新构建系统...
    Chuckiefan阅读 12,011评论 8 72
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,368评论 6 343
  • 我的包给人拿着了。 我心里只有这么一个想法,然后五雷就轰的一声让人从头到脚地发麻。 1967年隆冬,我...
    杰克逊的Miss羊驼阅读 281评论 0 2