Clicking the back button twice to exit an activity

JavaAndroidBack Button

Java Problem Overview


I've noticed this pattern in a lot of Android apps and games recently: when clicking the back button to "exit" the application, a Toast comes up with a message similar to "Please click BACK again to exit".

I was wondering, as I'm seeing it more and more often, is that a built-in feature that you can somehow access in an activity? I've looked at the source code of many classes but I can't seem to find anything about that.

Of course, I can think about a few ways to achieve the same functionality quite easily (the easiest is probably to keep a boolean in the activity that indicates whether the user already clicked once...) but I was wondering if there's something already here.

EDIT: As @LAS_VEGAS mentioned, I didn't really mean "exit" in the traditional meaning. (i.e. terminated) I meant "going back to whatever was open before the application start activity was launched", if that makes sense :)

Java Solutions


Solution 1 - Java

> In Java Activity:

boolean doubleBackToExitPressedOnce = false;

@Override
public void onBackPressed() {
    if (doubleBackToExitPressedOnce) {
        super.onBackPressed();
        return;
    }
        
    this.doubleBackToExitPressedOnce = true;
    Toast.makeText(this, "Please click BACK again to exit", Toast.LENGTH_SHORT).show();
        
    new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
        
        @Override
        public void run() {
            doubleBackToExitPressedOnce=false;                       
        }
    }, 2000);
} 

> In Kotlin Activity:

private var doubleBackToExitPressedOnce = false
override fun onBackPressed() {
        if (doubleBackToExitPressedOnce) {
            super.onBackPressed()
            return
        }

        this.doubleBackToExitPressedOnce = true
        Toast.makeText(this, "Please click BACK again to exit", Toast.LENGTH_SHORT).show()

        Handler(Looper.getMainLooper()).postDelayed(Runnable { doubleBackToExitPressedOnce = false }, 2000)
    }

I Think this handler helps to reset the variable after 2 second.

Solution 2 - Java

Sudheesh B Nair's has a nice (and accepted) answer on the question, which i think should have a better alternative such as;

What's wrong with measuring time passed and checking if TIME_INTERVAL miliseconds (say 2000) passed since the last back press. The following sample code uses System.currentTimeMillis(); to store the time onBackPressed() is called;

private static final int TIME_INTERVAL = 2000; // # milliseconds, desired time passed between two back presses.
private long mBackPressed;

@Override
public void onBackPressed()
{
    if (mBackPressed + TIME_INTERVAL > System.currentTimeMillis()) 
    { 
        super.onBackPressed(); 
        return;
    }
    else { Toast.makeText(getBaseContext(), "Tap back button in order to exit", Toast.LENGTH_SHORT).show(); }

    mBackPressed = System.currentTimeMillis();
}

Back on accepted answer critique; Using a flag to indicate if it was pressed in last TIME_INTERVAL (say 2000) milliseconds and set - reset is via Handler's postDelayed() method was the first thing to come in my mind. But the postDelayed() action should be cancelled when activity is closing, removing the Runnable.

In order to remove the Runnable, it must not be declared anonymous, and be declared as member along with the Handler aswell. Then removeCallbacks() method of Handler can be called appropriately.

The following sample is the demonstration;

private boolean doubleBackToExitPressedOnce;
private Handler mHandler = new Handler();

private final Runnable mRunnable = new Runnable() {
    @Override
    public void run() {
        doubleBackToExitPressedOnce = false;                       
    }
};

@Override 
protected void onDestroy() 
{ 
    super.onDestroy();

    if (mHandler != null) { mHandler.removeCallbacks(mRunnable); }
}

@Override
public void onBackPressed() {
    if (doubleBackToExitPressedOnce) {
        super.onBackPressed();
        return;
    }
 
    this.doubleBackToExitPressedOnce = true;
    Toast.makeText(this, "Please click BACK again to exit", Toast.LENGTH_SHORT).show();

    mHandler.postDelayed(mRunnable, 2000);
}

Thanks to @NSouth for contributing; In order to prevent toast message appearing even after the application is closed, Toast can be declared as a member - say mExitToast - and can be cancelled via mExitToast.cancel(); just before super.onBackPressed(); call.

Solution 3 - Java

Just thought I would share how I did it in the end, I just added in my activity:

private boolean doubleBackToExitPressedOnce = false;

@Override
protected void onResume() {
    super.onResume();
    // .... other stuff in my onResume ....
    this.doubleBackToExitPressedOnce = false;
}

@Override
public void onBackPressed() {
    if (doubleBackToExitPressedOnce) {
        super.onBackPressed();
        return;
    }
    this.doubleBackToExitPressedOnce = true;
    Toast.makeText(this, R.string.exit_press_back_twice_message, Toast.LENGTH_SHORT).show();
}

And it just works exactly as I want. Including the reset of the state whenever the activity is resumed.

Solution 4 - Java

There is very simplest way among all these answers.

Simply write following code inside onBackPressed() method.

long back_pressed;

