android-fragments Android为什么在使用NavController导航时会创建两个相同的片段

6ioyuze2  于 2022-11-14  发布在  Android
关注(0)|答案(1)|浏览(206)

我有一个应用程序,使用单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();
    }

}

除了onCreateonCreateView方法之外,这个片段还有两个基本方法:在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的两个示例。你知道这种奇怪行为的原因是什么吗?我该如何处理它?

提醒:没人知道为什么会这样吗?

hyrbngr7

hyrbngr71#

当你调用setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)时,你会强制你的Activity执行configuration change--从纵向更改为横向。默认情况下,Android会销毁你的Activity(以及其中的所有片段),并以你所要求的方向重新创建它。
这就是为什么您会收到消息Method updateScreen - getActivity(): null-因为您的配置更改,Fragment及其所在的Activity已被完全销毁。
然而,即使在片段视图被破坏之后,您也不会停止一遍又一遍地调用countDownTime(),这意味着您已经创建了一个永久性的内存泄漏。
您已经在跟踪片段的视图是否是通过viewHasBeenCreated创建的,但您从未将其设置回false-您可能希望覆盖onDestroyView(),并将其用作您的视图已被破坏的信号。

@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);
}

请注意,您不需要手动跟踪viewHasBeenCreated-您可以使用getView() != null来执行相同的检查,但通过确保您实际上清理了onDestroyView()中的Handler,您将根本不需要执行此检查,因为可以保证您只在创建视图时运行。

相关问题