gradle 如何使用minifyEnabled true更正运行时错误

bzzcjhmw  于 2023-11-18  发布在  其他
关注(0)|答案(1)|浏览(93)

第一个月
我的应用程序运行良好,我只是想优化它的大小。

minifyEnabled true
            shrinkResources true

字符串
build.gradle
经过大量的试验和错误编译包和apk,我终于到达那里。
但现在,在运行时,我有一个错误:

2023-11-04 18:27:45.843 AndroidRuntime   E  FATAL EXCEPTION: main
                                            Process: XX.XX.XX, PID: 21345
                                            java.lang.NullPointerException: Attempt to invoke interface method 'java.lang.String java.lang.CharSequence.toString()' on a null object reference
                                                at java.text.Normalizer.normalize(Normalizer.java:167)
                                                at c4.e.v(SourceFile:1)
                                                at c4.e.a(SourceFile:1)
                                                at c4.d.compare(SourceFile:1)
                                                at java.util.TimSort.countRunAndMakeAscending(TimSort.java:355)
                                                at java.util.TimSort.sort(TimSort.java:220)
                                                at java.util.Arrays.sort(Arrays.java:1492)
                                                at java.util.ArrayList.sort(ArrayList.java:1470)
                                                at java.util.Collections.sort(Collections.java:206)
                                                at c4.e.D(SourceFile:1)
                                                at b4.g.H(SourceFile:1)
                                                at XX.XX.XX.RangtActivity.C(SourceFile:1)
                                                at XX.XX.XX.services.e$a.b(SourceFile:1)
                                                at o5.g$b$a.f(SourceFile:1)
                                                at o5.g$b$a.d(SourceFile:1)
                                                at o5.h.run(SourceFile:1)
                                                at android.os.Handler.handleCallback(Handler.java:938)
                                                at android.os.Handler.dispatchMessage(Handler.java:99)
                                                at android.os.Looper.loop(Looper.java:223)
                                                at android.app.ActivityThread.main(ActivityThread.java:7656)
                                                at java.lang.reflect.Method.invoke(Native Method)
                                                at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
                                                at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)


但是,我使用minifyEnabled false没有问题。否则我可以试着纠正错误。
我甚至不知道我的代码的哪一部分出错了。
我唯一的解决方案是不要试图减少应用程序的大小吗?
你会明白的,我根本不是Maven。
build.gradle:

apply plugin: 'com.android.application'

android {
    compileSdk 33

    signingConfigs {
        release {
            storeFile file(RELEASE_STORE_FILE)
            storePassword RELEASE_STORE_PASSWORD
            keyAlias RELEASE_KEY_ALIAS
            keyPassword RELEASE_KEY_PASSWORD
            // Optional, specify signing versions used
            v1SigningEnabled true
            v2SigningEnabled true
        }
    }

    defaultConfig {
        applicationId "XX.XX.XX"
        minSdkVersion 22
        targetSdkVersion 33
        versionCode 251
        versionName '2.5.1'
        testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
        vectorDrawables.useSupportLibrary = true
    }
    buildTypes {
        debug {
            buildConfigField "String", "VARIANT", "\"dev\""
            buildConfigField "boolean", "DEV", "true"
//            buildConfigField "int", "FOO", "52"
        }
        release {
            signingConfig signingConfigs.release
            buildConfigField "String", "VARIANT", "\"\""
            buildConfigField "boolean", "DEV", "false"
            minifyEnabled true
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            android.applicationVariants.all { variant ->
                def appName
                //Check if an applicationName property is supplied; if not use the name of the parent project.
                if (project.hasProperty("applicationName")) {
                    appName = applicationName
                } else {
                    appName = parent.name
                }
                variant.outputs.all { output ->
//                    outputFileName = "${appName}-${output.baseName}-${variant.versionName}.apk"
                    outputFileName = "${appName}-${variant.versionName}.apk"
//                def formattedDate = new Date().format('yyyyMMddHHmmss')
//                outputFileName = "${appName}-${output.baseName}-${variant.versionName}-${formattedDate}.apk"
                }
            }
        }
    }
    productFlavors {
        demo {
            applicationId "XX.XX.XXdemo"
//            dimension 'app'
            versionNameSuffix '-demo'
        }
        full {
//            dimension 'app'
        }
    }
    compileOptions {
        targetCompatibility = 1.8
        sourceCompatibility = 1.8
    }
    packagingOptions {
        resources {
            excludes += ['META-INF/LICENSE.md', 'META-INF/NOTICE.md']
        }
    }
    namespace 'XX.XX.XX'
    flavorDimensions += 'app'
}

