开发一个优秀的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.pngThreadPolicy线程策略检测
自定义的耗时调用 使用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.png8 . 混淆前后文件目录变化如下图所示,其中mapping.txt文件为混淆的映射文件,每个版本要注意保存起来(Umeng上需要导入这个文件的)。
image.png9 . 对系统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;
}