likes
comments
collection
share

Fragment面试必会知识点

作者站长头像
站长
· 阅读数 5
  • Fragment
    • 参考链接
    • 对于Fragment设计的理解
    • 生命周期
      • Fragment生命周期
      • 和Activity交互的生命周期
      • 代码结构
      • Logcat例子
        • 在Activity的onCreate使用FragmentManager添加
        • 使用ViewPager
  • 使用
    • 添加
      • XML - 1
      • XML - 2
      • 使用FragmentManager
    • 回退栈管理
    • 和Activity、Fragment通信
      • 与宿主 Activity 共享数据
      • 在 Fragment 之间共享数据
      • 在父 Fragment 与子 Fragment 之间共享数据
    • 过渡动画
  • 遇到的问题
    • getSupportFragmentManager()和getChildFragmentManager()
    • commit() 和 commitAllowingStateLoss()
    • 重建
      • 遇到的问题
  • Commit is asynchronous
  • FragmentPagerAdapter 和FragmentStatePagerAdapter
  • 懒加载

Fragment

参考链接

我发现针对Fragment的官方文档,中文和英文似乎不太一样? 感觉中文的更精炼一点,英文更详细一点

对于Fragment设计的理解

Fragment是3.0引入的,我对Fragment的理解就是组件化重用适应不同屏幕尺寸,Fragment拥有和Activity类似的生命周期,针对大屏设备可以将重复的模块代码分别封装到对应的Fragment中,在不同的屏幕设备进行切换,例如新闻类App,有列表Fragment和内容Fragment,在平板和手机切换可重用Fragment。有点类似于在不同 Activity 中重复使用的“子 Activity”

生命周期

Fragment生命周期

Fragment面试必会知识点

static final int INITIALIZING = 0;     // Not yet created.
static final int CREATED = 1;          // Created.
static final int ACTIVITY_CREATED = 2; // The activity has finished its creation.
static final int STOPPED = 3;          // Fully created, not started.
static final int STARTED = 4;          // Created and started, not resumed.
static final int RESUMED = 5;     

Fragment面试必会知识点

和Activity交互的生命周期

Fragment面试必会知识点

代码结构

Fragment生命周期状态的转移是通过FragmentManager实现的,FragmentManager是一个抽象类,有Impl实现,具体可以看moveToState方法,而FragmentTransaction也是一个抽象类,在commit时才会执行之前add、replace等操作,具体的实现类是BackStackRecord

Logcat例子

在Activity的onCreate使用FragmentManager添加

2021-02-20 17:33:01.465 4010-4010/com.xx.app.test D/xx: Activity#onCreate
2021-02-20 17:33:01.481 4010-4010/com.xx.app.test D/xx: TestFragment#onAttach
2021-02-20 17:33:01.482 4010-4010/com.xx.app.test D/xx: TestFragment#onCreate
2021-02-20 17:33:01.482 4010-4010/com.xx.app.test D/xx: TestFragment#onCreateView
2021-02-20 17:33:01.484 4010-4010/com.xx.app.test D/xx: TestFragment#onActivityCreated
2021-02-20 17:33:01.485 4010-4010/com.xx.app.test D/xx: Activity#onStart
2021-02-20 17:33:01.485 4010-4010/com.xx.app.test D/xx: TestFragment#onStart
2021-02-20 17:33:01.486 4010-4010/com.xx.app.test D/xx: Activity#onResume
2021-02-20 17:33:01.486 4010-4010/com.xx.app.test D/xx: TestFragment#onResume


2021-02-20 17:33:05.587 4010-4010/com.xx.app.test D/xx: TestFragment#onPause
2021-02-20 17:33:05.587 4010-4010/com.xx.app.test D/xx: Activity#onPause
2021-02-20 17:33:06.006 4010-4010/com.xx.app.test D/xx: TestFragment#onStop
2021-02-20 17:33:06.006 4010-4010/com.xx.app.test D/xx: Activity#onStop
2021-02-20 17:33:06.006 4010-4010/com.xx.app.test D/xx: TestFragment#onDestroyView
2021-02-20 17:33:06.007 4010-4010/com.xx.app.test D/xx: TestFragment#onDestroy
2021-02-20 17:33:06.007 4010-4010/com.xx.app.test D/xx: TestFragment#onDetach
2021-02-20 17:33:06.007 4010-4010/com.xx.app.test D/xx: Activity#onDestroy

使用ViewPager


