Retrofit2在Android模拟器正常,签名APK调用GPT接口报异常
问题:签名APK中Retrofit调用OpenAI ChatGPT-3.5 Turbo API失败
在Android Java聊天应用中使用Retrofit调用OpenAI接口,模拟器和调试设备上运行正常,但生成签名APK后调用API时抛出java.lang.IllegalArgumentException,错误信息如下:
java.lang.IllegalArgumentException: Unable to create call adapter for interface d6.g for method a.a... java.lang.IllegalArgumentException: Call return type must be parameterized as Call or Call<? extends Foo>...
问题原因
签名APK默认开启代码混淆,Retrofit核心类、自定义API接口、响应实体类被混淆重命名,导致Retrofit无法识别参数化的Call<ChatCompletionResponse>类型,从而抛出适配器创建失败的异常。
解决方案
1. 添加混淆规则到proguard-rules.pro
在项目的proguard-rules.pro文件中添加以下规则,避免Retrofit、OkHttp、Gson及自定义相关类被混淆:
# Retrofit -keepattributes Signature, InnerClasses, EnclosingMethod -keep class retrofit2.** { *; } -keep interface retrofit2.** { *; } -keepclasseswithmembers class * { @retrofit2.http.* <methods>; } # OkHttp -keep class okhttp3.** { *; } -keep interface okhttp3.** { *; } -dontwarn okhttp3.** # Gson -keepattributes *Annotation* -keep class sun.misc.Unsafe { *; } -keep class com.google.gson.stream.** { *; } # 自定义API接口和响应实体类(替换为实际包名) -keep class com.yourpackage.APIInterface { *; } -keep class com.yourpackage.ChatCompletionResponse { *; } -keep class com.yourpackage.ChatCompletionResponse$* { *; } # 保留内部类
2. 优化请求体写法(可选)
原代码手动构建RequestBody的方式可简化,直接用Gson将请求对象转为RequestBody,减少手动拼接JSON的错误:
首先创建请求实体类:
public class ChatRequest { private String model; private List<Message> messages; public ChatRequest(String model, List<Message> messages) { this.model = model; this.messages = messages; } public static class Message { private String role; private String content; public Message(String role, String content) { this.role = role; this.content = content; } } }
修改sendRequest方法:
private void sendRequest(String query) { ChatRequest.Message message = new ChatRequest.Message("system", query); List<ChatRequest.Message> messageList = Collections.singletonList(message); ChatRequest request = new ChatRequest("gpt-3.5-turbo", messageList); RequestBody requestBody = RequestBody.create( GsonConverterFactory.create().getGson().toJson(request), MediaType.parse("application/json") ); Call<ChatCompletionResponse> call = RetrofitInstance.getInstance().apiInterface.getData(requestBody); // 后续回调逻辑不变 }
3. 验证签名APK配置
确保Module级别的build.gradle中release配置正确引用混淆规则:
android { buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } }
相关代码
请求函数
private void sendRequest(String query) { JSONObject jsonObject = new JSONObject(); try { jsonObject.put("model","gpt-3.5-turbo"); JSONArray jsonArray = new JSONArray(); JSONObject jsonObject1 = new JSONObject(); jsonObject1.put("role","system"); jsonObject1.put("content",query); jsonArray.put(0, jsonObject1); jsonObject.put("messages",jsonArray); } catch (JSONException e) { Log.d(TAG,e.getMessage()); throw new RuntimeException(e); } RequestBody requestBody = new RequestBody() { @Override public MediaType contentType() { return MediaType.parse("application/json"); } @Override public void writeTo(BufferedSink sink) throws IOException { sink.writeUtf8(jsonObject.toString()); } }; Call<ChatCompletionResponse> call = RetrofitInstance.getInstance().apiInterface.getData(requestBody); call.enqueue(new Callback<ChatCompletionResponse>() { @Override public void onResponse(@NonNull Call<ChatCompletionResponse> call, @NonNull Response<ChatCompletionResponse> response) { if(response.isSuccessful()){ assert response.body() != null; String content = response.body().getChoices()[0].getMessage().getContent(); TextMessage textMessage = new TextMessage(user.getUid(),content, CometChatConstants.RECEIVER_TYPE_USER); textMessage.setSender(user); addMessage(textMessage); setIndicator(false); } } @Override public void onFailure(@NonNull Call<ChatCompletionResponse> call, @NonNull Throwable t) { Log.d(TAG,"failurse occued" + t.getMessage()); String msg = "Request failed !!"; TextMessage textMessage = new TextMessage(logedinuser.getUid(),msg, CometChatConstants.RECEIVER_TYPE_USER); textMessage.setSender(logedinuser); addMessage(textMessage); } }); }
RetrofitInstance.java
public class RetrofitInstance { public static RetrofitInstance instance; public APIInterface apiInterface; static String BASE_URL = "https://api.openai.com/v1/chat/"; RetrofitInstance(){ OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(new Interceptor() { @NonNull @Override public Response intercept(@NonNull Chain chain) throws IOException { Request newRequest = chain.request().newBuilder() .addHeader("Content-Type", "application/json") .addHeader("Authorization", "Bearer " + AppKeys.GPT_TOKEN ) .build(); return chain.proceed(newRequest); } }) .build(); apiInterface = new Retrofit.Builder() .client(client) .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build().create(APIInterface.class); } public static RetrofitInstance getInstance(){ if(instance == null){ instance = new RetrofitInstance(); } return instance; } }
APIInterface.java
public interface APIInterface { @POST("completions") Call<ChatCompletionResponse> getData( @Body RequestBody body ); }
ChatCompletionResponse.java
public class ChatCompletionResponse { public String getId() { return id; } public String getObject() { return object; } public long getCreated() { return created; } public String getModel() { return model; } public UsageData getUsage() { return usage; } public Choice[] getChoices() { return choices; } @SerializedName("id") private String id; public void setId(String id) { this.id = id; } public void setObject(String object) { this.object = object; } public void setCreated(long created) { this.created = created; } public void setModel(String model) { this.model = model; } public void setUsage(UsageData usage) { this.usage = usage; } public void setChoices(Choice[] choices) { this.choices = choices; } @SerializedName("object") private String object; @SerializedName("created") private long created; public ChatCompletionResponse(String id, String object, long created, String model, UsageData usage, Choice[] choices) { this.id = id; this.object = object; this.created = created; this.model = model; this.usage = usage; this.choices = choices; } @SerializedName("model") private String model; @SerializedName("usage") private UsageData usage; @SerializedName("choices") private Choice[] choices; public static class UsageData { @SerializedName("prompt_tokens") private int promptTokens; @SerializedName("completion_tokens") private int completionTokens; @SerializedName("total_tokens") private int totalTokens; // Getters and setters } public static class Choice { public Choice(Message message, String finishReason, int index) { this.message = message; this.finishReason = finishReason; this.index = index; } public void setMessage(Message message) { this.message = message; } public void setFinishReason(String finishReason) { this.finishReason = finishReason; } public void setIndex(int index) { this.index = index; } @SerializedName("message") private Message message; public Message getMessage() { return message; } public String getFinishReason() { return finishReason; } public int getIndex() { return index; } @SerializedName("finish_reason") private String finishReason; @SerializedName("index") private int index; // Getters and setters } public static class Message { @SerializedName("role") private String role; public String getContent() { return content; } public Message(String role, String content) { this.role = role; this.content = content; } public void setRole(String role) { this.role = role; } public void setContent(String content) { this.content = content; } @SerializedName("content") private String content; // Getters and setters } }
内容的提问来源于stack exchange,提问作者SHIVA VERMA
相关产品推荐
相关产品推荐

