[英]Android why are two identical Fragments created when navigating with NavController
I have an app that uses the single activity and multiple fragments approach and I use the NavController for navigating.我有一个使用单一活动和多个片段方法的应用程序,我使用 NavController 进行导航。 Unfortunately, when navigating to a Fragment that contains a
Runnable
in an anymous class, two identical instances of this Fragment are being created and I don't understand why.不幸的是,当导航到包含任意 class 中的
Runnable
的片段时,正在创建该片段的两个相同实例,我不明白为什么。
Here is the code of the main activity:这是主要活动的代码:
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
public static DB_SQLite_Helper sqLite_DB;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
View view = binding.getRoot();
getSupportActionBar().hide();
setContentView(view);
sqLite_DB = new DB_SQLite_Helper(this);
}
}
The Home-Fragment in the nav_graph is the Fragment FR_Menu
that you can see here: nav_graph 中的 Home-Fragment 是您可以在此处看到的 Fragment
FR_Menu
:
public class FR_Menu extends Fragment implements View.OnClickListener{
private FragmentMenuBinding binding;
public FR_Menu() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = FragmentMenuBinding.inflate(inflater, container, false);
getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
binding.buttonExit.setOnClickListener(this);
binding.buttonTest.setOnClickListener(this);
return binding.getRoot();
}
@Override
public void onClick(View view) {
if(view.getId() == R.id.button_test) {
Navigation.findNavController(getView()).navigate(FR_MenuDirections.actionFRMenuToTest());
}
if(view.getId() == R.id.button_exit) {
getActivity().finishAndRemoveTask();
}
}
}
Here I just have a OnClickListener
and navigate to the Fragment with the Runnables by using the navController in the line Navigation.findNavController(getView()).navigate(FR_MenuDirections.actionFRMenuToTest());
在这里,我只有一个 OnClickListener 并使用
Navigation.findNavController(getView()).navigate(FR_MenuDirections.actionFRMenuToTest());
行中的OnClickListener
导航到带有 Runnables 的片段。 . . So far, so good.
到目前为止,一切都很好。 Now the Fragment with the Runnable, called
Test
is created.现在创建了名为
Test
的带有 Runnable 的片段。 Here you see the code of this Fragment:在这里你可以看到这个片段的代码:
public class Test extends Fragment {
private Handler handler = new Handler();
int helpCounterRun =0;
private boolean viewHasBeenCreated = false;
private FragmentTestBinding binding;
public Test() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = FragmentTestBinding.inflate(inflater, container, false);
viewHasBeenCreated = true;
getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
countDownTime();
return binding.getRoot();
}
private void updateScreen() {
Log.e("LogTag", "Method updateScreen - this: " + this);
}
private void countDownTime(){
handler.postDelayed(new Runnable() {
@Override
public void run() {
helpCounterRun++;
Log.e("LogTag", "Method run - helpCounterRun: " + helpCounterRun);
Log.e("LogTag", "Method run - this: " + this);
if(viewHasBeenCreated) {
countDownTime();
}
}
}, 100);
updateScreen();
}
}
Next to the onCreate
and onCreateView
method this Fragment has 2 basic methods.除了
onCreate
和onCreateView
方法之外,这个 Fragment 有 2 个基本方法。 In the updateScreen
method, the current Fragment is printed to the console.在
updateScreen
方法中,将当前 Fragment 打印到控制台。 And in the countDownTime
method a Runnable is created and an auxilliary variable helpCounterRun
is incremented.并且在
countDownTime
方法中创建了一个 Runnable 并递增了一个辅助变量helpCounterRun
。 The value of the auxillary variable and the current instance of the Runnable are printed to the console.辅助变量的值和 Runnable 的当前实例被打印到控制台。 The output looks like this:
output 看起来像这样:
2022-04-18 10:01:33.742 25086-25086/com.example.game E/LogTag: Method run - helpCounterRun: 1
2022-04-18 10:01:33.743 25086-25086/com.example.game E/LogTag: Method run - this: com.example.game.Test$1@78ea3f9
2022-04-18 10:01:33.745 25086-25086/com.example.game E/LogTag: Method updateScreen - this: Test{706103f} (335e5b64-5e97-4f3d-ac1b-8f5a1fcc559c)}
2022-04-18 10:01:34.277 25086-25086/com.example.game E/LogTag: Method run - helpCounterRun: 1
2022-04-18 10:01:34.278 25086-25086/com.example.game E/LogTag: Method run - this: com.example.game.Test$1@9c893ee
2022-04-18 10:01:34.278 25086-25086/com.example.game E/LogTag: Method updateScreen - this: Test{5140689} (c513c6da-fb15-4273-bea2-dfd89382d9e8) id=0x7f08013e}
2022-04-18 10:01:34.294 25086-25086/com.example.game E/LogTag: Method run - helpCounterRun: 2
2022-04-18 10:01:34.305 25086-25086/com.example.game E/LogTag: Method run - this: com.example.game.Test$1@f8db08f
2022-04-18 10:01:34.306 25086-25086/com.example.game E/LogTag: Method updateScreen - this: Test{706103f} (335e5b64-5e97-4f3d-ac1b-8f5a1fcc559c)}
2022-04-18 10:01:34.382 25086-25086/com.example.game E/LogTag: Method run - helpCounterRun: 2
2022-04-18 10:01:34.382 25086-25086/com.example.game E/LogTag: Method run - this: com.example.game.Test$1@8b9ef1c
2022-04-18 10:01:34.382 25086-25086/com.example.game E/LogTag: Method updateScreen - this: Test{5140689} (c513c6da-fb15-4273-bea2-dfd89382d9e8) id=0x7f08013e}
2022-04-18 10:01:34.414 25086-25086/com.example.game E/LogTag: Method run - helpCounterRun: 3
2022-04-18 10:01:34.414 25086-25086/com.example.game E/LogTag: Method run - this: com.example.game.Test$1@9ad8725
2022-04-18 10:01:34.415 25086-25086/com.example.game E/LogTag: Method updateScreen - this: Test{706103f} (335e5b64-5e97-4f3d-ac1b-8f5a1fcc559c)}
2022-04-18 10:01:34.503 25086-25086/com.example.game E/LogTag: Method run - helpCounterRun: 3
2022-04-18 10:01:34.503 25086-25086/com.example.game E/LogTag: Method run - this: com.example.game.Test$1@9ae00fa
2022-04-18 10:01:34.504 25086-25086/com.example.game E/LogTag: Method updateScreen - this: Test{5140689} (c513c6da-fb15-4273-bea2-dfd89382d9e8) id=0x7f08013e}
2022-04-18 10:01:34.531 25086-25086/com.example.game E/LogTag: Method run - helpCounterRun: 4
2022-04-18 10:01:34.562 25086-25086/com.example.game E/LogTag: Method run - this: com.example.game.Test$1@ebec6ab
2022-04-18 10:01:34.562 25086-25086/com.example.game E/LogTag: Method updateScreen - this: Test{706103f} (335e5b64-5e97-4f3d-ac1b-8f5a1fcc559c)}
2022-04-18 10:01:34.611 25086-25086/com.example.game E/LogTag: Method run - helpCounterRun: 4
2022-04-18 10:01:34.611 25086-25086/com.example.game E/LogTag: Method run - this: com.example.game.Test$1@b04e108
2022-04-18 10:01:34.611 25086-25086/com.example.game E/LogTag: Method updateScreen - this: Test{5140689} (c513c6da-fb15-4273-bea2-dfd89382d9e8) id=0x7f08013e}
What you can see from the output of the updateScreen
method, that 2 instances of this Fragment are created and are running simultaneously.从
updateScreen
方法的 output 可以看出,此 Fragment 的 2 个实例已创建并同时运行。 One has the id Test{706103f} (335e5b64-5e97-4f3d-ac1b-8f5a1fcc559c)
and the other Test{5140689} (c513c6da-fb15-4273-bea2-dfd89382d9e8) id=0x7f08013e}
and thus the auxillary variable helpCounter
is printed out 2 times with the same value before being incremented.一个具有 id
Test{706103f} (335e5b64-5e97-4f3d-ac1b-8f5a1fcc559c)
和另一个Test{5140689} (c513c6da-fb15-4273-bea2-dfd89382d9e8) id=0x7f08013e}
因此辅助变量helpCounter
被打印出来在递增之前具有相同值的 2 次。
My question is why is this happening.我的问题是为什么会这样。 I don't see any part of my code that explicity created 2 instances of the Fragment
Test
.我没有看到我的代码的任何部分明确创建了 Fragment
Test
的 2 个实例。 Do you have any idea what the cause of this strange behaviour might be and how I can tackle it?您是否知道这种奇怪行为的原因可能是什么以及我该如何解决?
Reminder : Does nobody have an idea why this is happening?提醒:没有人知道为什么会这样吗?
When you call setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
, you force your activity to go through a configuration change - changing from portrait to landscape orientation.当您调用
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
时,您通过配置更改将您的活动强制为 go - 从纵向更改为横向。 By default, Android is going to destroy your activity (and all fragments in it) and recreate it in your requested orientation.默认情况下,Android 将销毁您的活动(以及其中的所有片段)并按照您要求的方向重新创建它。
This is why you are getting a message Method updateScreen - getActivity(): null
- that Fragment has been completely destroyed along with the activity it is in because of your configuration change.这就是为什么您收到一条消息
Method updateScreen - getActivity(): null
- 由于您的配置更改,Fragment 及其所在的 Activity 已被完全销毁。
However, you never stop calling countDownTime()
over and over again even after your fragment's view is destroyed.但是,即使在片段的视图被销毁之后,您也永远不会停止一遍又一遍地调用
countDownTime()
。 This means you've created a permanent memory leak.这意味着您已经创建了一个永久性的 memory 泄漏。
You're already tracking whether the fragment's view is created via your viewHasBeenCreated
, but you never set it back to false
- you'd want to override onDestroyView()
and use that as the signal that your View has been destroyed.您已经在跟踪片段的视图是否是通过您的
viewHasBeenCreated
创建的,但您从未将其设置回false
- 您想要覆盖onDestroyView()
并将其用作您的视图已被销毁的信号。 This is also the appropriate place to remove any postDelayed
calls that haven't yet run by using removeCallbacksAndMessages()
这也是使用
removeCallbacksAndMessages()
删除尚未运行的任何postDelayed
调用的合适位置
@Override
public void onDestroyView() {
super.onDestroyView();
// Reset your variable to false
viewHasBeenCreated = false;
// And clean up any postDelayed callbacks that are waiting to fire
handler.removeCallbacksAndMessages(null);
}
Note that you don't need to manually track viewHasBeenCreated
- you can use getView() != null
to do this same check, but by making sure you actually clean up your Handler
in onDestroyView()
, you won't need to do this check at all as you'll be guaranteed to only be running while your view is created.请注意,您不需要手动跟踪
viewHasBeenCreated
- 您可以使用getView() != null
来执行相同的检查,但是通过确保在onDestroyView()
中实际清理Handler
,您将不需要执行此操作完全检查,因为您将保证仅在创建视图时运行。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.