Android中MVVM+Retrofit搜索功能重复返回结果问题求助
问题:Android搜索结果叠加重复(MVVM+Retrofit架构)
搜索API在Postman中能正常返回结果,但App中每次输入新关键词搜索,结果会叠加旧搜索结果,次数越多重复越严重:
- 输入'm',预期返回
"moon"、"mine",实际结果正确; - 输入'a',预期返回
"apple"、"alpha",实际返回"moon"、"mine"、"apple"、"alpha"、"apple"、"alpha"; - 输入'l',预期返回
"locus"、"lord",实际返回"apple"、"alpha"、"apple"、"alpha"、"locus"、"lord"、"locus"、"lord"。
相关代码
SearchRepo.java
public class SearchRepo { private Application application; private ApiService apiService; public SearchRepo(Application application) { this.application = application; this.apiService = RetrofitClient.getClient(BASE_URL) .create(ApiService.class); } public void SearchForKeyWord(String lang, String keyword, int perPage, SearchRepositoryCallBack<ProductModel> callBack) { apiService.search(lang,keyword,perPage) .enqueue(new Callback<ProductModel>() { @Override public void onResponse(Call<ProductModel> call, Response<ProductModel> response) { if (response.isSuccessful()) { callBack.onSuccess(response.body()); } else { callBack.onFailure("Error : "+response.code()); } } @Override public void onFailure(Call<ProductModel> call, Throwable t) { callBack.onFailure("Error : "+t.getMessage()); } }); } public interface SearchRepositoryCallBack<T> { void onSuccess(T post); void onFailure(String error); } }
SearchViewModel.java
public class SearchViewModel extends AndroidViewModel { private SearchRepo search; private MutableLiveData<ProductModel> searchResult = new MutableLiveData<>(); public SearchViewModel(@NonNull Application application) { super(application); this.search = new SearchRepo(application); } public void searchProducts(String lang,String keyword,int perPage) { search.SearchForKeyWord(lang, keyword, perPage, new SearchRepo.SearchRepositoryCallBack<ProductModel>() { @Override public void onSuccess(ProductModel post) { searchResult.postValue(post); } @Override public void onFailure(String error) { Log.e(APITAG,error); } }); } public LiveData<ProductModel> getSearchResult() { return searchResult; } }
SearchFragment.java 中的 search 方法
private void search(String lang, String keyword, int perPage) { productViewModel.searchProduct(lang,keyword,perPage); productViewModel.getResultList().observe(getViewLifecycleOwner(), new Observer<ProductModel>() { @Override public void onChanged(ProductModel productModel) { List<Product> mlist = productModel.getData(); for (int i=0;i<mlist.size();i++) { System.out.println(i); System.out.println("My List "+mlist.size()); System.out.println("My List "+mlist.get(i).getName()); } System.out.println("====================================="); } }); }
问题原因
- 重复注册Observer:每次调用
search()方法时,都会对LiveData调用observe()注册新的Observer,但旧的Observer并未被移除。当ViewModel中的searchResult发布新结果时,所有已注册的Observer都会触发执行,导致同一批新结果被多次处理。 - 数据源未清空(结合现象推测):如果实际业务中是将新结果直接追加到适配器的数据源列表,而非清空后替换,会导致旧结果保留,新结果被多次添加,最终出现叠加重复。
解决方法
方案1:统一注册Observer,避免重复监听
将Observer注册逻辑移到Fragment的生命周期方法中,仅注册一次:
@Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); // 只注册一次Observer,监听所有搜索结果 productViewModel.getResultList().observe(getViewLifecycleOwner(), productModel -> { List<Product> mlist = productModel.getData(); // 先清空原有列表,再添加新数据 adapter.getData().clear(); adapter.getData().addAll(mlist); adapter.notifyDataSetChanged(); // 原打印逻辑 for (int i=0;i<mlist.size();i++) { System.out.println(i); System.out.println("My List "+mlist.size()); System.out.println("My List "+mlist.get(i).getName()); } System.out.println("====================================="); }); } // 修改后的search方法仅触发搜索,不再注册Observer private void search(String lang, String keyword, int perPage) { productViewModel.searchProduct(lang,keyword,perPage); }
方案2:使用单次监听(如需临时监听)
如果需要每次搜索仅监听一次结果,可以自定义LiveData的单次监听扩展方法:
// 自定义LiveData扩展方法,实现单次监听 public static <T> void observeOnce(LiveData<T> liveData, LifecycleOwner owner, Observer<T> observer) { liveData.observe(owner, new Observer<T>() { @Override public void onChanged(T t) { observer.onChanged(t); liveData.removeObserver(this); } }); } // 修改search方法中的监听逻辑 private void search(String lang, String keyword, int perPage) { productViewModel.searchProduct(lang,keyword,perPage); observeOnce(productViewModel.getResultList(), getViewLifecycleOwner(), productModel -> { List<Product> mlist = productModel.getData(); adapter.getData().clear(); adapter.getData().addAll(mlist); adapter.notifyDataSetChanged(); // 原打印逻辑 for (int i=0;i<mlist.size();i++) { System.out.println(i); System.out.println("My List "+mlist.size()); System.out.println("My List "+mlist.get(i).getName()); } System.out.println("====================================="); }); }
核心注意点
- 处理新结果时,必须先清空原有数据源列表,再添加新数据,确保每次搜索结果独立。
- 严格控制
LiveData的Observer注册次数,避免重复回调导致的结果重复。
内容的提问来源于stack exchange,提问作者Mohamed Hossam
相关产品推荐
相关产品推荐

