片段:替换片段会导致它在按下后重新实例化

Fragments: Replacing a fragment causes it to reinstantiate on back pressed

我正在使用一种 activity 全片段方法。现在我有一个片段 A,当我转到片段 B 时,我用

将容器中的 A 替换为 B
.replace(R.id.master_fragment_container_above_toolbar, fragment)
                .addToBackStack("")
                .commit();

如您所见,我已将片段添加到 backStack。但是现在当我在设备上按回时,它会重新实例化片段 A,因此返回需要时间。

还有其他方法吗?我不想使用add(),如果我向容器中添加多个片段,它会占用大量内存。

简短的回答 - 你的情况没有灵丹妙药。您必须使用 replace() ,它会在返回时重新创建片段的 View 。那是 "by design".

好消息是,您可以采取一些技巧来降低 UX 的戏剧性。

  • 尽可能缓存。如果您从网络加载内容 - 将其写入本地 SQLite 数据库。并从本地存储填充屏幕,同时从服务器刷新数据。

  • onCreateView() 中避免重新创建已经存在的适配器。如果用户从 FragmentB 返回到 FragmentA,FragmentA 将重新创建其 View。但这并不意味着此时局部变量为空。所以我这样做:

    public class FeedFragment extends Fragment {
    
        private  FeedAdapter adapter;
    
        ......
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            View rootView = initialize(inflater, container, R.layout.fragment_with_recyclerview);
            ....
            if (adapter == null) {
                adapter = new FeedAdapter();
            }
    
            RecyclerView recyclerView = (RecyclerView)rootView.findViewById(R.id.recyclerView)
            recyclerView.setAdapter(adapter);
            recyclerView.setLayoutManager(new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL));
    
            return rootView;
        }
    }
    

    这很重要,因为填充 TextView 等操作非常快,您并不真正关心它,而巨大的 ListViewRecyclerView 可能会很多更贵;

  • 使用图像缓存和良好的图像加载工具。我推荐来自 Square 的 Picasso。它具有自动内存和磁盘缓存;

  • 尝试从逻辑上解耦您的应用程序。让我们以一个 Gmail 风格的应用程序为例: 它有 NavigationDrawer,它通过根片段切换用户(即不需要保留导航堆栈)。打开邮件线程后,它会将根片段替换为 MailThread 片段。在那里你可以消费内容。

    但是一旦您点击 "Compose email" - 您将重定向到一个单独的 Activity - 当您从消费内容状态转变为撰写内容状态时(即用户行为的显着变化:新用户场景==新activity).

    许多顶级开发人员走得更远,几乎所有内容都有活动。以 Twitter(及其卫星产品)为例。但就个人而言,我更喜欢保持平衡(因为从性能角度来看,打开新 activity 是一项昂贵的操作)。