进入页面:
2021-02-20 15:21:47.809 26057-26057/com.xx.android E/xx: TestActivity#onCreate
2021-02-20 15:21:48.129 26057-26057/com.xx.android E/xx: TestActivity#onStart
2021-02-20 15:21:48.132 26057-26057/com.xx.android E/xx: TestActivity#onResume
2021-02-20 15:21:48.157 26057-26057/com.xx.android E/xx: TestFragment#onAttach - TestFragment{78d3cb6 #0 id=0x7xx098 android:switcher:2131755160:0}
2021-02-20 15:21:48.158 26057-26057/com.xx.android E/xx: TestFragment#onCreate - TestFragment{78d3cb6 #0 id=0x7xx098 android:switcher:2131755160:0}
2021-02-20 15:21:48.166 26057-26057/com.xx.android E/xx: TestFragment#onCreateView - TestFragment{78d3cb6 #0 id=0x7xx098 android:switcher:2131755160:0}
2021-02-20 15:21:48.169 26057-26057/com.xx.android E/xx: TestFragment#onActivityCreated - TestFragment{78d3cb6 #0 id=0x7xx098 android:switcher:2131755160:0}
2021-02-20 15:21:48.219 26057-26057/com.xx.android E/xx: TestFragment#onStart - TestFragment{78d3cb6 #0 id=0x7xx098 android:switcher:2131755160:0}
2021-02-20 15:21:48.225 26057-26057/com.xx.android E/xx: TestFragment#onResume - TestFragment{78d3cb6 #0 id=0x7xx098 android:switcher:2131755160:0}

进入另外一个页面:
2021-02-20 15:21:56.226 26057-26057/com.xx.android E/xx: TestActivity#onPause
2021-02-20 15:21:56.228 26057-26057/com.xx.android E/xx: TestFragment#onPause - TestFragment{78d3cb6 #0 id=0x7xx098 android:switcher:2131755160:0}
2021-02-20 15:21:56.820 26057-26057/com.xx.android E/xx: TestFragment#onStop - TestFragment{78d3cb6 #0 id=0x7xx098 android:switcher:2131755160:0}

回到上一个页面:
2021-02-20 15:22:07.246 26057-26057/com.xx.android E/xx: TestActivity#onRestart
2021-02-20 15:22:07.247 26057-26057/com.xx.android E/xx: TestActivity#onStart
2021-02-20 15:22:07.249 26057-26057/com.xx.android E/xx: TestFragment#onStart - TestFragment{78d3cb6 #0 id=0x7xx098 android:switcher:2131755160:0}
2021-02-20 15:22:07.256 26057-26057/com.xx.android E/xx: TestActivity#onResume
2021-02-20 15:22:07.261 26057-26057/com.xx.android E/xx: TestFragment#onResume - TestFragment{78d3cb6 #0 id=0x7xx098 android:switcher:2131755160:0}

离开页面:
2021-02-20 15:22:14.822 26057-26057/com.xx.android E/xx: TestActivity#onPause
2021-02-20 15:22:14.825 26057-26057/com.xx.android E/xx: TestFragment#onPause - TestFragment{78d3cb6 #0 id=0x7xx098 android:switcher:2131755160:0}
2021-02-20 15:22:15.281 26057-26057/com.xx.android E/xx: TestFragment#onStop - TestFragment{78d3cb6 #0 id=0x7xx098 android:switcher:2131755160:0}
2021-02-20 15:22:15.285 26057-26057/com.xx.android E/xx: TestActivity#onDestroy
2021-02-20 15:22:15.290 26057-26057/com.xx.android E/xx: TestFragment#onDestroyView - TestFragment{78d3cb6 #0 id=0x7xx098 android:switcher:2131755160:0}
2021-02-20 15:22:15.291 26057-26057/com.xx.android E/xx: TestFragment#onDestroy - TestFragment{78d3cb6 #0 id=0x7xx098 android:switcher:2131755160:0}
2021-02-20 15:22:15.292 26057-26057/com.xx.android E/xx: TestFragment#onDetach - TestFragment{78d3cb6 #0 id=0x7xx098 android:switcher:2131755160:0}

使用

添加

XML - 1

<fragment android:name="com.example.news.ArticleListFragment"
            android:id="@+id/list"
            android:layout_weight="1"
            android:layout_width="0dp"
            android:layout_height="match_parent" />

XML - 2

<androidx.fragment.app.FragmentContainerView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/fragment_container_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:name="com.example.ExampleFragment" />

使用FragmentManager

FragmentManager fm = getFragmentManager();
FragmentTransaction ft =fm.beginTransaction();
ft.replace(R.id.fragment_container, new TestFragment());
ft.commit();

回退栈管理

可以加入到回退栈里,按返回键不会退出Activity,会回退fragment

fragmentTransaction.replace(R.id.fragment_container_1, new TestFragment3());
fragmentTransaction.addToBackStack("WTF");
fragmentTransaction.commit();