dependencies {
    modules {
        module("org.jetbrains.kotlin:kotlin-stdlib-jdk7") {
            replacedBy("org.jetbrains.kotlin:kotlin-stdlib", "kotlin-stdlib-jdk7 is now part of kotlin-stdlib")
        }
        module("org.jetbrains.kotlin:kotlin-stdlib-jdk8") {
            replacedBy("org.jetbrains.kotlin:kotlin-stdlib", "kotlin-stdlib-jdk8 is now part of kotlin-stdlib")
        }
    }

    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
    implementation 'androidx.recyclerview:recyclerview:1.3.2'
    implementation 'androidx.cardview:cardview:1.0.0'
    implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
    implementation 'androidx.recyclerview:recyclerview:1.3.2'
    implementation 'androidx.preference:preference:1.2.1'
    implementation 'org.droidparts:droidparts-misc:3.2.5'
    implementation 'joda-time:joda-time:2.12.5'
    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
    implementation 'com.google.android.gms:play-services-vision:20.1.3'
    //noinspection GradleDependency
    implementation 'com.google.android.material:material:1.9.0'
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
    implementation 'com.sun.mail:android-mail:1.6.7'
    implementation 'com.sun.mail:android-activation:1.6.7'
    implementation 'androidx.appcompat:appcompat:1.6.1'
    implementation 'androidx.annotation:annotation:1.7.0'
    implementation 'com.github.yalantis:ucrop:2.2.6'
    implementation 'com.journeyapps:zxing-android-embedded:4.3.0'
}
allprojects {
   repositories {
      maven { url "https://jitpack.io" }
   }
}
configurations {
    configureEach {
        exclude group: 'androidx.lifecycle', module: 'lifecycle-viewmodel-ktx'
    }
}

static String firstMatchingSubstring(String taskName, String[] keys) {
    def lcName = taskName.toLowerCase()
    for(String key: keys) { if(lcName.contains(key.toLowerCase())) return key }
    return null
}

/**
 *
 * @param taskName e.g., bundleMyFlavorRelease or bundleRelease
 * @return
 */
String getBuildType(String taskName) {
    return firstMatchingSubstring(taskName, getBuildTypeNames())
}

/**
 *
 * @param taskName e.g., bundleMyFlavorRelease
 * @return
 */
String getFlavor(String taskName) {
    return firstMatchingSubstring(taskName, getProductFlavorNames())
}

String[] getBuildTypeNames() {
    def types = []
    android.buildTypes.configureEach { type -> types.add(type.name) }
    return types
}

String[] getProductFlavorNames() {
    def flavors = []
    android.productFlavors.configureEach { flavor -> flavors.add(flavor.name) }
    return flavors
}

tasks.configureEach { task ->
    def name = task.name
    //Skip some unnecessary tasks
    if (name.startsWith("bundle")
            && !name.contains("Classes")
            && !name.contains("Resources")
            && name != "bundle") {

        def renameTaskName = "rename${task.name.capitalize()}Aab"
        def version = "${android.defaultConfig.versionName}"
        def flavor = getFlavor(name)
        def type = getBuildType(name)
        if(flavor == null || type == null) return

        def outputName
        if (flavor == "full")
            outputName = "DontEat-$version"
        else
            outputName = "DontEat-$version-$flavor"

        tasks.register(renameTaskName) {
            def path = "${rootDir}/app/${flavor}/${type}/"
            def originalFile = "$path/app-${flavor}-${type}.aab"

            doLast {
                if (file("$originalFile").exists()) {
                    ant.move file: "$originalFile",
                            tofile: "$path/${outputName}.aab"
                }
            }
        }
        task.finalizedBy(renameTaskName)
    }
}


