My code toggles airplane mode continuously

2019-06-08 23:58发布

问题:

I have a screen (Activity) that does the following: it has a toggle button that toggles the airplane mode; it does it by using a service that spawn off a new Thread. It also has a button that does exactly the same thing in exactly the same way. There is really nothing fancy as the code snippets below show. While everything works as expected for the toggle button (airplane mode changes to "on" if currently the phone is not in airplane mode; changes to "off" if currently in airplane mode), when the button is clicked, the airplane mode toggles continuously (airplane mode toggles from "on" to "off" and back to "on" and then to "off"...) as if it falls into a loop. After some research on the internet, I suspected this had something to do with the way the intent/broadcastReceiver for the phone/service state was fired in Android; as the toggle button had two states which effectively prevented the intent from being broadcast again. Is this correct?? If so, what would be the right way of toggling airplane mode using a button (vs. a radiobutton or a togglebutton)?

/** Handler for the button. */
runToggleAirplaneModeServiceBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
    startService(new Intent(SleepScheduleController.this, AirplaneModeService.class));
}
});
/** Handler for the toggle button. */
airplaneModeToggleButton.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    startService(new Intent(SleepScheduleController.this, AirplaneModeService.class));
}
});
/** In the same screen as the toggle button and the button.
 * Need this to update the state of the toggle button once 
 * the toggling command is executed.
 */
intentFilter = new IntentFilter("android.intent.action.SERVICE_STATE");
receiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        displayAirplaneMode();
    }};
registerReceiver(receiver, intentFilter);

private void displayAirplaneMode() {
    if(airplaneModeToggler.isInAirplaneMode()){
        airplaneModeToggleButton.setChecked(true);
        airplaneModeTextView.setText(R.string.airplane_mode_on);
    }else{
     airplaneModeToggleButton.setChecked(false);
     airplaneModeTextView.setText(R.string.airplane_mode_off);
    }
}

/** Code snippet in AirplaneModeService*/
@Override
public void onCreate() {
    airplaneModeToggler = new AirplaneModeToggler(this);
    Thread mThread = new Thread(null, airplaneModeToggleTask, "AirplaneModeToggleTask");
    mThread.start();
}

private Runnable airplaneModeToggleTask = new Runnable() {
    @Override
    public void run() {
        airplaneModeToggler.toggle(null);
        AirplaneModeService.this.stopSelf();
    }
};

/** Code snippet in the Utility class used by AirplaneModeService.*/
public Boolean toggleAirplaneMode(Boolean enabling) {
    boolean _enabling = enabling == null ? !isInAirplaneMode() : enabling.booleanValue();
Settings.System.putInt(mContext.getContentResolver(),
            Settings.System.AIRPLANE_MODE_ON, 
            _enabling ? AIRPLANE_MODE_ON : AIRPLANE_MODE_OFF);
    Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
    intent.putExtra("state", _enabling);
    mContext.sendBroadcast(intent);
    return _enabling;
}

回答1:

So I tracked down the bug. It's caused by the OnCheckedChangeListener of the toggleButton re-excuting the command in my code. Say if the toggling command was previously called programmatically, or by clicking on the other button, then these would cause the checked state change on the toggleButton, which would subsequently execute the same toggling command, which would result in another round of checked state change on the toggleButton.