回退Fragment除了点击返回按键,也可以调用popBackStack

FragmentManager fm = getFragmentManager();
//会把WTF1标记的fragment之上的全部清掉,不包含WTF1
fm.popBackStack("WTF1", 0);
//会把WTF1标记的fragment之上的全部清掉,包含WTF1
fm.popBackStack("WTF1", POP_BACK_STACK_INCLUSIVE);

回退Fragment,根据我的实际代码,发现Fragment会调用onDestroy、onDettach之后重新创建

和Activity、Fragment通信

public static class FragmentA extends ListFragment {
    OnArticleSelectedListener listener;
    ...
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        try {
            listener = (OnArticleSelectedListener) context;
        } catch (ClassCastException e) {
            throw new ClassCastException(context.toString() + " must implement OnArticleSelectedListener");
        }
    }
    ...
}

与宿主 Activity 共享数据

public class ItemViewModel extends ViewModel {
    private final MutableLiveData<Item> selectedItem = new MutableLiveData<Item>();
    public void selectItem(Item item) {
        selectedItem.setValue(item);
    }
    public LiveData<Item> getSelectedItem() {
        return selectedItem;
    }
}
public class MainActivity extends AppCompatActivity {
    private ItemViewModel viewModel;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        viewModel = new ViewModelProvider(this).get(ItemViewModel.class);
        viewModel.getSelectedItem().observe(this, item -> {
            // Perform an action with the latest item data
        });
    }
}

public class ListFragment extends Fragment {
    private ItemViewModel viewModel;

    @Override
    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        viewModel = new ViewModelProvider(requireActivity()).get(ItemViewModel.class);

        ...

        items.setOnClickListener(item -> {
            // Set a new item
            viewModel.select(item);
        });
    }
}

在 Fragment 之间共享数据

public class ListViewModel extends ViewModel {
    private final MutableLiveData<Set<Filter>> filters = new MutableLiveData<>();

    private final LiveData<List<Item>> originalList = ...;
    private final LiveData<List<Item>> filteredList = ...;

    public LiveData<List<Item>> getFilteredList() {
        return filteredList;
    }

    public LiveData<Set<Filter>> getFilters() {
        return filters;
    }

    public void addFilter(Filter filter) { ... }

    public void removeFilter(Filter filter) { ... }
}

public class ListFragment extends Fragment {
    private ListViewModel viewModel;

    @Override
    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        viewModel = new ViewModelProvider(requireActivity()).get(ListViewModel.class);
        viewModel.getFilteredList().observe(getViewLifecycleOwner(), list -> {
            // Update the list UI
        });
    }
}

public class FilterFragment extends Fragment {
    private ListViewModel viewModel;

    @Override
    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        viewModel = new ViewModelProvider(requireActivity()).get(ListViewModel.class);
        viewModel.getFilters().observe(getViewLifecycleOwner(), set -> {
            // Update the selected filters UI
        });
    }

    public void onFilterSelected(Filter filter) {
        viewModel.addFilter(filter);
    }

    public void onFilterDeselected(Filter filter) {
        viewModel.removeFilter(filter);
    }
}

在父 Fragment 与子 Fragment 之间共享数据

public class ListFragment extends Fragment {
    private ListViewModel viewModel;

    @Override
    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        viewModel = new ViewModelProvider(this).get(ListViewModel.class);
        viewModel.getFilteredList().observe(getViewLifecycleOwner(), list -> {
            // Update the list UI
        }
    }
}

public class ChildFragment extends Fragment {
    private ListViewModel viewModel;
    @Override
    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        viewModel = new ViewModelProvider(requireParentFragment()).get(ListViewModel.class);
        ...
    }
}

过渡动画

遇到的问题

getSupportFragmentManager()和getChildFragmentManager()

在 Activity 中访问

每个 FragmentActivity 及其子类(如 AppCompatActivity)都可以通过 getSupportFragmentManager() 方法访问 FragmentManager。

在 Fragment 中访问

Fragment 也能够托管一个或多个子 Fragment。在 Fragment 内,您可以通过 getChildFragmentManager() 获取对管理 Fragment 子级的 FragmentManager 的引用。如果您需要访问其宿主 FragmentManager,可以使用 getParentFragmentManager()。

commit() 和 commitAllowingStateLoss()

public int commit() {
    return commitInternal(false);
}

public int commitAllowingStateLoss() {
    return commitInternal(true);
}
int commitInternal(boolean allowStateLoss) {
    if (mCommitted) {
        throw new IllegalStateException("commit already called");
    }
    ......
    mCommitted = true;
    if (mAddToBackStack) {
        mIndex = mManager.allocBackStackIndex(this);
    } else {
        mIndex = -1;
    }
    mManager.enqueueAction(this, allowStateLoss);
    return mIndex;
}

