android 常见错误_android分割常见的查询和错误

android 常见错误

重点 (Top highlight)

The Fragment class in Android is used to build dynamic User Interfaces and should be used within the activity. The biggest advantage of using fragments is that it simplifies the task of creating UI for multiple screen sizes. An activity can contain any number of fragments.

Android中的Fragment类用于构建动态用户界面,应在活动中使用。 使用片段的最大优势在于,它简化了为多种屏幕尺寸创建UI的任务。 一个活动可以包含任意数量的片段。

Now, this explanation makes fragments sounds good and easy, right? Not so fast, there is a lot more involved. In this piece, we will cover the main needs and common mistakes while using fragments.

现在,这种解释使片段听起来很容易,对吗? 没那么快,涉及更多。 在本文中,我们将介绍使用片段时的主要需求和常见错误。

Note: I am assuming you are having basic knowledge of fragments and fragment lifecycle callbacks. Also, I am assuming you know how to implement communication between two fragments. This article goes beyond that.

注意:我假设您具有片段和片段生命周期回调的基本知识。 另外,我假设您知道如何实现两个片段之间的通信。 本文不仅仅限于此。

障碍物 (Obstacles)

Here are a few obstacles related to fragments some of you must have faced already or might run into in the future:

以下是一些与片段有关的障碍,其中有些必须已经遇到或将来可能遇到:

  • FragmentManager: getSupportFragmentManager and getChildFragmentManager. Which one to use when and avoid memory leaks while using them

    FragmentManager:getSupportFragmentManager和getChildFragmentManager。 何时使用哪一种,并避免使用它们时出现内存泄漏
  • Callback from DialogFragment, ChildFragment, BottomSheetFragment to parent fragment

    从DialogFragment,ChildFragment,BottomSheetFragment回调到父片段
  • Fragments when using ViewPager and when to use FragmentStateAdapter vs FragmentPagerAdapter.

    使用ViewPager以及何时使用FragmentStateAdapter和FragmentPagerAdapter时的片段。
  • When to use FragmentTransaction add vs replace

    何时使用FragmentTransaction添加与替换
  • Fragment receivers, broadcasts and memory leaks

    片段接收器,广播和内存泄漏
  • How to handle these Fragment BottomBarNavigation and drawer

    如何处理这些片段BottomBarNavigation和抽屉
  • commit() and commitAllowingStateLoss()

    commit()和commitAllowingStateLoss()
  • Fragment option menus

    片段选项菜单
  • Fragment getActivity(), getView() and NullPointers Exceptions

    片段getActivity(),getView()和NullPointers异常
  • onActivityResult with nested fragments

    带有嵌套片段的onActivityResult
  • Fragment and Bundle

    片段和捆绑
  • Back Navigation

    后退导航

Whoa, that’s a big list! If you’ve run into any that I’ve missed, let me know in the comments.

哇,那是一个很大的清单! 如果您遇到了我错过的任何内容,请在评论中告诉我。

getSupportFragmentManager和getChildFragmentManager (getSupportFragmentManager and getChildFragmentManager)

FragmentManager is class provided by the framework which is used to create transactions for adding, removing or replacing fragments.

FragmentManager是框架提供的类,用于创建用于添加,删除或替换片段的事务。

  • getSupportFragmentManager is associated with an activity. Consider it as a FragmentManager for your activity.

    getSupportFragmentManager 与活动相关联。 将其视为您活动的FragmentManager。

So whenever you are using ViewPager, BottomSheetFragment, and DialogFragment in an activity you will use getSupportFragmentManager

因此,每当在活动中使用ViewPager,BottomSheetFragment和DialogFragment时,都将使用getSupportFragmentManager

Example:

例:

BottomDialogFragment bottomSheetDialog = BottomDialogFragment.getInstance();
bottomSheetDialog.show(getSupportFragmentManager(), "Custom Bottom Sheet");
  • getChildFragmentManager is associated with fragments.

    getChildFragmentManager 与片段关联。

Whenever you are ViewPager inside a fragment you will use getChildFragmentManager.

每当您在片段中使用ViewPager时,都将使用getChildFragmentManager。

Example:

例:

FragmentManager cfManager=getChildFragmentManager();
viewPagerAdapter = new ViewPagerAdapter(cfManager);

Here is the official link for this for better understanding.

这是对此的官方链接 ,以便您更好地理解。

When it comes to common mistakes people make when they are using ViewPager inside a Fragment, they often pass getSupportFragmentManager, which is a fragment manager for an activity, and it causes issues such as memory leaks or ViewPager not updating properly.

当人们在Fragment中使用ViewPager时犯的常见错误时,他们通常会传递getSupportFragmentManager, 这是一项活动的片段管理器,它会导致诸如内存泄漏或ViewPager无法正确更新之类的问题。

The most important issue caused by using getSupportFragmentManager in fragments is a memory leak. But why does it happen? Well, you have a stack of fragments which are used by ViewPager, and all these fragments stack in activity since you used getSupportFragmentManager. Now if close your Parent fragment, it will be closed but it will not be destroyed because all child fragments are active and they are still in memory, hence causing leak. It will not just leak parent fragment but also all of the child fragments since none of them can be cleared from heap memory. So never try to use getSupportFragmentManager in a fragment

使用getSupportFragmentManager引起的最重要的问题 碎片是内存泄漏。 但是为什么会发生呢? 好吧,您有一堆ViewPager使用的片段,并且所有这些片段都在活动中堆叠,因为您使用的是getSupportFragmentManager。 现在,如果关闭您的父片段,它将被关闭,但不会被销毁,因为所有子片段都处于活动状态,并且它们仍在内存中,从而导致泄漏。 它不仅会泄漏父片段,还会泄漏所有子片段,因为它们都无法从堆内存中清除。 因此,请勿尝试在片段中使用getSupportFragmentManager

从DialogFragment,ChildFragment,BottomSheetFragment回调到父片段 (Callback from DialogFragment, ChildFragment, BottomSheetFragment to a Parent Fragment)

This is a very common issue people face when they use BottomSheetFragment or DialogFragment or ChildFragment.

人们在使用BottomSheetFragment或DialogFragment或ChildFragment时会遇到一个非常普遍的问题。

Example:

例:

Add a child fragment:

添加一个子片段:

Another example bottomSheetFragment:

另一个示例bottomSheetFragment:

BottomSheetDialogFragment fragment = BottomSheetDialogFragment.newInstance();
fragment.show(getChildFragmentManager(), fragment.getTag());

Now suppose you want a callback from these child fragments to parent fragments. Most people create connections between two fragments using an activity, few people pass interface listeners as a parameter to fragments (which is a really a bad practice that one should avoid). The best way to call getParentFragment() from your child fragment is to create a callback. This is very simple. Consider the example below:

现在假设您希望从这些子片段到父片段的回调。 大多数人使用活动在两个片段之间创建连接,很少有人将接口侦听器作为片段的参数传递(这是一种应避免的非常糟糕的做法)。 从您的子片段调用getParentFragment()的最佳方法是创建一个回调。 这很简单。 考虑下面的示例:

dialogFragment.show(ParentFragment.this.getChildFragmentManager(), "dialog_fragment");

Then set the callback to the parent fragment by adding the following code in the child fragment:

然后通过在子片段中添加以下代码,将回调设置为父片段:

That’s it. You can give a callback to your parent fragment now easily.

而已。 您现在可以轻松地给父片段提供回调。

Using the same method, you can create a callback from a child fragment inside ViewPager to the parent fragment who is holding ViewPager.

使用相同的方法,可以创建从ViewPager内的子片段到持有ViewPager的父片段的回调。

使用ViewPager以及何时使用FragmentStateAdapter和FragmentPagerAdapter时的片段 (Fragments When Using ViewPager and When to Use FragmentStateAdapter vs FragmentPagerAdapter)

FragmentPagerAdapter stores the whole fragment in memory and can cause an increase of memory overhead if a large number of fragments are used in ViewPager. FragmentStatePagerAdapter only stores the savedInstanceState of fragments and destroys all the fragments when they lose focus.