@Override
public void onBackPressed() {
	if (back_pressed + 1000 > System.currentTimeMillis()){
		super.onBackPressed();
	}
	else{
		Toast.makeText(getBaseContext(),
				"Press once again to exit!", Toast.LENGTH_SHORT)
				.show();
	}
	back_pressed = System.currentTimeMillis();
}

You need to define back_pressed object as long in activity.

Solution 5 - Java

Process Flow Diagram: Press again to exit.

Java Code:

private long lastPressedTime;
private static final int PERIOD = 2000;

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
	if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
		switch (event.getAction()) {
		case KeyEvent.ACTION_DOWN:
			if (event.getDownTime() - lastPressedTime < PERIOD) {
				finish();
			} else {
				Toast.makeText(getApplicationContext(), "Press again to exit.",
						Toast.LENGTH_SHORT).show();
				lastPressedTime = event.getEventTime();
			}
			return true;
		}
	}
	return false;
}

Solution 6 - Java

You may use a snackbar instead of a toast, so you can rely on their visibility to decide whether to close the app or not. Take this example:

Snackbar mSnackbar;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    final LinearLayout layout = findViewById(R.id.layout_main);
    mSnackbar = Snackbar.make(layout, R.string.press_back_again, Snackbar.LENGTH_SHORT);
}

@Override
public void onBackPressed() {
    if (mSnackbar.isShown()) {
        super.onBackPressed();
    } else {
        mSnackbar.show();
    }
}

Solution 7 - Java

Based upon the correct answer and suggestions in comments, I have created a demo which works absolutely fine and removes the handler callbacks after being used.

MainActivity.java

package com.mehuljoisar.d_pressbacktwicetoexit;

import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.widget.Toast;

public class MainActivity extends Activity {
	
	private static final long delay = 2000L;
	private boolean mRecentlyBackPressed = false;
	private Handler mExitHandler = new Handler();
	private Runnable mExitRunnable = new Runnable() {
		
		@Override
		public void run() {
			mRecentlyBackPressed=false;   
		}
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
	}
	
	@Override
	public void onBackPressed() {
		
		//You may also add condition if (doubleBackToExitPressedOnce || fragmentManager.getBackStackEntryCount() != 0) // in case of Fragment-based add
		if (mRecentlyBackPressed) {
			mExitHandler.removeCallbacks(mExitRunnable);
			mExitHandler = null;
            super.onBackPressed();
        }
		else
		{
	        mRecentlyBackPressed = true;
	        Toast.makeText(this, "press again to exit", Toast.LENGTH_SHORT).show();
	        mExitHandler.postDelayed(mExitRunnable, delay);
		}
	}

}

I hope it will be helpful !!

Solution 8 - Java

 public void onBackPressed() {
    if (doubleBackToExitPressedOnce) {
        super.onBackPressed();
        return;
    }

    this.doubleBackToExitPressedOnce = true;
    Toast.makeText(this, "Please click BACK again to exit", Toast.LENGTH_SHORT).show();

    new Handler().postDelayed(new Runnable() {

        @Override
        public void run() {
            doubleBackToExitPressedOnce=false;
        }
    }, 2000);

Declare Variable private boolean doubleBackToExitPressedOnce = false;

> Paste this in your Main Activity and this will solve your issue

Solution 9 - Java

It is not a good idea to use a Runnable when exit the application, I recently figure out a much simpler way to record and compare the period between two BACK button clicks. Sample code as following:

private static long back_pressed_time;
private static long PERIOD = 2000;

@Override
public void onBackPressed()
{
        if (back_pressed_time + PERIOD > System.currentTimeMillis()) super.onBackPressed();
        else Toast.makeText(getBaseContext(), "Press once again to exit!", Toast.LENGTH_SHORT).show();
        back_pressed_time = System.currentTimeMillis();
}

This will do the trick to exit the application by a double BACK button clicks within a certain delay period which is 2000 millisecond in sample.

Solution 10 - Java

The Accepted answer is Best one but if you are using Android Design Support Library then you can use SnackBar for Better Views.

   boolean doubleBackToExitPressedOnce = false;

    @Override
    public void onBackPressed() {
        if (doubleBackToExitPressedOnce) {
            super.onBackPressed();
            return;
        }

        this.doubleBackToExitPressedOnce = true;

        Snackbar.make(findViewById(R.id.photo_album_parent_view), "Please click BACK again to exit", Snackbar.LENGTH_SHORT).show();

        new Handler().postDelayed(new Runnable() {

            @Override
            public void run() {
                doubleBackToExitPressedOnce=false;
            }
        }, 2000);
    }

Solution 11 - Java

It's not a built in functionality. I think it is not even the recommended behavior. Android apps are not meant to exit:

https://stackoverflow.com/questions/2439978/why-dont-android-applications-provide-an-exit-option

Solution 12 - Java

Zefnus's answer using System.currentTimeMillis() is the best one (+1). The way I did it is not better than that, but still posting it to add to the above ideas.

If the toast is not visible when the back button is pressed, the toast is displayed, whereas, if it is visible (back has already been pressed once within the last Toast.LENGTH_SHORT time), then it exits.

exitToast = Toast.makeText(this, "Press again to exit",	Toast.LENGTH_SHORT);
.
.
@Override
public void onBackPressed() {
   if (exitToast.getView().getWindowToken() == null) //if toast is currently not visible
      exitToast.show();  //then show toast saying 'press againt to exit'
   else {                                            //if toast is visible then
      finish();                                      //or super.onBackPressed();
      exitToast.cancel();
   }
}

Solution 13 - Java

  1. Declare a global Toast variable for MainActivity Class. example: Toast exitToast;

  2. Initialize it in onCreate view method. example: exitToast = Toast.makeText(getApplicationContext(), "Press back again to exit", Toast.LENGTH_SHORT);

  3. Finally create a onBackPressedMethod as Follows:

     @Override
     public void onBackPressed() {
    
         if (exitToast.getView().isShown()) {
             exitToast.cancel();
             finish();
         } else {
             exitToast.show();
         }
     }
    

This works correctly, i have tested. and I think this is much simpler.

Solution 14 - Java

I know this is a very old question, but this is the easiest way to do what you want.

@Override
public void onBackPressed() {
   ++k; //initialise k when you first start your activity.
   if(k==1){
      //do whatever you want to do on first click for example:
      Toast.makeText(this, "Press back one more time to exit", Toast.LENGTH_LONG).show();
   }else{
      //do whatever you want to do on the click after the first for example:
      finish(); 
   }
}

I know this isn't the best method, but it works fine!

Solution 15 - Java

Recently, I needed to implement this back button feature in an app of mine. The answers on the original question were useful, but I had to take two more points into consideration:

  1. At some points in time, the back button is disabled
  2. The main activity is using fragments in combination with a back stack

Based on the answers and comments, I created the following code:

private static final long BACK_PRESS_DELAY = 1000;

private boolean mBackPressCancelled = false;
private long mBackPressTimestamp;
private Toast mBackPressToast;

@Override
public void onBackPressed() {
    // Do nothing if the back button is disabled.
    if (!mBackPressCancelled) {
        // Pop fragment if the back stack is not empty.
        if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
            super.onBackPressed();
        } else {
            if (mBackPressToast != null) {
                mBackPressToast.cancel();
            }

            long currentTimestamp = System.currentTimeMillis();

            if (currentTimestamp < mBackPressTimestamp + BACK_PRESS_DELAY) {
                super.onBackPressed();
            } else {
                mBackPressTimestamp = currentTimestamp;

                mBackPressToast = Toast.makeText(this, getString(R.string.warning_exit), Toast.LENGTH_SHORT);
                mBackPressToast.show();
            }
        }
    }
}

