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

使用FragmentStatePagerAdapter的ViewPager二次进入Fragment空白问题

问题分析与解决方案

看起来你遇到的核心问题是:首次进入BoutiqueActivity时,CategorieFragment能正常通过AsyncTask加载数据并更新视图,但返回后再次进入该Activity时,视图更新操作(包括ListView的notifyDataSetChanged和ProgressBar的setVisibility)完全失效。下面我来拆解问题原因并给出针对性的修复方案:

核心问题原因

1. 重复设置ViewPager Adapter导致视图重建

你的BoutiqueActivity在onStart方法中每次都会调用viewPager.setAdapter(adapter),这会导致ViewPager重复绑定Adapter,进而引发Fragment视图的频繁销毁与重建。当AsyncTask完成请求时,它持有的可能已经是被销毁的旧视图引用,此时再调用更新操作自然不会生效。

2. 未及时取消旧的AsyncTask

当用户返回前一个Activity时,当前的CategorieFragment会被销毁,但之前启动的AsyncTask并没有被取消,它会继续在后台运行。当任务完成后,它会尝试操作已被销毁的旧视图,不仅会导致无效操作,还可能引发内存泄漏。

3. AsyncTask持有强引用导致内存泄漏

你的HTTPRequest类持有Fragment、ProgressBar、ListView的强引用,即使Fragment已经被销毁,这些引用依然会阻止GC回收相关对象,导致旧的任务完成后仍能操作无效视图。

具体修复方案

方案1:移除重复设置Adapter的代码

将ViewPager绑定Adapter和TabLayout的代码从onStart移到onCreate中,只执行一次即可:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_boutique);
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    this.viewPager = this.findViewById(R.id.viewPager);
    this.viewPager.setSaveFromParentEnabled(false);
    this.savedInstanceState = savedInstanceState;
    this.adapter = new ViewPagerAdapter(getSupportFragmentManager());
    
    if (savedInstanceState == null){
        this.setupViewPager();
    }else {
        Integer count = savedInstanceState.getInt("tabsCount");
        String[] titles = savedInstanceState.getStringArray("titles");
        for (int i = 0; i < count; i++) {
            this.adapter.addFragment(getFragment(i), titles[i]);
        }
    }
    
    // 移到此处,仅初始化一次
    viewPager.setAdapter(adapter);
    this.tabLayout = this.findViewById(R.id.tabLayout);
    this.tabLayout.setupWithViewPager(viewPager);
}

@Override
public void onStart(){
    super.onStart();
    // 移除原有的adapter设置代码
}

方案2:在Fragment销毁时取消AsyncTask

在CategorieFragment中保存当前的AsyncTask引用,并在视图销毁时主动取消任务:

public class CategorieFragment extends Fragment implements ActiviteEnAttenteAvecResultat<Categorie> {
    protected ArrayList<Categorie> categories;
    protected ListView listView;
    protected Drawable substitut;
    protected ProgressBar loader;
    // 保存当前请求的引用
    private HTTPRequest<Categorie> currentRequest;

    // ... 其他原有代码 ...

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        this.listView = view.findViewById(R.id.liste);
        this.loader = view.findViewById(R.id.loader);
        CategorieAdapter categorieAdapter = new CategorieAdapter(getActivity(), categories, substitut);
        this.listView.setAdapter(categorieAdapter);
        
        // 取消旧的请求(如果存在)
        if(currentRequest != null && !currentRequest.isCancelled()){
            currentRequest.cancel(true);
        }
        // 启动新请求并保存引用
        currentRequest = CategorieDAO.getInstance(this, this.loader, this.listView).findAll();
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        // 视图销毁时取消请求,避免无效操作和内存泄漏
        if(currentRequest != null && !currentRequest.isCancelled()){
            currentRequest.cancel(true);
        }
    }

    // ... 其他原有代码 ...
}

方案3:使用弱引用避免内存泄漏

修改HTTPRequest类,将activite、loader、listView改为弱引用,确保视图销毁后这些引用能被GC回收:

public class HTTPRequest<T extends Object> extends AsyncTask<String, Void, String> {
    protected WeakReference<ProgressBar> loaderRef;
    protected WeakReference<ListView> listViewRef;
    protected WeakReference<ActiviteEnAttenteAvecResultat> activiteRef;
    protected DAO dao;
    protected String method;
    protected String error;
    protected Object data;
    protected Class deserializationClass;

    public HTTPRequest(ActiviteEnAttenteAvecResultat activite, DAO dao, String method, Object data, Class deserializationClass, ProgressBar loader, ListView listView){
        this.activiteRef = new WeakReference<>(activite);
        this.dao = dao;
        this.method = method;
        this.data = data;
        this.deserializationClass = deserializationClass;
        this.loaderRef = new WeakReference<>(loader);
        this.listViewRef = new WeakReference<>(listView);
    }

    // ... 其他构造函数 ...

    @Override
    protected void onPreExecute(){
        ProgressBar loader = loaderRef.get();
        ListView listView = listViewRef.get();
        if(loader != null && listView != null){
            loader.setVisibility(View.VISIBLE);
            listView.setVisibility(View.INVISIBLE);
        }
    }

    @Override
    protected void onPostExecute(String result){
        ActiviteEnAttenteAvecResultat activite = activiteRef.get();
        ProgressBar loader = loaderRef.get();
        ListView listView = listViewRef.get();
        
        // 检查引用是否有效
        if(activite == null || loader == null || listView == null){
            return;
        }

        Gson gson = new Gson();
        ArrayList<T> liste = new ArrayList<>();
        if(result != null){
            switch (this.method){
                case (HTTPRequestMethod.GET):
                    JsonParser parser = new JsonParser();
                    JsonArray array = parser.parse(result).getAsJsonArray();
                    for (int i = 0; i < array.size(); i++) {
                        Object object = gson.fromJson(array.get(i), this.deserializationClass);
                        liste.add((T) object);
                    }
                    activite.notifyRetourRequeteFindAll(liste);
                    break;
            }
        } else {
            // 可以添加错误提示逻辑
        }
        
        loader.setVisibility(View.INVISIBLE);
        listView.setVisibility(View.VISIBLE);
    }

    // 移除原有的afficheLoader和cacheLoaderAfficheContenu方法,直接在onPreExecute/onPostExecute中操作弱引用对象
}

方案4:在视图更新前检查有效性

在CategorieFragment的notifyRetourRequeteFindAll方法中,先检查Fragment是否仍依附于Activity、视图是否有效,再执行更新操作:

@Override
public void notifyRetourRequeteFindAll(ArrayList<Categorie> liste){
    // 检查Fragment状态和视图有效性
    if(getActivity() == null || listView == null || listView.getAdapter() == null){
        return;
    }
    this.categories.clear();
    this.categories.addAll(liste);
    ((BaseAdapter) this.listView.getAdapter()).notifyDataSetChanged();
}

总结

通过以上几个方案的组合修复,你可以解决重复设置Adapter导致的视图重建问题,避免旧AsyncTask的无效操作,同时消除内存泄漏风险,确保每次进入Activity时,Fragment都能正常加载数据并更新视图。

内容的提问来源于stack exchange,提问作者useryg

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:25:50