FragmentPagerAdapter将整个片段存储在内存中,如果ViewPager中使用了大量片段,则可能导致内存开销增加。 FragmentStatePagerAdapter仅存储FragmentStatePagerAdapter的savedInstanceState,并在失去焦点时销毁所有片段。

So when you are going to have many fragments, use FragmentStateAdapter. If ViewPager is going to have less than three fragments, use FragmentPagerAdapter.

因此,当您要有许多片段时,请使用FragmentStateAdapter。 如果ViewPager的片段少于三个,请使用FragmentPagerAdapter。

Let’s look at some commonly faced issues.

让我们看一些常见的问题。

Update ViewPager not working:

更新ViewPager不起作用:

Remember ViewPager fragments are managed by FragmentManager, either from fragment or activity, and FragmentManager holds instances of all ViewPager fragments.

请记住,ViewPager片段是由FragmentManager从片段或活动中管理的,而FragmentManager包含所有ViewPager片段的实例。

So when people say ViewPager is not refreshed, it’s nothing but old instances of fragments are still being held by FragmentManager. You need to find out why FragmentManger is holding an instance of fragments. Is there a leak or not? Ideally, to refresh ViewPager the following code works. If it is not, you are doing something wrong.

因此,当人们说ViewPager没有刷新时,它不过是FragmentManager仍保留的旧片段实例。 您需要找出FragmentManger为什么持有片段实例的原因。 是否有泄漏? 理想情况下,刷新ViewPager可以使用以下代码。 如果不是,则说明您做错了。

List<String> strings = new ArrayList<>();
strings.add("1");
strings.add("2");
strings.add("3");
viewPager.setAdapter(new PagerFragAdapter(getSupportFragmentManager(), strings));
strings.add("4");
viewPager.getAdapter().notifyDataSetChanged();

Access current Fragment from ViewPager:

从ViewPager访问当前片段:

This is also a very common issue we come across. If you come across this, either create an array list of fragments inside the adapter or try to access the fragment using some tags. I prefer another option, however. FragmentStateAdapter and FragmentPagerAdapter both provide the method setPrimaryItem. This can be used to set the current fragment as seen below:

这也是我们遇到的一个非常普遍的问题。 如果遇到这种情况,请在适配器内部创建片段的数组列表,或者尝试使用某些标签访问片段。 不过,我更喜欢另一种选择。 FragmentStateAdapter和FragmentPagerAdapter都提供方法setPrimaryItem。 可以用来设置当前片段,如下所示:

I am leaving a GitHub link to this simple ViewPager project so that everyone can understand better.

我将GitHub链接保留到这个简单的ViewPager项目,以便每个人都能更好地理解。

FragmentTransaction添加与替换 (FragmentTransaction Add vs Replace)

In our activity, we have a container with our fragments displayed inside.

在我们的活动中,我们有一个容器,其中装有片段。

Add will simply add a fragment to the container. Suppose you add FragmentA and FragmentB to the container. The container will have FragmentA and FragmentB and if the container is FrameLayout, fragments are added one above the other.

添加只会将一个片段添加到容器中。 假设您将FragmentA和FragmentB添加到容器中。 容器将具有FragmentA和FragmentB,并且如果容器是FrameLayout,则将片段添加到另一个之上。

Replace will simply replace a fragment on top of the container, so if I call create FragmentC and call replace FragmentB which was on top, FragmentB will be removed from the container (unless you are not calling addToBackStack) and now FragmentC will be on top.

Replace将简单地替换容器顶部的片段,因此,如果我调用create FragmentC并调用顶部的replace FragmentB,则FragmentB将被从容器中删除(除非您不调用addToBackStack),现在FragmentC将位于顶部。

So which one to use when? replace removes the existing fragment and adds a new fragment. This means when you press the back button, the fragment that got replaced will be created with its onCreateView being invoked. On the other hand, add retains the existing fragments and adds a new fragment that means existing fragment will be active and they wont be in 'paused' state. Hence, when a back button is pressed onCreateView, it is not called for the existing fragment (the fragment which was there before the new fragment was added). In terms of fragment's life cycle events, onPause, onResume, onCreateView, and other life cycle events will be invoked in case of replace but not in the case of add.

