如何对按需初始化持有者模式的单例实例进行垃圾回收/字段清理?
实现GetItemQuery单例用于Retrofit API请求复用
既然你想通过单例模式复用GetItemQuery实例,避免每次API调用都新建对象,那静态内部类的懒加载实现是个不错的选择——它天然线程安全,还能实现懒加载(第一次调用才初始化实例)。我给你补全并优化了代码,同时附上使用示例和注意事项:
完整的GetItemQuery单例实现
public class GetItemQuery { // 请求数据字段 private String itemId; private int numberToLoad; // 响应数据字段 private Item item; // 私有构造方法,禁止外部直接new实例 private GetItemQuery() {} // 静态内部类实现懒加载单例,JVM类加载机制保证线程安全 private static class Loader { private static final GetItemQuery INSTANCE = new GetItemQuery(); } // 获取全局唯一实例 public static GetItemQuery getInstance() { return Loader.INSTANCE; } // 请求字段的Setter方法,用于动态设置API请求参数 public void setItemId(String itemId) { this.itemId = itemId; } public void setNumberToLoad(int numberToLoad) { this.numberToLoad = numberToLoad; } // 请求字段的Getter方法(Retrofit可能需要通过反射读取这些字段) public String getItemId() { return itemId; } public int getNumberToLoad() { return numberToLoad; } // 响应字段的Setter/Getter,用于接收API返回数据 public void setItem(Item item) { this.item = item; } public Item getItem() { return item; } // 可选但推荐:重置字段方法,避免上次请求的残留数据干扰下一次调用 public void reset() { this.itemId = null; this.numberToLoad = 0; this.item = null; } }
如何在Retrofit调用中使用
每次发起API请求前,先获取单例、重置字段、设置请求参数,再传入Retrofit的Call中:
// 1. 获取单例并重置字段(关键!避免旧数据残留) GetItemQuery query = GetItemQuery.getInstance(); query.reset(); // 2. 设置本次请求的参数 query.setItemId("item_001"); query.setNumberToLoad(5); // 3. 发起Retrofit请求 Call<GetItemQuery> call = yourApiService.getItem(query); call.enqueue(new Callback<GetItemQuery>() { @Override public void onResponse(Call<GetItemQuery> call, Response<GetItemQuery> response) { if (response.isSuccessful()) { // 从响应中获取数据(也可以直接用response.body()获取整个实例) Item resultItem = response.body().getItem(); // 处理业务逻辑... } } @Override public void onFailure(Call<GetItemQuery> call, Throwable t) { // 处理请求失败逻辑... } });
注意事项
- 线程安全问题:如果你的App存在多线程并发发起请求的场景,直接用全局单例可能会出现参数被覆盖的问题。解决办法有两个:
- 在设置参数+发起请求的代码块上加同步锁:
synchronized (GetItemQuery.class) { GetItemQuery query = GetItemQuery.getInstance(); query.reset(); query.setItemId("item_001"); // 发起请求... } - 改用ThreadLocal为每个线程维护独立实例,彻底避免并发冲突:
public class GetItemQuery { // 替换原来的Loader内部类和getInstance方法 private static final ThreadLocal<GetItemQuery> THREAD_LOCAL = ThreadLocal.withInitial(GetItemQuery::new); public static GetItemQuery getInstance() { return THREAD_LOCAL.get(); } // 可选:请求结束后移除当前线程的实例,避免内存泄漏 public static void clear() { THREAD_LOCAL.remove(); } // ... 其他字段和方法保持不变 }
- 在设置参数+发起请求的代码块上加同步锁:
- Retrofit序列化兼容:确保字段名和API的请求/响应字段一致,或者用
@SerializedName注解指定映射关系(比如API字段是item_id,你的字段是itemId,就加@SerializedName("item_id"))。 - 重置字段的必要性:一定要记得调用
reset(),不然上次请求的itemId或响应数据可能会影响下一次请求的结果。
内容的提问来源于stack exchange,提问作者bycfly
相关产品推荐
相关产品推荐