public void enqueueAction(Runnable action, boolean allowStateLoss) {
    if (!allowStateLoss) {
        checkStateLoss();
    }
    synchronized (this) {
        if (mDestroyed || mHost == null) {
            throw new IllegalStateException("Activity has been destroyed");
        }
        if (mPendingActions == null) {
            mPendingActions = new ArrayList<Runnable>();
        }
        mPendingActions.add(action);
        if (mPendingActions.size() == 1) {
            mHost.getHandler().removeCallbacks(mExecCommit);
            mHost.getHandler().post(mExecCommit);
        }
    }
}

private void checkStateLoss() {
    if (mStateSaved) {
        throw new IllegalStateException(
                "Can not perform this action after onSaveInstanceState");
    }
    if (mNoTransactionsBecause != null) {
        throw new IllegalStateException(
                "Can not perform this action inside of " + mNoTransactionsBecause);
    }
}

如果activity的状态被保存了,这里再提交就会检查这个状态,符合条件就抛出一个异常来终止应用进程。也就是说在activity调用了onSaveInstanceState()之后,再commit一个事务就会出现该异常。 在Activity和FragmentActivity内的onSaveInstanceState方法保存了fragment的状态。

重建

在Activity意外销毁时会保存Fragment的状态,tag是android:support:fragments或者android:fragments

Fragment面试必会知识点

Fragment面试必会知识点

在onCreate中会恢复Fragment的状态

Fragment面试必会知识点

最终会走到FragmentManager的restoreAllState方法

Fragment面试必会知识点

Fragment面试必会知识点

最终会调用class的无参数构造方法

Fragment面试必会知识点

值得注意的是 FragmentManager的saveAllState方法会将Fragment的arguments也保存下来

Fragment面试必会知识点

Fragment面试必会知识点 所以使用Fragment时最好从getArguments中读取参数。

遇到的问题

Activity、Fragment Frament中有一些参数,自己写了create方法,set了一些参数, Activity有ViewPager,ViewPager使用FragmentPagerAdapter,每次重新创建Fragment填充到ViewPager中 不保留活动开启,按home再进入Activity 发生crash,发生NPE,就是自己写的create方法没走到,导致set的参数没有,发生NPE

1.为什么没走到自己写的create方法

恢复的时候会重建Fragment,调用的是无参构造函数

2.重新恢复的Activity中,ViewPager是新的Fragment还是上一次的Fragment 重建了,不是同一个了。

可以通过

savedInstanceState.remove("android:support:fragments");

参考

Commit is asynchronous

Calling commit() doesn't perform the transaction immediately. Rather, the transaction is scheduled to run on the main UI thread as soon as it is able to do so. If necessary, however, you can call commitNow() to run the fragment transaction on your UI thread immediately.

Note that commitNow is incompatible with addToBackStack. Alternatively, you can execute all pending FragmentTransactions submitted by commit() calls that have not yet run by calling executePendingTransactions(). This approach is compatible with addToBackStack.

For the vast majority of use cases, commit() is all you need.

FragmentPagerAdapter 和FragmentStatePagerAdapter

  • FragmentPagerAdapter 中每一个Fragment都长存在与内存中,适用于比较固定的少量的Fragment。FragmentPagerAdapter 在我们切换Fragment过程中不会销毁Fragment,只是调用事务中的detach方法。而在detach方法中只会销毁Fragment中的View,而不会销毁Fragment对象。
  • FragmentStatePagerAdapter中实现将只保留当前页面,当页面离开视线后,就会被消除,释放其资源。而在页面需要显示时,生成新的页面。在较多的Fragment的时候为了减少内存可适用。FragmentStatePagerAdapter在我们切换Fragment,会把前面的Fragment直接销毁掉。

懒加载

利用 setUserVisibleHint,这个方法可能在Fragment的生命周期之外调用,所以不要和Fragment的生命周期的顺序去绑定,没有参考价值。这个方法可能在FragmentPagerAdapter使用。 所以要在这个方法中判断Fragment的状态。

public abstract class BaseListFragment extends AbsMvpFragment

    @Override
     public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        ...
        if (!isLazyLoadEnable() && isAutoRefreshEnable()) {
            doRefresh(false);
        }
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (isLazyLoadEnable() && isVisibleToUser && !mHasLazyLoaded) {
            if (getView() != null) {
                onLazyLoad();
            } else {
                mDoLazyLoadLater = true;
            }
        }
    }

    @Override
    public void onStart() {
        super.onStart();
        if (mDoLazyLoadLater) {
            onLazyLoad();
        }
    }
转载自:https://juejin.cn/post/6984605769245130788
评论
请登录