那么什么时候使用哪个呢? replace删除现有片段并添加一个新片段。 这意味着当您按下“后退”按钮时,将创建被替换的片段,并调用其onCreateView。 另一方面, add保留现有片段,并添加一个新片段,这意味着现有片段将处于活动状态,并且它们不会处于“已暂停”状态。 因此,在CreateView上按下后退按钮时,现有片段(添加新片段之前的片段)不会被调用。 就片段的生命周期事件而言,在replace情况下将调用onPause,onResume,onCreateView和其他生命周期事件,在add的情况下不会调用。

Use replace fragment if don’t need to revisit current fragments and current fragments are not required anymore. Also if your app has memory constraints, consider using replace instead of add.

如果不需要重新访问当前片段并且不再需要当前片段,请使用替换片段。 另外,如果您的应用有内存限制,请考虑使用replace而不是add。

片段接收器,广播和内存泄漏 (Fragment Receivers, Broadcasts and Memory Leaks)

When using receivers inside a fragment, a common mistake is to forget to unregister the receiver in onPause or OnDestroy. If you are registering a fragment to listen to the receiver inside onCreate or OnResume, you will have to unregister it inside onPause or onDestroy. Otherwise, it will cause a memory leak.

在片段中使用接收器时,常见的错误是忘记在onPause或OnDestroy中注销接收器。 如果要注册一个片段以监听onCreate或OnResume中的接收器,则必须在onPause或onDestroy中取消注册它。 否则,将导致内存泄漏。

LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(mYourBroadcastReceiver);

Also, if have multiple fragments listening to the same broadcast receiver, make sure you register in onResume and unregister in onPause. If you use onCreate and onDestroy to register and unregister, other fragments will not receive the broadcast as this fragment is not destroyed

另外,如果有多个片段正在收听同一广播接收器,请确保您在onResume中注册,并在onPause中注销。 如果使用onCreate和onDestroy进行注册和注销,则其他片段将不会收到广播,因为该片段未被销毁

如何处理片段BottomBarNavigation和NavigationDrawer (How to Handle Fragment BottomBarNavigation and NavigationDrawer)

When we are using BottomBarNavigation and NavigationDrawer, it’s common to see issues such as fragments being recreated or the same fragment being added multiple times.

当我们使用BottomBarNavigation和NavigationDrawer时,通常会看到诸如重新创建片段或多次添加相同片段之类的问题。

In such a case, you can use the fragment transactions show and hide instead of add or replace.

在这种情况下,您可以使用片段事务显示和隐藏而不是添加或替换。

There is also a beautiful library which takes care of navigations and avoids recreation of fragments called FragNav. I’ve linked it below.

还有一个漂亮的图书馆,它负责导航并避免重新生成称为FragNav的片段。 我已经在下面链接了。

commit()和commitAllowingStateLoss() (commit() and commitAllowingStateLoss())

If your activity is not in a resume state and you try to commit a fragment, your app will crash. To avoid this, you need to check if the activity or fragment is in a resume state or not isAdded() / isResumed()

如果您的活动未处于恢复状态,并且您尝试提交片段,则应用程序将崩溃。 为避免这种情况,您需要检查活动或片段是否处于恢复状态,或者是否是isAdded() / isResumed()

Another solution, if you don’t care much about the state of the fragment, is that you can call commitAllowingStateLoss. This ensures the fragments are added or replaced, despite the activity is finishing or not being in a resume state.

如果您不太关心片段的状态,则另一个解决方案是可以调用commitAllowingStateLoss。 这样可以确保添加或替换片段,尽管活动已完成或未处于恢复状态。

片段选项菜单 (Fragment Option Menus)

When using option menu inside the fragment, remember to add the following line. People often forgot to add this and keep wondering where the option is on the toolbar.

在片段中使用选项菜单时,请记住添加以下行。 人们经常忘记添加此内容,而一直想知道该选项在工具栏上的何处。

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}

When using the toolbar inside the fragment, you can inflate the menu using code:

在片段中使用工具栏时,可以使用以下代码来扩展菜单:

getToolbar().inflateMenu(R.menu.toolbar_menu_gmr);

