问题

今天再给项目配置混淆打release包时,遇到了个很尴尬的问题,项目打正式包下奔溃,打测试包正常运行。下面是打包配置(app下的build.gradle),关于为什么要proguard应用程序,请看这篇文章为什么每个人都应该将ProGuard用于他们的Android应用程序

android {    compileSdkVersion rootProject.ext.android.compileSdkVersion    defaultConfig {        applicationId "com.kanghanbin.wanandroid"        minSdkVersion rootProject.ext.android.minSdkVersion        targetSdkVersion rootProject.ext.android.targetSdkVersion        versionName rootProject.ext.android.versionName        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"        ndk {            // 设置支持的SO库架构            abiFilters 'armeabi', 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a'        }    }    signingConfigs {        release {            //RELEASE_KEY_ALIAS等常量放在gradle.properties文件中            keyAlias RELEASE_KEY_ALIAS            keyPassword RELEASE_KEY_PASSWORD            storeFile file(RELEASE_STORE_FILE)            storePassword RELEASE_STORE_PASSWORD        }    }    buildTypes {        release {            minifyEnabled true            shrinkResources true // 自动清理无用资源            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'            signingConfig signingConfigs.release        }        debug {            signingConfig signingConfigs.release            minifyEnabled false            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'        }    }    //只需禁用lint警告。有时应用不会发布到Google Play,因此不需要深层链接等:    lintOptions {        disable 'GoogleAppIndexingWarning'        baseline file("lint-baseline.xml")        checkReleaseBuilds false        abortOnError false    }}

然后我就将buildType为debug时的minifyEnabled 也改为true,在手机上调试

 debug {            signingConfig signingConfigs.release            minifyEnabled true            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'        }

不出意外,来了个NPE

定位错误是在BaseSubscriber的61行代码报错

   if (((ApiException) t).getCode() == Constant.UN_LOGIN                    || t.getMessage().equals(Constant.UN_LOGIN_MESSAGE)) {                mView.startLoginActivity();            }            mView.showToast(t.getMessage());

发现就是t.getmessage为空然后我就多加了个判断,BaseSubscriber类的完整代码如下

public abstract class BaseSubscriber extends ResourceSubscriber {    private BaseView mView;    private String mErrorMsg = "";    private boolean isShowErrorState = true;    public BaseSubscriber(BaseView mView) {        this.mView = mView;    }    public BaseSubscriber(BaseView mView, boolean isShowErrorState) {        this.mView = mView;        this.isShowErrorState = isShowErrorState;    }    public BaseSubscriber(BaseView mView, String mErrorMsg) {        this.mView = mView;        this.mErrorMsg = mErrorMsg;    }    public BaseSubscriber(BaseView mView, String mErrorMsg, boolean isShowErrorState) {        this.mView = mView;        this.mErrorMsg = mErrorMsg;        this.isShowErrorState = isShowErrorState;    }    @Override    public void onComplete() {    }    @Override    public void onError(Throwable t) {        if (mView == null) {            return;        }        if (mErrorMsg != null && !TextUtils.isEmpty(mErrorMsg)) {            mView.showToast(mErrorMsg);        } else if (t instanceof ApiException) {            if (((ApiException) t).getCode() == Constant.UN_LOGIN                    || t.getMessage() == null                    || t.getMessage().equals(Constant.UN_LOGIN_MESSAGE)) {                mView.startLoginActivity();            }            mView.showToast(t.getMessage());        } else if (t instanceof HttpException) {            mView.showToast(MyApplication.getInstance().getString(R.string.data_fail));        } else {            mView.showToast(MyApplication.getInstance().getString(R.string.unknow_error));        }        if (isShowErrorState) {            mView.showFailed();        }    }}

但是我就纳闷为啥会走到onError里,回头一想既然不混淆没问题肯定是混淆规则有问题

然后我就开始看混淆规则,排查了一遍发现这里用到的retrofit和rxjava2混淆都没问题呢,可是什么导致的一直走onerror。最后发现我只对项目实体类进行了keep

#-----------处理实体类---------------# 在开发的时候我们可以将所有的实体类放在一个包内,这样我们写一次混淆就行了。-keep class com.kanghanbin.wanandroid.model.bean.**{ *; }

并没有将自己封装的BaseResponse类进行keep,因为这里的BaseResponse用于http统一响应。BaseResponse完整代码:

/** * 创建时间:2018/10/26 * 编写人:kanghb * 功能描述:封装http响应格式 */public class BaseResponse {    /**     * data : null     * errorCode : -1     * errorMsg : 账号密码不匹配!     */    private T data;    private int errorCode;    private String errorMsg;    public T getData() {        return data;    }    public void setData(T data) {        this.data = data;    }    public int getErrorCode() {        return errorCode;    }    public void setErrorCode(int errorCode) {        this.errorCode = errorCode;    }    public String getErrorMsg() {        return errorMsg;    }    public void setErrorMsg(String errorMsg) {        this.errorMsg = errorMsg;    }    @Override    public String toString() {        return "BaseResponse{" +                "data=" + data +                ", errorCode=" + errorCode +                ", errorMsg='" + errorMsg + '\'' +                '}';    }}

BaseResponse用法举例:

        addSubscribe(apiService.login(username, password)                .compose(RxUtil.>rxFlowableSchedulerHelper())                .compose(RxUtil.handleResult())                .subscribeWith(new BaseSubscriber(mView) {                    @Override                    public void onNext(UserBean userBean) {                        mView.showToast("登录成功");                        mView.gotoMain();                        sharePreferencesHelper.setLoginAccount(username);                        sharePreferencesHelper.setLoginPassword(password);                        sharePreferencesHelper.setLoginStatus(true);                        RxBus.getDefault().post(new EventLogin(true));                    }                }));

解决

在proguard-rules.pro中加入完美结局问题,正式包也可以正常运行了

#-----------处理实体类---------------# 在开发的时候我们可以将所有的实体类放在一个包内,这样我们写一次混淆就行了。-keep class com.kanghanbin.wanandroid.model.bean.**{ *; }-keep class com.kanghanbin.wanandroid.http.BaseResponse{ *; }

科普一下

多简化序列化和反序列化这些字段的工具都依赖于反射。GSON,Retrofit,Firebase - 它们都检查数据类中的字段名称,并将它们转换为另一种表示形式(例如:),{“name”: “Sue”, “age”: 28}用于传输或存储。当他们将数据读入Java对象时会发生同样的事情 - 他们看到一个键值对“name”:”John”并尝试通过查找String name字段将其应用于Java对象。

结论:我们不能让ProGuard重命名或删除这些数据类中的任何字段,因为它们必须与序列化格式匹配。可以安全地@Keep在整个类上添加注释或在所有模型上添加通配符规则:

-keep class com.kanghanbin.wanandroid.model.bean.**{ *; }

更多相关文章

  1. Android(安卓)库 Gson
  2. android webkit 内核
  3. Android(安卓)drivers/switch驱动详解(用于通过GPIO状态检测耳机
  4. Android中,如何在其他类调用Activity的方法,适用于类似场景
  5. Android(安卓)2.0以后的Contacts API--ContactsContract
  6. AndroidManifest文件格式、Resource.arsc文件格式解析与混淆
  7. android 混淆时出现的一些问题
  8. AndroidManifest.xml文件详解(instrumentation)
  9. Android(安卓)app的混淆打包

随机推荐

  1. android开发退出程序的几种方式
  2. 【linkify】Android(安卓)Linkify介绍
  3. 【Android】一个简单又实用的toolbar
  4. Android(安卓)Studio 4.0 设置代理proxy
  5. Android(安卓)SO逆向-流程控制语句及表达
  6. Android(安卓)自定义控件一 带圆形进度的
  7. Android(安卓)5.x theme: AppCompatActiv
  8. Android客户端调用Asp.net的WebService
  9. Android(安卓)Studio 打包国际化报错以及
  10. Android不使用第三方升级库实现应用升级