proguard-rules.pro:

# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
#   public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
-dontwarn org.joda.convert.**
-dontwarn org.joda.time.**
-keep class org.joda.time.** { *; }
-keep interface org.joda.time.** { *;}

# Retrofit does reflection on generic parameters. InnerClasses is required to use Signature and
# EnclosingMethod is required to use InnerClasses.
-keepattributes Signature, InnerClasses, EnclosingMethod

# Retrofit does reflection on method and parameter annotations.
-keepattributes RuntimeVisibleAnnotations, RuntimeVisibleParameterAnnotations

# Keep annotation default values (e.g., retrofit2.http.Field.encoded).
-keepattributes AnnotationDefault

# Retain service method parameters when optimizing.
-keepclassmembers,allowshrinking,allowobfuscation interface * {
    @retrofit2.http.* <methods>;
}

# Ignore annotation used for build tooling.
#-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement

# Ignore JSR 305 annotations for embedding nullability information.
-dontwarn javax.annotation.**

# Guarded by a NoClassDefFoundError try/catch and only used when on the classpath.
-dontwarn kotlin.Unit

# Top-level functions that can only be used by Kotlin.
-dontwarn retrofit2.KotlinExtensions
-dontwarn retrofit2.KotlinExtensions$*

# With R8 full mode, it sees no subtypes of Retrofit interfaces since they are created with a Proxy
# and replaces all potential values with null. Explicitly keeping the interfaces prevents this.
-if interface * { @retrofit2.http.* <methods>; }
-keep,allowobfuscation interface <1>

# Keep inherited services.
-if interface * { @retrofit2.http.* <methods>; }
-keep,allowobfuscation interface * extends <1>

# With R8 full mode generic signatures are stripped for classes that are not
# kept. Suspend functions are wrapped in continuations where the type argument
# is used.
-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation

# R8 full mode strips generic signatures from return types if not kept.
-if interface * { @retrofit2.http.* public *** *(...); }
-keep,allowoptimization,allowshrinking,allowobfuscation class <3>

# With R8 full mode generic signatures are stripped for classes that are not kept.
-keep,allowobfuscation,allowshrinking class retrofit2.Response

# JSR 305 annotations are for embedding nullability information.
-dontwarn javax.annotation.**

# A resource is loaded with a relative path so the package of this class must be preserved.
-adaptresourcefilenames okhttp3/internal/publicsuffix/PublicSuffixDatabase.gz

# Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java.
-dontwarn org.codehaus.mojo.animal_sniffer.*

# OkHttp platform used only on JVM and when Conscrypt and other security providers are available.
-dontwarn okhttp3.internal.platform.**
-dontwarn org.conscrypt.**
-dontwarn org.bouncycastle.**
-dontwarn org.openjsse.**

编辑:

感谢@sgjesse(*)的宝贵帮助,我终于能够找到更多关于错误的信息:

java.lang.NullPointerException: Attempt to invoke interface method 'java.lang.String java.lang.CharSequence.toString()' on a null object reference
                                             at java.text.Normalizer.normalize(Normalizer.java:167)


现在已经更正了,但我在R8上运行应用程序时仍然有问题。我试图理解并纠正它们,但这在这个问题中偏离了主题。
(*)将-dontobfuscate添加到prodguard-rules.pro,将应用程序下载到我的智能手机,将智能手机连接到android-studio以查看logcat

iqxoj9l9

iqxoj9l91#

如果你打算使用minifyEnabled和shrinkResources,你应该事先计划好。这两个工具的作用是,它们试图通过某种算法压缩你的apk大小,从而以某种方式压缩你的模型类和其他属性。当这些模型类被压缩时,你的应用会出现错误,你会在这里或那里得到异常。
有一些标志应该与你的模型类一起使用,这可以防止它们被压缩。我不知道它们的确切位置,但你可以搜索它们。

相关问题