You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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("=====================================");
            }
        });
    }

问题原因

  1. 重复注册Observer:每次调用search()方法时,都会对LiveData调用observe()注册新的Observer,但旧的Observer并未被移除。当ViewModel中的searchResult发布新结果时,所有已注册的Observer都会触发执行,导致同一批新结果被多次处理。
  2. 数据源未清空(结合现象推测):如果实际业务中是将新结果直接追加到适配器的数据源列表,而非清空后替换,会导致旧结果保留,新结果被多次添加,最终出现叠加重复。

解决方法

方案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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 20:52:32