使用Retrofit2上传图片与数据无回调响应,该如何解决?
我在Postman中可以正常上传指定数据,但用Retrofit2编写的代码出现了问题:调试时流程正常,但执行到call.enqueue()后,既不进入onResponse也不进入onFailure,直接走到方法末尾,请求毫无反应。以下是我的代码:
客户端创建
public BeeMappingClient(String url){ retrofit= new Retrofit.Builder() .baseUrl(url) .addConverterFactory(ScalarsConverterFactory.create()) .addConverterFactory(GsonConverterFactory.create()) .build(); conexion=retrofit.create(Conexion.class); }
接口定义
@Multipart @POST("task") Call<ResponsetTask> API_Task(@Header("Authorization") String key, @Part MultipartBody.Part multipartImage, @Part("message") RequestBody message , @Part("filecomment") RequestBody filecomment, @Part("api_token") RequestBody api_token, @Part("user_id") RequestBody user_id);
请求调用
File file=new File(path); MultipartBody.Part[] multipartImageList = new MultipartBody.Part[1]; RequestBody surveyBody = RequestBody.create(MediaType.parse("image/*"), file); multipartImageList[0] = MultipartBody.Part.createFormData("image", "image.jpg", surveyBody); RequestBody message = RequestBody.create(MediaType.parse("message"), Constantes.MESSAGE); RequestBody filecomment = RequestBody.create(MediaType.parse("filecomment"), Constantes.FILECOMMENT); RequestBody api_token = RequestBody.create(MediaType.parse("api_token"),Constantes.api_token); RequestBody user_id = RequestBody.create(MediaType.parse("user_id"), Integer.toString(Constantes.id)); Call<ResponsetTask> call = conexion.API_Task(Constantes.AUTH,multipartImageList[0],message,filecomment,api_token,user_id); call.enqueue(new Callback<ResponsetTask>() { @Override public void onResponse(Call<ResponsetTask> call, Response<ResponsetTask> response) { Constantes.api_task=response.body().getTaskId(); } @Override public void onFailure(Call<ResponsetTask> call, Throwable t) { } });
请问该如何解决?当前上传方式是否最优?感谢帮助。
这种情况我之前做项目的时候也碰到过,Postman能正常跑但Retrofit没回调,大概率是代码里的几个细节没处理好,咱们一步步来解决:
一、排查无响应的核心原因
1. 非文件参数的MediaType设置错误
你给message、filecomment这些普通字符串参数设置的MediaType是参数名本身(比如MediaType.parse("message")),这是错误的!服务器无法识别这种自定义的MediaType,可能导致请求卡住或无法被正确解析,进而回调不触发。
修正方式:把非文件参数的MediaType改成标准的文本类型,比如text/plain:
RequestBody message = RequestBody.create(MediaType.parse("text/plain"), Constantes.MESSAGE); RequestBody filecomment = RequestBody.create(MediaType.parse("text/plain"), Constantes.FILECOMMENT); RequestBody api_token = RequestBody.create(MediaType.parse("text/plain"), Constantes.api_token); RequestBody user_id = RequestBody.create(MediaType.parse("text/plain"), Integer.toString(Constantes.id));
2. Retrofit/OkHttpClient实例生命周期问题
如果你的BeeMappingClient是局部变量(比如在某个方法内临时创建),方法执行完毕后对象会被GC回收,导致Retrofit的请求被取消,自然不会触发回调。
修正方式:把BeeMappingClient改成全局单例,或者作为Activity/Fragment的成员变量,确保在请求完成前不会被销毁。
3. 缺少网络权限(Android平台)
如果是Android项目,别忘了在AndroidManifest.xml中添加网络权限,否则请求根本发不出去:
<uses-permission android:name="android.permission.INTERNET"/>
4. 添加日志拦截器排查请求细节
建议给OkHttpClient添加日志拦截器,这样能在Logcat里看到完整的请求发送、服务器响应细节,判断请求是否真的发出去了:
首先添加依赖(Gradle):
implementation 'com.squareup.okhttp3:logging-interceptor:4.11.0'
然后修改Retrofit创建代码:
OkHttpClient client = new OkHttpClient.Builder() .connectTimeout(30, TimeUnit.SECONDS) // 添加超时时间,避免无限等待 .readTimeout(30, TimeUnit.SECONDS) .writeTimeout(30, TimeUnit.SECONDS) .addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)) .build(); retrofit= new Retrofit.Builder() .baseUrl(url) .client(client) // 关联自定义OkHttpClient .addConverterFactory(ScalarsConverterFactory.create()) .addConverterFactory(GsonConverterFactory.create()) .build();
添加超时时间后,如果请求超时会触发onFailure,方便你排查问题。
二、当前上传方式的优化建议
你的Multipart上传方式是标准实现,但可以做几个优化让代码更简洁、高效:
1. 使用@PartMap简化参数传递
不用一个个写@Part,把所有非文件参数放到Map里,代码更简洁:
// 修改接口定义 @Multipart @POST("task") Call<ResponsetTask> API_Task(@Header("Authorization") String key, @Part MultipartBody.Part multipartImage, @PartMap Map<String, RequestBody> params);
// 请求调用时构建参数Map
Map<String, RequestBody> params = new HashMap<>(); params.put("message", RequestBody.create(MediaType.parse("text/plain"), Constantes.MESSAGE)); params.put("filecomment", RequestBody.create(MediaType.parse("text/plain"), Constantes.FILECOMMENT)); params.put("api_token", RequestBody.create(MediaType.parse("text/plain"), Constantes.api_token)); params.put("user_id", RequestBody.create(MediaType.parse("text/plain"), Integer.toString(Constantes.id))); Call<ResponsetTask> call = conexion.API_Task(Constantes.AUTH, multipartImageList[0], params);
2. 单例化Retrofit和OkHttpClient
避免每次请求都创建新的Retrofit/OkHttpClient实例,节省资源,同时统一配置(比如拦截器、超时时间)。
3. 处理回调中的异常
现在你的onFailure是空实现,建议添加日志打印,方便排查问题:
@Override public void onFailure(Call<ResponsetTask> call, Throwable t) { Log.e("RetrofitError", "请求失败: " + t.getMessage()); t.printStackTrace(); }
内容的提问来源于stack exchange,提问作者user9920179

