我有一个应用程序,使用单Activity和多片段的方法,我使用NavController导航。不幸的是,当导航到一个片段,其中包含一个Runnable
在一个anymous类,这个片段的两个相同的示例被创建,我不明白为什么。
下面是主要活动的代码:
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);
}
}
nav_graph中的Home片段是片段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();
}
}
}
这里我有一个OnClickListener
,使用Navigation.findNavController(getView()).navigate(FR_MenuDirections.actionFRMenuToTest());
行中的navController导航到包含Runnables的Fragment。到目前为止,一切都很顺利。现在创建了包含Runnable的Fragment,称为Test
。下面您可以看到此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();
}
}
除了onCreate
和onCreateView
方法之外,这个片段还有两个基本方法:在updateScreen
方法中,当前的Fragment被打印到控制台。在countDownTime
方法中,创建了一个Runnable,并增加了一个辅助变量helpCounterRun
。辅助变量的值和Runnable的当前示例被打印到控制台。输出如下所示:
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}
从updateScreen
方法的输出中可以看到,这个Fragment的2个示例被创建并同时运行。一个示例的id为Test{706103f} (335e5b64-5e97-4f3d-ac1b-8f5a1fcc559c)
,另一个示例的id为Test{5140689} (c513c6da-fb15-4273-bea2-dfd89382d9e8) id=0x7f08013e}
,因此辅助变量helpCounter
在递增之前被打印了2次,其值相同。
我的问题是为什么会发生这种情况。我没有看到我的代码中有任何部分显式地创建了片段Test
的两个示例。你知道这种奇怪行为的原因是什么吗?我该如何处理它?
提醒:没人知道为什么会这样吗?
1条答案
按热度按时间hyrbngr71#
当你调用
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
时,你会强制你的Activity执行configuration change--从纵向更改为横向。默认情况下,Android会销毁你的Activity(以及其中的所有片段),并以你所要求的方向重新创建它。这就是为什么您会收到消息
Method updateScreen - getActivity(): null
-因为您的配置更改,Fragment及其所在的Activity已被完全销毁。然而,即使在片段视图被破坏之后,您也不会停止一遍又一遍地调用
countDownTime()
,这意味着您已经创建了一个永久性的内存泄漏。您已经在跟踪片段的视图是否是通过
viewHasBeenCreated
创建的,但您从未将其设置回false
-您可能希望覆盖onDestroyView()
,并将其用作您的视图已被破坏的信号。请注意,您不需要手动跟踪
viewHasBeenCreated
-您可以使用getView() != null
来执行相同的检查,但通过确保您实际上清理了onDestroyView()
中的Handler
,您将根本不需要执行此检查,因为可以保证您只在创建视图时运行。