getToolbar().inflateMenu(R.menu.toolbar_menu_gmr);

Alternatively, you can override on createOptionsMenu but I prefer the above method as it does not rely on a super class

另外,您可以在createOptionsMenu上重写,但我更喜欢上面的方法,因为它不依赖于超类

片段getActivity(),getView()NullPointers异常 (Fragment getActivity(), getView() NullPointers Exceptions)

If any background process posts a result and the fragment is not in the stack or in a resume state, accessing the view of the fragment will cause a NullPointer exception. So, whenever you are accessing getView or getActivity after a background operation or delay, make sure you cancel all background operations on termination.

如果有任何后台进程发布结果,并且该片段不在堆栈中或处于恢复状态,则访问该片段的视图将导致NullPointer异常。 因此,每当在后台操作或延迟后访问getView或getActivity时,请确保在终止时取消所有后台操作。

Example:

例:

嵌套片段onActivityResult (Nested Fragment onActivityResult)

Yes, the onActivityResult() in a nested fragment will not be invoked.

是的,嵌套片段中的onActivityResult()不会被调用。

The calling sequence of onActivityResult (in Android support library) is

onActivityResult的调用顺序(在Android支持库中)为

  1. Activity.dispatchActivityResult().

    Activity.dispatchActivityResult()

  2. FragmentActivity.onActivityResult().

    FragmentActivity.onActivityResult()

  3. Fragment.onActivityResult().

    Fragment.onActivityResult()

You will have to use onActivityResult() in parent fragments or activity and to pass the result to a nested fragment as below:

您将必须在父片段或活动中使用onActivityResult()并将结果传递给嵌套片段,如下所示:

片段和捆绑 (Fragment and Bundle)

Whenever you are passing arguments to a fragment, make sure you are using Bundle instead of a constructor.

每当将参数传递给片段时,请确保您使用的是Bundle而不是构造函数。

The Android documentation states:

Android文档指出:

Every fragment must have an empty constructor, so it can be instantiated when restoring its activity’s state. It is strongly recommended that subclasses do not have other constructors with parameters, since these constructors will not be called when the fragment is re-instantiated; instead, arguments can be supplied by the caller with setArguments(Bundle) and later retrieved by the Fragment with getArguments().

每个片段都必须有一个空的构造函数,因此可以在恢复其活动状态时实例化它。 强烈建议子类不要使用带有参数的其他构造函数,因为在重新实例化片段时不会调用这些构造函数; 取而代之的是,参数可以由调用者使用setArguments(Bundle)提供,然后由Fragment使用getArguments()进行检索。

That’s why it’s better to use a bundle to set the parameters of the fragment, it’s easier for the system to restore its values when the fragment is re-instantiated.

这就是为什么最好使用捆绑软件设置片段的参数的原因,当重新实例化片段时,系统更容易恢复其值。

后退导航 (Back Navigation)

You should ensure that pressing the Back button on a detail screen returns the user to the master screen. To do so, call addToBackStack() before you commit the transaction:

您应确保在详细信息屏幕上按“ 后退”按钮可将用户返回到主屏幕。 为此,请在提交事务之前调用addToBackStack()

When there are FragmentTransaction objects on the back stack and the user presses the Back button, the FragmentManager pops the most recent transaction off the back stack and performs the reverse action (such as removing a fragment if the transaction added it).

当后退堆栈上有FragmentTransaction对象,并且用户按下“ 后退”按钮时, FragmentManager会将最新的事务弹出后退堆栈并执行相反的操作(例如,如果事务添加了片段,则删除片段)。

结论 (Conclusion)

Fragments can seem pretty easy at first but there is a lot more to them. You need to be careful of a number of things while using fragments, such as memory, navigation, callbacks, and bundle. I hope most commonly faced issues and most commonly made mistakes were covered in this article.

起初碎片看起来很容易,但是碎片还有很多。 使用片段时,需要注意许多事项,例如内存,导航,回调和捆绑。 我希望本文涵盖最常遇到的问题和最常犯的错误。

翻译自: https://levelup.gitconnected.com/android-fragments-common-queries-and-mistakes-915b4a1f34ce

android 常见错误

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值