基于Androidx+Fragment源码分析fragment重叠的原因

本文深入剖析了在Android中使用Fragment时,由于Activity内存不足导致的Fragment重叠问题。通过跟踪源码,揭示了问题产生的根本原因,并提出了解决方案。

本文基于implementation "androidx.fragment:fragment-ktx:1.3.1"源码,分析了为什么fragment以show/hide方式,在Activity内存不足重新打开时会出现重叠问题的原因,及解决思路。

首先查看Activity的super.onSaveInstanceState(outState)方法
最后会在Activity中的onSaveInstanceState方法中看到

Parcelable p = mFragments.saveAllState();
if (p != null) {
   
   
    outState.putParcelable(FRAGMENTS_TAG, p);
}

如上代码,看看mFragments是什么
final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
发现mFragments是FragmentController对象的引用,查看FragmentController是如何创建的
private final FragmentHostCallback<?> mHost;

/**
 * Returns a {@link FragmentController}.
 */
public static final FragmentController createController(FragmentHostCallback<?> callbacks) {
   
   
    return new FragmentController(callbacks);
}

private FragmentController(FragmentHostCallback<?> callbacks) {
   
   
    mHost = callbacks;
}

发现其通过静态createController方法,直接创建了FragmentController对象并返回,并将
FragmentHostCallback对象赋值给mHost在FragmentController保存了起来。
而HostCallback通过new的方式直接创建,查看HostCallback类发现

class HostCallbacks extends FragmentHostCallback<Activity> {
   
   
    public HostCallbacks() {
   
   
        super(Activity.this /*activity*/);
    }
}

HostCallback类继承了FragmentHostCallback类,并在构造方法中传入了Activity实例,此时
FragmentHostCallback持有了Activity对象的引用
继续查看saveAllStete方法

/**
 * Saves the state for all Fragments.
 */
public Parcelable saveAllState() {
   
   
    return mHost.mFragmentManager.saveAllState();
}

其调用了mHost.mFragmentManager,FragmentManager的saveAllState方法
查看FragmentHostCallback类可知
final FragmentManager mFragmentManager = new FragmentManagerImpl();
mFragmentManager对象是直接new出来的。现在查看FragmentManagerImpl类

class FragmentManagerImpl extends FragmentManager {
   
   
}

发现只是继承了FragmentManager
找到FragmentManager的saveAllSate方法

