일모도원(日暮途遠) 개발자

[안드로이드개발] 릴리즈 모드에서만 죽는 현상 proguard관련 본문

안드로이드 개발/안드로이드

[안드로이드개발] 릴리즈 모드에서만 죽는 현상 proguard관련

달님개발자 2023. 3. 31. 03:48

챗GPT API를 쓰는 앱을 개발중에 릴리즈모드에서만 죽는 현상이 발견되었다.

 

에러로그가 이렇게 알아보기 힘들게 나온다. 다행히 ".NoSuchFieldException: system"라는 문구로 어디가 문제인지는 파악이 된다.

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.dalnimsoft.araconv, PID: 17238
    java.lang.AssertionError: java.lang.NoSuchFieldException: system
        at f.h.g.r.k.n$j0.<init>(:792)
        at f.h.g.r.k.n$w.a(:818)
        at f.h.g.d.k(:458)
        at f.h.g.r.k.i.b(:117)
        at f.h.g.r.k.i.e(:166)
        at f.h.g.r.k.i.a(:102)
        at f.h.g.d.k(:458)
        at f.h.g.r.k.b.a(:53)
        at f.h.g.d.k(:458)
        at f.h.g.r.k.i.b(:117)
        at f.h.g.r.k.i.e(:166)
        at f.h.g.r.k.i.a(:102)
        at f.h.g.d.k(:458)
        at retrofit2.converter.gson.GsonConverterFactory.requestBodyConverter(:70)
        at retrofit2.Retrofit.nextRequestBodyConverter(:315)

로그가 저렇게 나오는 이유가 결국 에러원인과도 관련이 있다.

 

proguard-rules.pro에 정의 된대로 obfuscate를 하는데, obfuscate라는 말 그대로 리버스 엔지니어링 할때 혼동스럽게 하기 위해서 클래스명 필드명들을 전부 a, b 이런씩으로 확 줄여버린다. (또한 조금이나마 용량이 줄어들기도 한다. 글자수를 줄이니까)

buildTypes {
    release {
        debuggable false
        minifyEnabled true
        shrinkResources true
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        signingConfig signingConfigs.config
        manifestPlaceholders = [crashlyticsEnabled: true]
        ndk {
            debugSymbolLevel 'FULL'
        }
    }
}

만약 proguard를 적용안시키고 테스트해볼려고 하면 그냥 minifyEnabled와 shrinkResources를 false로 두고 테스트 하면 된다.

 

 

문제가 된 코드는 여기다.

ChatGPTRequest 클래스 안데 Message라는 클래스가 있고 그안에 다시 Role이라는 enum이 있는데 여기에 있는 system이 축약이 되어서 문제가 되었다. (근데 이게 왜 크래쉬인지는 모르겠다. API호출이 실패하면 되는거 같은데...) 

public class ChatGPTRequest {
    public static class Message {
        public enum Role {
            system,
            user,
            assistant
        }
    }
}

그리고 이부분에서도 에러가 났다. 아마 @SerializedName("object")도 "object"를 a 이런씩으로 줄여버린거 같다.

public class ChatCompletionResponse {
    @SerializedName("id")
    private String id;
    @SerializedName("object")
    private String object;
    public static class Choice {
        @SerializedName("message")
        private Message message;
    }
    public static class Message {
        @SerializedName("role")
        private String role;

        @SerializedName("content")
        private String content;
    }

 

그래서 proguard-rules.pro에 아래를 추가해서 해결했다.

즉 아래 클래스에 있는것 obfuscate하지 말고 그냥 두라는거다.

주의할점은 부모 클래스만 하지 말고 내부 클래스와 열거형(enum)도 같이 적어줘야 한다.

-keepclassmembers class com.dalread.network.ChatCompletionResponse { *; }
-keepclassmembers class com.dalread.network.ChatCompletionResponse$Message { *; }
-keepclassmembers class com.dalread.network.ChatCompletionResponse$Choice { *; }
-keepclassmembers class com.dalread.network.ChatCompletionResponse$Usage { *; }
-keepclassmembers class com.dalread.network.ChatGPTRequest { *; }
-keepclassmembers class com.dalread.network.ChatGPTRequest$Message { *; }
-keepclassmembers class com.dalread.network.ChatGPTRequest$Message$Role { *; }

 

내부 클래스와 열거형(enum)을 일일히 적기 귀찮으면 아래처럼 $*를 클래스에 추가하면 된다. 

-keepclassmembers class com.dalread.network.ChatCompletionResponse$* { *; }
-keepclassmembers class com.dalread.network.ChatGPTRequest$* { *; }

참고로 -keep을 써도 되는데 -keep은 클래스명도 축약하지 말라는 뜻이다. 내 경우에는 클래스명은 축약해도 되니까 -keppclassmembers를 써서 클래스 멤버들만 축약하지 말고 keep하라는 명령을 사용해서 해결했다.