The code above assumes that the support library is used. If you use fragments but not the support library, you want to replace getSupportFragmentManager() by getFragmentManager().

Remove the first if, if the back button is never cancelled. Remove the second if, if you don`t use fragments or a fragment back stack

Also, it is important to be aware that the method onBackPressed is supported since Android 2.0. Check this page for an elaborate description. To make the back press feature work on older versions as well, add the following method to your activity:

@Override
public boolean onKeyDown(int keyCode, KeyEvent event)  {
    if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.ECLAIR
            && keyCode == KeyEvent.KEYCODE_BACK
            && event.getRepeatCount() == 0) {
        // Take care of calling this method on earlier versions of
        // the platform where it doesn't exist.
        onBackPressed();
    }

    return super.onKeyDown(keyCode, event);
}

Solution 16 - Java

In java

private Boolean exit = false; 

if (exit) {
onBackPressed(); 
}

 @Override
public void onBackPressed() {
    if (exit) {
        finish(); // finish activity
    } else {
        Toast.makeText(this, "Press Back again to Exit.",
                Toast.LENGTH_SHORT).show();
        exit = true;
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                exit = false;
            }
        }, 3 * 1000);

    }
}
    

In kotlin

 private var exit = false

 if (exit) {
        onBackPressed()
         }

 override fun onBackPressed(){
           if (exit){
               finish() // finish activity
           }else{
            Toast.makeText(this, "Press Back again to Exit.",
                    Toast.LENGTH_SHORT).show()
            exit = true
            Handler().postDelayed({ exit = false }, 3 * 1000)

        }
    }

Solution 17 - Java

I would normally add a comment, but my reputation doesn't allow this. So here's my two cents:

In Kotlin you can use coroutines to delay the setting to false:

private var doubleBackPressed = false
private var toast : Toast ?= null

override fun onCreate(savedInstanceState: Bundle?) {
    toast = Toast.maketext(this, "Press back again to exit", Toast.LENGTH_SHORT)
}

override fun onBackPressed() {
    if (doubleBackPressed) {
        toast?.cancel()
        super.onBackPressed()
        return
    }
    this.doubleBackPressed = true
    toast?.show()
    GlobalScope.launch {
        delay(2000)
        doubleBackPressed = false
    }
}

You will have to import:

import kotlinx.coroutines.launch
import kotlinx.coroutines.delay
import kotlinx.coroutines.GlobalScope

Solution 18 - Java

For this purpose I have implemented the following function:

private long onRecentBackPressedTime;
@Override
public void onBackPressed() {
    if (System.currentTimeMillis() - onRecentBackPressedTime > 2000) {
       onRecentBackPressedTime = System.currentTimeMillis();
       Toast.makeText(this, "Please press BACK again to exit", Toast.LENGTH_SHORT).show();
       return;
     }
   super.onBackPressed();
}

Solution 19 - Java

A slightly better method than Zefnus I think. Call System.currentTimeMillis() just one time and omit return; :

long previousTime;

@Override
public void onBackPressed()
{
    if (2000 + previousTime > (previousTime = System.currentTimeMillis())) 
    { 
        super.onBackPressed();
    } else {
        Toast.makeText(getBaseContext(), "Tap back button in order to exit", Toast.LENGTH_SHORT).show();
    }
}

Solution 20 - Java

This also helps when you have previous stack activity stored in stack.

I have modified Sudheesh's answer

boolean doubleBackToExitPressedOnce = false;

@Override
public void onBackPressed() {
    if (doubleBackToExitPressedOnce) {
        //super.onBackPressed();

  Intent intent = new Intent(Intent.ACTION_MAIN);
                    intent.addCategory(Intent.CATEGORY_HOME);
                    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);//***Change Here***
                    startActivity(intent);
                    finish();
                    System.exit(0);
        return;
    }

    this.doubleBackToExitPressedOnce = true;
    Toast.makeText(this, "Please click BACK again to exit", Toast.LENGTH_SHORT).show();

    new Handler().postDelayed(new Runnable() {

        @Override
        public void run() {
            doubleBackToExitPressedOnce=false;                       
        }
    }, 2000);
} 

Solution 21 - Java

@Override public void onBackPressed() {
   Log.d("CDA", "onBackPressed Called");
   Intent intent = new Intent();
   intent.setAction(Intent.ACTION_MAIN);
   intent.addCategory(Intent.CATEGORY_HOME);

   startActivity(intent);
}

Solution 22 - Java

Here is the full working code. And also don't forget to remove the callbacks so that it don't cause a memory leak in the app. :)

private boolean backPressedOnce = false;
private Handler statusUpdateHandler;
private Runnable statusUpdateRunnable;

public void onBackPressed() {
        if (backPressedOnce) {
            finish();
        }

        backPressedOnce = true;
        final Toast toast = Toast.makeText(this, "Press again to exit", Toast.LENGTH_SHORT);
        toast.show();
        
        statusUpdateRunnable = new Runnable() {
            @Override
            public void run() {
                backPressedOnce = false;
                toast.cancel();  //Removes the toast after the exit.
            }
        };

        statusUpdateHandler.postDelayed(statusUpdateRunnable, 2000);
}

@Override
protected void onDestroy() {
    super.onDestroy();
    if (statusUpdateHandler != null) {
        statusUpdateHandler.removeCallbacks(statusUpdateRunnable);
    }
}

Solution 23 - Java

For the activity whose is having Navigation Drawer, Use the following code for OnBackPressed()

boolean doubleBackToExitPressedOnce = false;

@Override
    public void onBackPressed() {
        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START);
        } else {
            if (doubleBackToExitPressedOnce) {
                if (getFragmentManager().getBackStackEntryCount() ==0) {
                    finishAffinity();
                    System.exit(0);
                } else {
                    getFragmentManager().popBackStackImmediate();
                }
                return;
            }

            if (getFragmentManager().getBackStackEntryCount() ==0) {
                this.doubleBackToExitPressedOnce = true;
                Toast.makeText(this, "Please click BACK again to exit", Toast.LENGTH_SHORT).show();

                new Handler().postDelayed(new Runnable() {

                    @Override
                    public void run() {
                        doubleBackToExitPressedOnce = false;
                    }
                }, 2000);
            } else {
                getFragmentManager().popBackStackImmediate();
            }
        }
    }

Solution 24 - Java

Here, I have generalized write the code for N tap counts. The code is similarly written for the Enable Developer option in android device phone. Even you can use this to enable features while developer testing the app.

 private Handler tapHandler;
 private Runnable tapRunnable;
 private int mTapCount = 0;
 private int milSecDealy = 2000;

onCreate(){
 ...
tapHandler = new Handler(Looper.getMainLooper());

 }

Call askToExit() on backpress or logout option.

private void askToExit() {
   if (mTapCount >= 2) {
    releaseTapValues();
    /* ========= Exit = TRUE  =========  */
   }

   mTapCount++;
   validateTapCount();
  }


  /* Check with null to avoid create multiple instances of the runnable */
  private void validateTapCount() {
   if (tapRunnable == null) {
    tapRunnable = new Runnable() {
     @Override
     public void run() {
      releaseTapValues();
      /* ========= Exit = FALSE  =========  */
     }
    };
    tapHandler.postDelayed(tapRunnable, milSecDealy);
   }
  }

  private void releaseTapValues() {
   /* Relase the value  */
   if (tapHandler != null) {
    tapHandler.removeCallbacks(tapRunnable);
    tapRunnable = null; /* release the object */
    mTapCount = 0; /* release the value */
   }
  }


  @Override
  protected void onDestroy() {
   super.onDestroy();
   releaseTapValues();
  }

Solution 25 - Java

I use this

import android.app.Activity;
import android.support.annotation.StringRes;
import android.widget.Toast;

public class ExitApp {

    private static long lastClickTime;

    public static void now(Activity ctx, @StringRes int message) {
        now(ctx, ctx.getString(message), 2500);
    }

    public static void now(Activity ctx, @StringRes int message, long time) {
        now(ctx, ctx.getString(message), time);
    }

    public static void now(Activity ctx, String message, long time) {
        if (ctx != null && !message.isEmpty() && time != 0) {
            if (lastClickTime + time > System.currentTimeMillis()) {
                ctx.finish();
            } else {
                Toast.makeText(ctx, message, Toast.LENGTH_SHORT).show();
                lastClickTime = System.currentTimeMillis();
            }
        }
    }

}

use to in event onBackPressed

@Override
public void onBackPressed() {
   ExitApp.now(this,"Press again for close");
}

or ExitApp.now(this,R.string.double_back_pressed)

for change seconds need for close, specified miliseconds

ExitApp.now(this,R.string.double_back_pressed,5000)

Solution 26 - Java

When HomeActivity contain navigation drawer and double backPressed() funtion to exit app. (Don't forget to initilize global variable boolean doubleBackToExitPressedOnce = false;) new handler after 2 sec set doubleBackPressedOnce variable to false

@Override
public void onBackPressed() {
    DrawerLayout drawer = findViewById(R.id.drawer_layout);
    if (drawer.isDrawerOpen(GravityCompat.END)) {
        drawer.closeDrawer(GravityCompat.END);
    } else {
        if (doubleBackToExitPressedOnce) {
            super.onBackPressed();
            moveTaskToBack(true);
            return;
        } else {
            this.doubleBackToExitPressedOnce = true;
            Toast.makeText(this, "Please click BACK again to exit", Toast.LENGTH_SHORT).show();
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    doubleBackToExitPressedOnce = false;
                }
            }, 2000);
        }
    }
}

Solution 27 - Java

I think this is what you need, I mean when we want to show this toast, when there is only one activity in the stack and user pressing back from this last activity of the stack.

var exitOpened=false // Declare it globaly

and in onBackPressed method change it like:

override fun onBackPressed() {
        if (isTaskRoot && !exitOpened)
        {
            exitOpened=true
            toast("Please press back again to exit")
            return
        }
        super.onBackPressed()
    }

Here, isTaskRoot will return true if the current activity is the root activity(first activity) of the stack and false if it is not.

You can check the official documentation here

Solution 28 - Java

boolean doubleBackToExitPressedOnce = false;

@Override
public void onBackPressed() {
    if (doubleBackToExitPressedOnce) {
        super.onBackPressed();
        return;
    }

    this.doubleBackToExitPressedOnce = true;

    Snackbar.make(findViewById(R.id.photo_album_parent_view), "Please click BACK again to exit", Snackbar.LENGTH_SHORT).show();

    new Handler().postDelayed(new Runnable() {

        @Override
        public void run() {
            doubleBackToExitPressedOnce=false;
        }
    }, 2000);
}

Solution 29 - Java

Some Improvements in Sudheesh B Nair's answer, i have noticed it will wait for handler even while pressing back twice immediately, so cancel handler as shown below. I have cancled toast also to prevent it to display after app exit.

 boolean doubleBackToExitPressedOnce = false;
        Handler myHandler;
        Runnable myRunnable;
        Toast myToast;
    
    @Override
        public void onBackPressed() {
            if (doubleBackToExitPressedOnce) {
                myHandler.removeCallbacks(myRunnable);
                myToast.cancel();
                super.onBackPressed();
                return;
            }
    
            this.doubleBackToExitPressedOnce = true;
            myToast = Toast.makeText(this, "Please click BACK again to exit", Toast.LENGTH_SHORT);
            myToast.show();
    
            myHandler = new Handler();
    
            myRunnable = new Runnable() {
    
                @Override
                public void run() {
                    doubleBackToExitPressedOnce = false;
                }
            };
            myHandler.postDelayed(myRunnable, 2000);
        }

Solution 30 - Java

This is the same of the accepted and most voted response but this snipped used Snackbar instead of Toast.

boolean doubleBackToExitPressedOnce = false;

    @Override
    public void onBackPressed() {
        if (doubleBackToExitPressedOnce) {
            super.onBackPressed();
            return;
        }

        this.doubleBackToExitPressedOnce = true;
        Snackbar.make(content, "Please click BACK again to exit", Snackbar.LENGTH_SHORT)
                .setAction("Action", null).show();


        new Handler().postDelayed(new Runnable() {

            @Override
            public void run() {
                doubleBackToExitPressedOnce=false;
            }
        }, 2000);
    }

Solution 31 - Java

In my case, I've depend on Snackbar#isShown() for better UX.

private Snackbar exitSnackBar;

@Override
public void onBackPressed() {
    if (isNavDrawerOpen()) {
        closeNavDrawer();
    } else if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
        if (exitSnackBar != null && exitSnackBar.isShown()) {
            super.onBackPressed();
        } else {
            exitSnackBar = Snackbar.make(
                    binding.getRoot(),
                    R.string.navigation_exit,
                    2000
            );
            exitSnackBar.show();
        }
    } else {
        super.onBackPressed();
    }
}

Solution 32 - Java

In this situation, Snackbar is the better option then Toast to display the quit action. Here is the method with snackbar that works.

@Override
        public void onBackPressed() {
            if (doubleBackToExitPressedOnce) {
                super.onBackPressed();
                return;
            }
            this.doubleBackToExitPressedOnce = true;
            Snackbar.make(this.getWindow().getDecorView().findViewById(android.R.id.content), "Please click BACK again to exit", Snackbar.LENGTH_SHORT).show();

            new Handler().postDelayed(new Runnable() {

                @Override
                public void run() {
                    doubleBackToExitPressedOnce=false;
                }
            }, 2000);
        }

Solution 33 - Java

Here is another way... using the CountDownTimer method

private boolean exit = false;
@Override
public void onBackPressed() {
        if (exit) {
            finish();
        } else {
            Toast.makeText(this, "Press back again to exit",
                    Toast.LENGTH_SHORT).show();
            exit = true;
            new CountDownTimer(3000,1000) {

                @Override
                public void onTick(long l) {

                }

                @Override
                public void onFinish() {
                    exit = false;
                }
            }.start();
        }

    }

Solution 34 - Java

In Kotlin on the back press to exit app you can use:

Define a global variable:

private var doubleBackToExitPressedOnce = false

Override onBackPressed:

override fun onBackPressed() {
        if (doubleBackToExitPressedOnce) {
            super.onBackPressed()
            return
        }

        doubleBackToExitPressedOnce = true
        Toast.makeText(this, "Please click BACK again to exit", Toast.LENGTH_LONG).show()

        Handler().postDelayed({
            doubleBackToExitPressedOnce = false;
        }, 2000)
    }

Solution 35 - Java

Best Solution with Toast

> In Java

private Toast exitToast;

@Override
public void onBackPressed() {
    if (exitToast == null || exitToast.getView() == null || exitToast.getView().getWindowToken() == null) {
        exitToast = Toast.makeText(this, "Press again to exit", Toast.LENGTH_LONG);
        exitToast.show();
    } else {
        exitToast.cancel();
        super.onBackPressed();
    }
}

> In Kotlin

private var exitToast: Toast? = null

override fun onBackPressed() {
    if (exitToast == null || exitToast!!.view == null || exitToast!!.view.windowToken == null) {
        exitToast = Toast.makeText(this, "Press again to exit", Toast.LENGTH_LONG)
        exitToast!!.show()
    } else {
        exitToast!!.cancel()
        super.onBackPressed()
    }
}

Solution 36 - Java

The unique thing about this solution is the behaviour; in that a non double tap will show the toast and a successful double tap will show no toast while closing the app. The only disadvantage being the the showing of the toast will have a 650 millisecond delay. I believe this is the best solution with the best behaviour as logic dictates that it would be impossible to have this behaviour without such a delay

//App Closing Vars
private var doubleBackPressedInterval: Long = 650
private var doubleTap = false
private var pressCount = 0
private var timeLimit: Long = 0

override fun onBackPressed() {
    pressCount++
    if(pressCount == 1) {
        timeLimit = System.currentTimeMillis() + doubleBackPressedInterval
        if(!doubleTap) {
            showExitInstructions()
        }
    }
    if(pressCount == 2) {
        if(timeLimit > System.currentTimeMillis()) {
            doubleTap = true
            super.onBackPressed()
        }
        else {
            showExitInstructions()
        }
        pressCount = 1
        timeLimit = System.currentTimeMillis() + doubleBackPressedInterval
    }
}

private fun showExitInstructions() {
    Handler().postDelayed({
        if(!doubleTap) {
            Toast.makeText(this, "Try Agian", Toast.LENGTH_SHORT).show()
        }
    }, doubleBackPressedInterval)
}

Solution 37 - Java

This answer is easy to use but we need to double-tap to exit. I just modify the answer ,

    @Override
public void onBackPressed() {
    ++k;
    if(k==1){
        Toast.makeText(this, "Press back one more time to exit", Toast.LENGTH_SHORT).show();
        new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
            @Override
            public void run() {
                --k;
            }
        },1000);
    }else{
        //do whatever you want to do on the click after the first for example:
        finishAffinity();
    }
}

Solution 38 - Java

After having had to implement the same things many times, decided its about time someone built a simple to use library. And that is DoubleBackPress Android library. The README explains all the APIs provided along with Examples (like ToastDisplay + Exit Activity), but a short rundown of the steps here.


To get going, start by adding the dependency to your application :

dependencies {
    implementation 'com.github.kaushikthedeveloper:double-back-press:0.0.1'
} 

Next, create a DoubleBackPress object in your Activity which provides the required behaviour.

DoubleBackPress doubleBackPress = new DoubleBackPress();
doubleBackPress.setDoublePressDuration(3000);           // msec

Then create a Toast that needs to be shown upon the First Back Press. Here, you can create your own Toast, or go with Standard Toast provided in the library. Doing so here by the later option.

FirstBackPressAction firstBackPressAction = new ToastDisplay().standard(this);
doubleBackPress.setFirstBackPressAction(firstBackPressAction);   // set the action

Now, define what should happen when your Second Back Press happens. Here, we are closing the Activity.

DoubleBackPressAction doubleBackPressAction = new DoubleBackPressAction() {
    @Override
    public void actionCall() {
        finish();
        System.exit(0);
    }
};

Finally, override your back press behaviour with the DoubleBackPress behaviour.

@Override
public void onBackPressed() {
    doubleBackPress.onBackPressed();
}

Example GIF of similar behavioural requirements

Solution 39 - Java

 private static final int TIME_DELAY = 2000;
    private static long back_pressed;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    @Override
    public void onBackPressed() {
        if (back_pressed + TIME_DELAY > System.currentTimeMillis()) {
            super.onBackPressed();
        } else {
            Toast.makeText(getBaseContext(), "Press once again to exit!",
                    Toast.LENGTH_SHORT).show();
        }
        back_pressed = System.currentTimeMillis();
    }

Solution 40 - Java

you can even make it more simple, and without using a hander, only do this =)

Long firstClick = 1L;
Long secondClick = 0L;

@Override
public void onBackPressed() {
secondClick = System.currentTimeMillis();
    if ((secondClick - firstClick) / 1000 < 2) {
          super.onBackPressed();
    } else {
          firstClick = System.currentTimeMillis();
          Toast.makeText(MainActivity.this, "click BACK again to exit", Toast.LENGTH_SHORT).show();
        }
 }

Solution 41 - Java

You can also use the visibility of a Toast, so you don't need that Handler/postDelayed ultra solution.

Toast doubleBackButtonToast;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    doubleBackButtonToast = Toast.makeText(this, "Double tap back to exit.", Toast.LENGTH_SHORT);
}

@Override
public void onBackPressed() {
    if (doubleBackButtonToast.getView().isShown()) {
        super.onBackPressed();
    }

    doubleBackButtonToast.show();
}

Solution 42 - Java

private static final int TIME_INTERVAL = 2000;
private long mBackPressed;
    @Override
    	public void onBackPressed() {
    
    		if (mBackPressed + TIME_INTERVAL > System.currentTimeMillis()) {
    			super.onBackPressed();
    			Intent intent = new Intent(FirstpageActivity.this,
    					HomepageActivity.class);
    			startActivity(intent);
    			finish();
    
    			return;
    		} else {
    		
    			Toast.makeText(getBaseContext(),
    					"Tap back button twice  to go Home.", Toast.LENGTH_SHORT)
    					.show();
    
    			mBackPressed = System.currentTimeMillis();
    
    		}
    
    	}

Solution 43 - Java

Here's my take on this :

 int oddeven = 0;
 long backBtnPressed1;
 long backBtnPressed2;
 @Override
 public void onBackPressed() {
     oddeven++;
     if(oddeven%2==0){
         backBtnPressed2 = System.currentTimeMillis();
         if(backBtnPressed2-backBtnPressed1<2000) {
            super.onBackPressed();
            return;
         }
     }
     else if(oddeven%2==1) { 
         backBtnPressed1 = System.currentTimeMillis();    
        //  Insert toast back button here
     }
 }

Solution 44 - Java

Here is a way to do it using RxJava:

override fun onCreate(...) {
    backPresses.timeInterval(TimeUnit.MILLISECONDS, Schedulers.io())
            .skip(1) //Skip initial event; delay will be 0.
            .onMain()
            .subscribe {
                if (it.time() < 7000) super.onBackPressed() //7000 is the duration of a Toast with length LENGTH_LONG.
            }.addTo(compositeDisposable)

    backPresses.throttleFirst(7000, TimeUnit.MILLISECONDS, Schedulers.io())
            .subscribe { Toast.makeText(this, "Press back again to exit.", LENGTH_LONG).show() }
            .addTo(compositeDisposable)
}

override fun onBackPressed() = backPresses.onNext(Unit)

Solution 45 - Java

Back when Button pressed 2 times

public void click(View view){
    if (isBackActivated) {
        this.finish();
    }
    if (!isBackActivated) {
        isBackActivated = true;
        Toast.makeText(getApplicationContext(), "Again", Toast.LENGTH_SHORT).show();
        Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                isBackActivated = false;  // setting isBackActivated after 2 second
            }
        }, 2000);
    }

}

Solution 46 - Java

I've tried to create a utils class for this, so any activity or fragment can implement this to be simpler.

The code was written with Kotlin & has Java-interop as well.

I'm using coroutine to delay and reset the flag variable. But you can modify it to your needs.

Additional files: SafeToast.kt

lateinit var toast: Toast

fun Context.safeToast(msg: String, length: Int = Toast.LENGTH_LONG, action: (Context) -> Toast = default) {
    toast = SafeToast.makeText(this@safeToast, msg, length).apply {
        // do anything new here
        action(this@safeToast)
        show()
    }
}

fun Context.toastSpammable(msg: String) {
    cancel()
    safeToast(msg, Toast.LENGTH_SHORT)
}

fun Fragment.toastSpammable(msg: String) {
    cancel()
    requireContext().safeToast(msg, Toast.LENGTH_SHORT)
}

private val default: (Context) -> Toast = { it -> SafeToast.makeText(it, "", Toast.LENGTH_LONG) }

private fun cancel() {
    if (::toast.isInitialized) toast.cancel()
}

ActivityUtils.kt

@file:JvmMultifileClass
@file:JvmName("ActivityUtils")
package your.company.com

import android.app.Activity
import your.company.com.R
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch


private var backButtonPressedTwice = false

fun Activity.onBackPressedTwiceFinish() {
    onBackPressedTwiceFinish(getString(R.string.msg_back_pressed_to_exit), 2000)
}

fun Activity.onBackPressedTwiceFinish(@StringRes message: Int, time: Long) {
    onBackPressedTwiceFinish(getString(message), time)
}

fun Activity.onBackPressedTwiceFinish(message: String, time: Long) {
    if (backButtonPressedTwice) {
        onBackPressed()
    } else {
        backButtonPressedTwice = true
        toastSpammable(message)
        GlobalScope.launch {
            delay(time)
            backButtonPressedTwice = false
        }
    }
}

Usage in Kotlin

// ActivityA.kt
override fun onBackPressed() {
    onBackPressedTwiceFinish()
}

Usage in Java

@Override 
public void onBackPressed() {
    ActivityUtils.onBackPressedTwiceFinish()
}

This code was inspired by @webserveis here

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionGuillaumeView Question on Stackoverflow
Solution 1 - JavaSudheesh B NairView Answer on Stackoverflow
Solution 2 - JavaSaro TaÅŸciyanView Answer on Stackoverflow
Solution 3 - JavaGuillaumeView Answer on Stackoverflow
Solution 4 - JavaChintan RathodView Answer on Stackoverflow
Solution 5 - JavavHowView Answer on Stackoverflow
Solution 6 - JavaHugo PassosView Answer on Stackoverflow
Solution 7 - JavaMehul JoisarView Answer on Stackoverflow
Solution 8 - JavaRakshithView Answer on Stackoverflow
Solution 9 - JavaMax LeeView Answer on Stackoverflow
Solution 10 - JavaPratik ButaniView Answer on Stackoverflow
Solution 11 - JavaCanerView Answer on Stackoverflow
Solution 12 - JavaKartikView Answer on Stackoverflow
Solution 13 - JavaSuptoView Answer on Stackoverflow
Solution 14 - JavaChris Rohit BrendanView Answer on Stackoverflow
Solution 15 - JavaTomasz NguyenView Answer on Stackoverflow
Solution 16 - JavaPraveenView Answer on Stackoverflow
Solution 17 - JavaDannyRitiuView Answer on Stackoverflow
Solution 18 - JavaBu TiView Answer on Stackoverflow
Solution 19 - Javak8CView Answer on Stackoverflow
Solution 20 - JavaArpit PatelView Answer on Stackoverflow
Solution 21 - JavaSai AdityaView Answer on Stackoverflow
Solution 22 - JavaSaket KumarView Answer on Stackoverflow
Solution 23 - JavaPriya RajanView Answer on Stackoverflow
Solution 24 - JavaChintan KhetiyaView Answer on Stackoverflow
Solution 25 - JavaWebserveisView Answer on Stackoverflow
Solution 26 - JavaAbhishek SView Answer on Stackoverflow
Solution 27 - JavaSuraj VaishnavView Answer on Stackoverflow
Solution 28 - JavamaniixView Answer on Stackoverflow
Solution 29 - JavaDivyang PanchalView Answer on Stackoverflow
Solution 30 - JavaMobileOSView Answer on Stackoverflow
Solution 31 - JavaKasim RangwalaView Answer on Stackoverflow
Solution 32 - JavaMitkumar PatelView Answer on Stackoverflow
Solution 33 - JavaAnuj SainView Answer on Stackoverflow
Solution 34 - JavaShailendra MaddaView Answer on Stackoverflow
Solution 35 - JavaKarthik KompelliView Answer on Stackoverflow
Solution 36 - Javauser2288580View Answer on Stackoverflow
Solution 37 - JavaGanesh MBView Answer on Stackoverflow
Solution 38 - JavaKaushik NPView Answer on Stackoverflow
Solution 39 - JavaAvinashView Answer on Stackoverflow
Solution 40 - JavaThe DninoView Answer on Stackoverflow
Solution 41 - JavaHudson PereiraView Answer on Stackoverflow
Solution 42 - JavaUmesh MaharjanView Answer on Stackoverflow
Solution 43 - JavaR41NView Answer on Stackoverflow
Solution 44 - JavaurgentxView Answer on Stackoverflow
Solution 45 - JavaAnkush ShrivastavaView Answer on Stackoverflow
Solution 46 - JavamochadwiView Answer on Stackoverflow