How do I programmatically "restart" an Android app?

Firstly, I know that one should not really kill/restart an application on Android. In my use case, I want to factory-reset my application in a specific case where a server sends a piece of specific information to the client.

The user can only be logged in on the server with ONE instance of the application (i.e. multiple devices are not allowed). If another instance gets that "logged-in"-lock then all other instances of that user have to delete their data (factory-reset), to maintain consistency.

It is possible to forcibly get the lock because the user might delete the app and reinstall it which would result in a different instance-id and the user would not be able to free the lock anymore. Therefore it is possible to forcibly get the lock.

Because of that force-possibility, we need to always check in a concrete instance that it has the lock. That is done on (almost) each request to the server. The server might send a "wrong-lock-id". If that is detected, the client application must delete everything.


That was the use-case.

I have an Activity A that starts the Login Activity L or the app's main Activity B depending on a sharedPrefs value. After starting L or B it closes itself so that only L or B is running. So in the case that the user is logged in already B is running now.

B starts C. C calls startService for the IntentService D. That results in this stack:

(A) > B > C > D

From the onHandleIntent method of D, an event is sent to a ResultReceiver R.

R now handles that event by providing the user a dialog where he can choose to factory-reset the application (delete the database, sharedPrefs, etc.)

After the factory-reset I want to restart the application (to close all activities) and only start A again which then launches the login Activity L and finishes itself:

(A) > L

The Dialog's onClick-method looks like this:

@Override
public void onClick(DialogInterface dialog, int which) {

    // Will call onCancelListener
    MyApplication.factoryReset(); // (Deletes the database, clears sharedPrefs, etc.)
    Intent i = new Intent(MyApp.getContext(), A.class);
    i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    MyApp.getContext().startActivity(i);
}

And that's the MyApp class:

public class MyApp extends Application {
    private static Context context;

    @Override
    public void onCreate() {
        super.onCreate();
        context = getApplicationContext();
    }

    public static Context getContext() {
        return context;
    }

    public static void factoryReset() {
        // ...
    }
}

The problem is if I use the FLAG_ACTIVITY_NEW_TASK the Activities B and C are still running. If I hit the back button on the login Activity I see C, but I want to go back to the home screen.

If I do not set the FLAG_ACTIVITY_NEW_TASK I get the error:

07-07 12:27:12.272: ERROR/AndroidRuntime(9512): android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity  context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?

I cannot use the Activities' Context, because the ServiceIntent D might also be called from a background task which is started by the AlarmManager.

So how could I solve this to the activity stack becoming (A) > L?


Solution 1:

You can use PendingIntent to setup launching your start activity in the future and then close your application

Intent mStartActivity = new Intent(context, StartActivity.class);
int mPendingIntentId = 123456;
PendingIntent mPendingIntent = PendingIntent.getActivity(context, mPendingIntentId,    mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager mgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent);
System.exit(0);

Solution 2:

You can simply call:

public static void triggerRebirth(Context context, Intent nextIntent) {
    Intent intent = new Intent(context, YourClass.class);
    intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
    intent.putExtra(KEY_RESTART_INTENT, nextIntent);
    context.startActivity(intent);
    if (context instanceof Activity) {
      ((Activity) context).finish();
    }

    Runtime.getRuntime().exit(0);
}

Which is used in the ProcessPhoenix library


As an alternative:

Here's a bit improved version of @Oleg Koshkin answer.

If you really want to restart your activity including a kill of the current process, try following code. Place it in a HelperClass or where you need it.

public static void doRestart(Context c) {
        try {
            //check if the context is given
            if (c != null) {
                //fetch the packagemanager so we can get the default launch activity 
                // (you can replace this intent with any other activity if you want
                PackageManager pm = c.getPackageManager();
                //check if we got the PackageManager
                if (pm != null) {
                    //create the intent with the default start activity for your application
                    Intent mStartActivity = pm.getLaunchIntentForPackage(
                            c.getPackageName()
                    );
                    if (mStartActivity != null) {
                        mStartActivity.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                        //create a pending intent so the application is restarted after System.exit(0) was called. 
                        // We use an AlarmManager to call this intent in 100ms
                        int mPendingIntentId = 223344;
                        PendingIntent mPendingIntent = PendingIntent
                                .getActivity(c, mPendingIntentId, mStartActivity,
                                        PendingIntent.FLAG_CANCEL_CURRENT);
                        AlarmManager mgr = (AlarmManager) c.getSystemService(Context.ALARM_SERVICE);
                        mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent);
                        //kill the application
                        System.exit(0);
                    } else {
                        Log.e(TAG, "Was not able to restart application, mStartActivity null");
                    }
                } else {
                    Log.e(TAG, "Was not able to restart application, PM null");
                }
            } else {
                Log.e(TAG, "Was not able to restart application, Context null");
            }
        } catch (Exception ex) {
            Log.e(TAG, "Was not able to restart application");
        }
    }

This will also reinitialize jni classes and all static instances.

Solution 3:

I have slightly modified Ilya_Gazman answer to use new APIs (IntentCompat is deprecated starting API 26). Runtime.getRuntime().exit(0) seems to be better than System.exit(0).

 public static void triggerRebirth(Context context) {
    PackageManager packageManager = context.getPackageManager();
    Intent intent = packageManager.getLaunchIntentForPackage(context.getPackageName());
    ComponentName componentName = intent.getComponent();
    Intent mainIntent = Intent.makeRestartActivityTask(componentName);
    context.startActivity(mainIntent);
    Runtime.getRuntime().exit(0);
}