I have a general problem understanding how the run method is called when using handler.postDelayed in Android.
I have the following Java code:
public class Test extends Fragment implements Runnable {
public static final int DELAY_IN_MILLIS = 10;
private int currentTimeLeft_MILLIS;
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);
currentTimeLeft_MILLIS = 90 * 1000;
}
@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() {
binding.textViewTimeLeftValue.setText("" + currentTimeLeft_MILLIS/1000);
Log.e("LogTag", "Method updateScreen" );
}
private void countDownTime(){
currentTimeLeftInTheLevel_MILLIS = currentTimeLeftInTheLevel_MILLIS - DELAY_IN_MILLIS;
Log.e("LogTag", "Method countDownTime - currentTimeLeftInTheLevel_MILLIS: " + currentTimeLeftInTheLevel_MILLIS);
handler.postDelayed(this, DELAY_IN_MILLIS);
updateScreen();
}
@Override
public void run() {
helpCounterRun++;
Log.e("LogTag", "Method run - helpCounterRun: " + helpCounterRun);
if(viewHasBeenCreated) {
countDownTime();
}
}
}
Next to the onCreate and onCreateView method this Fragment has 3 basic methods. The updateScreen() method should update the currentTimeLeft_MILLIS in a textview. So it should actually display the remaining time in seconds (starting from 90 seconds). The method countDownTime() should just decrease the variable currentTimeLeft_MILLIS by DELAY_IN_MILLIS call the method updateScreen() and it used the call handler.postDelayed(this, DELAY_IN_MILLIS); to call the Runnable run() which calls the countDownTime()method again.
When I look at the LogTagoutputs it looks like this
2022-03-11 17:59:30.128 7361-7361/com.example.game E/LogTag: Method run - helpCounterRun: 1
2022-03-11 17:59:30.128 7361-7361/com.example.game E/LogTag: Method countDownTime - currentTimeLeft_MILLIS: 89980
2022-03-11 17:59:30.135 7361-7361/com.example.game E/LogTag: Method updateScreen
2022-03-11 17:59:31.918 7361-7361/com.example.game E/LogTag: Method run - helpCounterRun: 1
2022-03-11 17:59:31.919 7361-7361/com.example.game E/LogTag: Method countDownTime - currentTimeLeft_MILLIS: 89980
2022-03-11 17:59:31.919 7361-7361/com.example.game E/LogTag: Method updateScreen
2022-03-11 17:59:31.924 7361-7361/com.example.game E/LogTag: Method run - helpCounterRun: 2
2022-03-11 17:59:31.925 7361-7361/com.example.game E/LogTag: Method countDownTime - currentTimeLeft_MILLIS: 89970
2022-03-11 17:59:31.925 7361-7361/com.example.game E/LogTag: Method updateScreen
2022-03-11 17:59:32.028 7361-7361/com.example.game E/LogTag: Method run - helpCounterRun: 2
2022-03-11 17:59:32.029 7361-7361/com.example.game E/LogTag: Method countDownTime - currentTimeLeft_MILLIS: 89970
2022-03-11 17:59:32.029 7361-7361/com.example.game E/LogTag: Method updateScreen
2022-03-11 17:59:32.029 7361-7361/com.example.game E/LogTag: Method run - helpCounterRun: 3
2022-03-11 17:59:32.029 7361-7361/com.example.game E/LogTag: Method countDownTime - currentTimeLeft_MILLIS: 89960
2022-03-11 17:59:32.030 7361-7361/com.example.game E/LogTag: Method updateScreen
2022-03-11 17:59:32.089 7361-7361/com.example.game E/LogTag: Method run - helpCounterRun: 3
2022-03-11 17:59:32.089 7361-7361/com.example.game E/LogTag: Method countDownTime - currentTimeLeft_MILLIS: 89960
What I don't understand is why the run method is called two times before it updates helpCounterRun++;. So I can see that the code "visits" the run() method one time in which it does not update the variable helpCounterRun++; ? Also the other methods do every thing two times before updating and I don't understand why. Do I start 2 separate threads with this code?
Another problem (that might be realted to the first problem) is that the countdown that the textView displays is too slow. It should decrease the value by 1 every second (by decreasing the value of currentTimeLeftInTheLevel_MILLIS after DELAY_IN_MILLIS).
Do you have any idea why these 2 problems occur?
Update: When use thisbefore printing the strings in the LogTag in Log.e("LogTag", "Method countDownTime - this: " + this); and Log.e("LogTag", "Method run - this: " + this);
I get a slightly different output:
2022-03-13 09:37:54.693 9512-9512/com.example.game E/LogTag: Method run - helpCounterRun: 1
2022-03-13 09:37:54.693 9512-9512/com.example.game E/LogTag: Method run - this: Test{1f79232} (ad58656d-a106-4ab6-8d65-8ee20d095fbf)}
2022-03-13 09:37:54.693 9512-9512/com.example.game E/LogTag: Method countDownTime - currentTimeLeft_MILLIS: 89980
2022-03-13 09:37:54.694 9512-9512/com.example.game E/LogTag: Method countDownTime - this: Test{1f79232} (ad58656d-a106-4ab6-8d65-8ee20d095fbf)}
2022-03-13 09:37:54.694 9512-9512/com.example.game E/LogTag: Method updateScreen
2022-03-13 09:37:54.696 9512-9512/com.example.game E/LogTag: Method updateScreen - this: Test{1f79232} (ad58656d-a106-4ab6-8d65-8ee20d095fbf)}
2022-03-13 09:37:55.991 9512-9512/com.example.game E/LogTag: Method run - helpCounterRun: 1
2022-03-13 09:37:55.991 9512-9512/com.example.game E/LogTag: Method run - this: Test{1cbc30c} (6c4409c6-c501-4b40-9e33-cbf1895c68f6) id=0x7f08013c}
2022-03-13 09:37:55.992 9512-9512/com.example.game E/LogTag: Method countDownTime - currentTimeLeft_MILLIS: 89980
2022-03-13 09:37:55.992 9512-9512/com.example.game E/LogTag: Method countDownTime - this: Test{1cbc30c} (6c4409c6-c501-4b40-9e33-cbf1895c68f6) id=0x7f08013c}
2022-03-13 09:37:55.993 9512-9512/com.example.game E/LogTag: Method updateScreen
2022-03-13 09:37:55.993 9512-9512/com.example.game E/LogTag: Method updateScreen - this: Test{1cbc30c} (6c4409c6-c501-4b40-9e33-cbf1895c68f6) id=0x7f08013c}
2022-03-13 09:37:56.009 9512-9512/com.example.game E/LogTag: Method run - helpCounterRun: 2
2022-03-13 09:37:56.010 9512-9512/com.example.game E/LogTag: Method run - this: Test{1f79232} (ad58656d-a106-4ab6-8d65-8ee20d095fbf)}
2022-03-13 09:37:56.010 9512-9512/com.example.game E/LogTag: Method countDownTime - currentTimeLeft_MILLIS: 89970
2022-03-13 09:37:56.010 9512-9512/com.example.game E/LogTag: Method countDownTime - this: Test{1f79232} (ad58656d-a106-4ab6-8d65-8ee20d095fbf)}
2022-03-13 09:37:56.020 9512-9512/com.example.game E/LogTag: Method updateScreen
2022-03-13 09:37:56.021 9512-9512/com.example.game E/LogTag: Method updateScreen - this: Test{1f79232} (ad58656d-a106-4ab6-8d65-8ee20d095fbf)}
2022-03-13 09:37:56.121 9512-9512/com.example.game E/LogTag: Method run - helpCounterRun: 2
2022-03-13 09:37:56.122 9512-9512/com.example.game E/LogTag: Method run - this: Test{1cbc30c} (6c4409c6-c501-4b40-9e33-cbf1895c68f6) id=0x7f08013c}
2022-03-13 09:37:56.122 9512-9512/com.example.game E/LogTag: Method countDownTime - currentTimeLeft_MILLIS: 89970
2022-03-13 09:37:56.122 9512-9512/com.example.game E/LogTag: Method countDownTime - this: Test{1cbc30c} (6c4409c6-c501-4b40-9e33-cbf1895c68f6) id=0x7f08013c}
2022-03-13 09:37:56.124 9512-9512/com.example.game E/LogTag: Method updateScreen
2022-03-13 09:37:56.125 9512-9512/com.example.game E/LogTag: Method updateScreen - this: Test{1cbc30c} (6c4409c6-c501-4b40-9e33-cbf1895c68f6) id=0x7f08013c}
Update 2: I was asked to show the code that launches the problematic fragment Test. Here you can see the Fragment FR_Menu that launches the fragment Testafter clicking on the corresponding button.
public class FR_Menu extends Fragment implements View.OnClickListener{
private FragmentMenuBinding binding;
public FR_Menu() {
// Required empty public constructor
}
@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.buttonGame.setOnClickListener(this);
binding.buttonOptions.setOnClickListener(this);
binding.buttonHighscores.setOnClickListener(this);
binding.buttonFacts.setOnClickListener(this);
binding.buttonExit.setOnClickListener(this);
binding.buttonTest.setOnClickListener(this);
return binding.getRoot();
}
@Override
public void onClick(View view) {
if(view.getId() == R.id.button_game) {
Navigation.findNavController(getView()).navigate(FR_MenuDirections.actionFRMenuToFRGame());
}
if(view.getId() == R.id.button_highscores) {
Navigation.findNavController(getView()).navigate(FR_MenuDirections.actionFRMenuToFRHighScores());
}
if(view.getId() == R.id.button_facts) {
Navigation.findNavController(getView()).navigate(FR_MenuDirections.actionFRMenuToFRInterestingFacts());
}
if(view.getId() == R.id.button_options) {
Navigation.findNavController(getView()).navigate(FR_MenuDirections.actionFRMenuToFROptions());
}
//This launches the problematic Fragment "Test"
if(view.getId() == R.id.button_test) {
Navigation.findNavController(getView()).navigate(FR_MenuDirections.actionFRMenuToTest());
}
if(view.getId() == R.id.button_exit) {
getActivity().finishAndRemoveTask();
}
}
}
Reminder: Does nobody have any idea?