Parcelable saveAllState() {
   
   
    // Make sure all pending operations have now been executed to get
    // our state update-to-date.
    forcePostponedTransactions();
    endAnimatingAwayFragments();
    execPendingActions(true);

    mStateSaved = true;
    mNonConfig.setIsStateSaved(true);

    // First collect all active fragments.首先收集全部活动的fragments
    ArrayList<FragmentState> active = mFragmentStore.saveActiveFragments();

    if (active.isEmpty()) {
   
   
        if (isLoggingEnabled(Log.VERBOSE)) Log.v(TAG, "saveAllState: no fragments!");
        return null
<think>好的,我现在需要帮助用户解决关于Android底部导航栏多Fragment切换的设计框架和Java实现的问题。首先,我得回顾一下用户的具体需求:他们想要了解如何设计并使用Java实现一个基于底部导航栏的多Fragment切换的Android应用。用户还提到了引用标识,但给出的引用似乎与问题无关,可能需要忽略或后续确认。 首先,我应该确定用户可能的技术水平。他们可能已经有一定的Android开发基础,了解Activity和Fragment的基本概念,但需要指导如何将这些整合到底部导航栏中。用户需要的是一个设计模式或框架,而不仅仅是代码片段,因此我需要解释清楚设计思路,并提供相应的实现步骤。 接下来,我需要回忆Android中底部导航栏的标准实现方法。通常,Android推荐使用Navigation Component库来管理Fragment的切换和导航。结合底部导航栏(BottomNavigationView)和NavController,可以有效地实现多Fragment的切换。此外,使用ViewPager2也是一种常见的方式,特别是当需要滑动切换时。 考虑到用户要求的是设计框架和实现方法,可能需要分步骤讲解。首先,如何设置底部导航栏的菜单项;然后,如何将每个菜单项与对应的Fragment关联;接着,如何处理Fragment的切换,避免重复创建实例;最后,如何管理返回栈,确保用户体验正确。 还需要考虑代码的正确性和最佳实践。例如,使用FragmentTransaction来替换容器中的Fragment,或者使用ViewPager2结合TabLayoutMediator。此外,是否需要使用单Activity多Fragment架构,这也是现代Android开发推荐的做法,有助于简化导航和状态管理。 可能遇到的问题包括Fragment重叠、状态保存、性能优化等。因此,在回答中需要提到如何处理这些问题,例如使用show/hide方法来切换Fragment,而不是每次都replace,以减少资源消耗。同时,使用ViewModel来保存和共享数据,确保Fragment之间的通信。 用户提供的示例中有Python代码,但这里需要的是Java实现,所以需要确保示例代码正确无误,符合Java语法,并遵循Android API的最佳实践。例如,使用AppCompatActivity,使用findViewById的正确方式(如果未使用View Binding或Data Binding),以及如何处理底部导航项的点击事件。 此外,用户可能希望了解不同的设计模式,比如MVC、MVP或MVVM在此场景下的应用。不过,可能更倾向于具体的实现而非架构模式,但简要提及可以帮助用户扩展理解。 最后,生成的相关问题需要考虑用户可能的后续疑问,例如性能优化、动画效果、与ViewPager2的集成、如何处理复杂的数据传递等。这些问题应该在“相关问题”部分列出,帮助用户进一步探索。 总结下来,回答的结构应该是: 1. 设计模式概述:推荐使用Navigation Component或ViewPager2。 2. 实现步骤:包括布局文件、菜单资源、Fragment创建、导航配置。 3. 代码示例:展示如何将底部导航栏与Fragment切换结合。 4. 注意事项:如避免Fragment重叠、性能优化、返回栈管理。 5. 相关问题:引导用户深入探讨其他相关主题。 需要确保每个部分都清晰,并且代码示例正确,符合Java语法和Android开发规范。同时,引用标识可能存在问题,但用户提供的引用似乎不相关,可能需要忽略或指出其不适用。</think>### Android底部导航栏多Fragment切换实现方案 #### 一、标准实现模式(Navigation Component) 推荐使用Android Jetpack的Navigation组件实现,这是Google官方推荐的导航方案[^1]。 **实现步骤:** 1. 添加依赖项: ```gradle implementation "androidx.navigation:navigation-fragment:2.5.3" implementation "androidx.navigation:navigation-ui:2.5.3" ``` 2. 创建导航图资源文件(nav_graph.xml): ```xml <navigation xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/nav_graph" app:startDestination="@id/homeFragment"> <fragment android:id="@+id/homeFragment" android:name="com.example.HomeFragment"/> <fragment android:id="@+id/dashboardFragment" android:name="com.example.DashboardFragment"/> <fragment android:id="@+id/notificationsFragment" android:name="com.example.NotificationsFragment"/> </navigation> ``` 3. 主Activity布局: ```xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <fragment android:id="@+id/nav_host_fragment" android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"/> <com.google.android.material.bottomnavigation.BottomNavigationView android:id="@+id/bottom_nav" android:layout_width="match_parent" android:layout_height="wrap_content" app:menu="@menu/bottom_nav_menu"/> </LinearLayout> ``` 4. Java核心逻辑: ```java public class MainActivity extends AppCompatActivity { private NavController navController; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager() .findFragmentById(R.id.nav_host_fragment); navController = navHostFragment.getNavController(); BottomNavigationView bottomNav = findViewById(R.id.bottom_nav); NavigationUI.setupWithNavController(bottomNav, navController); } } ``` #### 二、传统实现模式(FragmentTransaction) 适用于需要自定义控制逻辑的场景: ```java public class MainActivity extends AppCompatActivity { private Fragment currentFragment; private void switchFragment(Fragment newFragment) { FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); if (currentFragment != null) { transaction.hide(currentFragment); } if (!newFragment.isAdded()) { transaction.add(R.id.container, newFragment); } transaction.show(newFragment).commit(); currentFragment = newFragment; } // 在BottomNavigationView的选中监听器中调用 bottomNav.setOnNavigationItemSelectedListener(item -> { switch(item.getItemId()) { case R.id.nav_home: switchFragment(new HomeFragment()); return true; case R.id.nav_dashboard: switchFragment(new DashboardFragment()); return true; case R.id.nav_notifications: switchFragment(new NotificationsFragment()); return true; } return false; }); } ``` #### 三、关键优化技巧 1. **Fragment复用**:使用`show/hide`代替`replace`,避免重复创建实例 2. **状态保存**:通过`onSaveInstanceState`保存当前选中状态 3. **懒加载**:结合`setUserVisibleHint`实现数据延迟加载 4. **动画效果**:自定义`FragmentTransaction`的过渡动画 5. **返回栈管理**:使用`addToBackStack(null)`控制回退逻辑 #### 四、设计模式建议 推荐使用MVVM模式配合LiveData: ```java public class MainViewModel extends ViewModel { private MutableLiveData<Integer> selectedTab = new MutableLiveData<>(); public void selectTab(int tabId) { selectedTab.setValue(tabId); } public LiveData<Integer> getSelectedTab() { return selectedTab; } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值