Как программно "перезапустить" приложение для Android?

Во-первых, я знаю, что на самом деле нельзя убивать/перезапускать приложение на Android. В моем случае я хочу factory - reset мое приложение в конкретном случае, когда сервер отправляет клиенту определенную информацию.

Пользователь может быть зарегистрирован на сервере только с ОДНОМ экземпляром приложения (т.е. несколько устройств не разрешены). Если другой экземпляр получает этот "logged-in" -lock, все остальные экземпляры этого пользователя должны удалить свои данные (factory - reset), чтобы поддерживать согласованность.

Можно принудительно получить блокировку, поскольку пользователь может удалить приложение и переустановить его, что приведет к другому идентификатору экземпляра, и пользователь больше не сможет освободить блокировку. Поэтому можно принудительно получить блокировку.

Из-за этой силовой возможности нам нужно всегда проверять конкретный пример, что он имеет блокировку. Это делается на (почти) каждом запросе на сервер. Сервер может послать "неверный-lock-id". Если это обнаружено, клиентское приложение должно удалить все.


Это был прецедент. Нет для вопроса реализации:

У меня есть Activity A, который запускает Login Activity L или главное приложение Activity B в зависимости от поля sharedPrefs. После запуска L или B он закрывается, так что работает только L или B. Таким образом, в том случае, если пользователь уже зарегистрирован в B, выполняется сейчас.

B запускает C. C вызывает startService для IntentService D. Это приводит к этому стеку:

(A) > B > C > D

Из метода onHandleIntent D событие отправляется на ResultReceiver R.

R теперь обрабатывает это событие, предоставляя пользователю диалог, в котором он может выбрать factory - reset приложение (удалить базу данных, sharedPrefs и т.д.).

После factory - reset я хочу перезапустить приложение (чтобы закрыть все действия) и снова запустить A, который затем запускает login Activity L и заканчивается:

(A) > L

Метод Dialog onClick выглядит следующим образом:

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

И что класс MyApp:

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() {
        // ...
    }
}

Теперь проблема заключается в том, что если я использую FLAG_ACTIVITY_NEW_TASK, действия B и C все еще работают. Если я нажму кнопку "Назад" в login Activity, я вижу C, но я хочу вернуться на главный экран.

Если я не устанавливаю FLAG_ACTIVITY_NEW_TASK, я получаю ошибку:

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?

Я не могу использовать "Действия" Context, потому что ServiceIntent D также может быть вызван из фоновой задачи, которая запускается AlarmManager.

Итак, как я могу решить это, чтобы стек активности стал (A) > L?

Ответ 1

Вы можете использовать PendingIntent, чтобы настроить запуск своей деятельности в будущем, а затем закройте приложение

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

Ответ 2

Вы можете просто позвонить:

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

Что используется в ProcessPhoenix библиотеке


В качестве альтернативы:

Здесь немного улучшена версия ответа @Oleg Koshkin.

Если вы действительно хотите перезапустить свою деятельность, в том числе убить текущий процесс, попробуйте выполнить код. Поместите его в HelperClass или где вам это нужно.

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

Это также приведет к повторной инициализации jni-классов и всех статических экземпляров.

Ответ 3

Джейк Уортон недавно опубликовал свою библиотеку ProcessPhoenix, которая делает это надежным способом. Вам в основном нужно только позвонить:

ProcessPhoenix.triggerRebirth(context);

Библиотека автоматически завершит вызывающую деятельность, уничтожит процесс приложения и перезапустит действие приложения по умолчанию после этого.

Ответ 4

IntentCompat.makeRestartActivityTask

Новый способ сделать это, используя IntentCompat.makeRestartActivityTask

Сделать намерение, которое можно использовать для повторного запуска приложения в его базовое состояние. Это похоже на makeMainActivity (ComponentName), но также устанавливает флаги Intent.FLAG_ACTIVITY_NEW_TASK и FLAG_ACTIVITY_CLEAR_TASK.

PackageManager packageManager = context.getPackageManager();
Intent intent = packageManager.getLaunchIntentForPackage(context.getPackageName());
ComponentName componentName = intent.getComponent();
Intent mainIntent = IntentCompat.makeRestartActivityTask(componentName);
context.startActivity(mainIntent);
System.exit(0);

Ответ 5

Есть действительно хороший трюк. Моя проблема заключалась в том, что некоторые действительно старые библиотеки С++ jni просочились в ресурсы. В какой-то момент он прекратил функционировать. Пользователь попытался выйти из приложения и запустить его снова - без результата, потому что завершение операции не совпадает с завершением (или убийством) процесса. (Кстати, пользователь может перейти в список запущенных приложений и остановить его там - это сработает, но пользователи просто не знают, как прекратить приложения.)

Если вы хотите наблюдать за эффектом этой функции, добавьте переменную static в свою активность и увеличите ее, скажем, нажатием кнопки. Если вы выйдете из активности приложения и затем снова запустите приложение, эта статическая переменная сохранит свое значение. (Если приложение действительно было завершено, переменной присваивается начальное значение.)

(И я должен прокомментировать, почему я не хотел исправлять ошибку. Библиотека была написана несколько десятилетий назад и с тех пор исчерпала ресурсы. Руководство полагает, что это всегда сработало. Стоимость предоставления исправления вместо обходного пути... Я думаю, вы поняли эту идею.)

Теперь, как я мог reset jni shared (a.k.a. dynamic,.so) библиотека перейти в исходное состояние? Я решил перезапустить приложение как новый процесс.

Фокус в том, что System.exit() закрывает текущую активность, а Android воссоздает приложение с меньшим количеством действий.

Итак, код:

/** This activity shows nothing; instead, it restarts the android process */
public class MagicAppRestart extends Activity {
    // Do not forget to add it to AndroidManifest.xml
    // <activity android:name="your.package.name.MagicAppRestart"/>
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        System.exit(0);
    }
    public static void doRestart(Activity anyActivity) {
        anyActivity.startActivity(new Intent(anyActivity.getApplicationContext(), MagicAppRestart.class));
    }
}

Вызывающая активность просто выполняет код MagicAppRestart.doRestart(this);, выполняется операция вызова onPause(), а затем процесс воссоздается. И не забудьте упомянуть об этой активности в AndroidManifest.xml

Преимущество этого метода в том, что задержек нет.

UPD: он работал в Android 2.x, но в Android 4 что-то изменилось.

Ответ 6

Хорошо, я отредактировал свое приложение, и я не закончу автоматически. Я позволяю этому всегда работать и заканчивать его через событие onActivityResult. Таким образом я могу использовать флаги FLAG_ACTIVITY_CLEAR_TOP + FLAG_ACTIVITY_NEW_TASK, чтобы получить то, что хочу:

public class A extends Activity {

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        finish();
    }

    protected void onResume() {
        super.onResume();
        // ...
        if (loggedIn) {
            startActivityForResult(new Intent(this, MainActivity.class), 0);
        } else {
            startActivityForResult(new Intent(this, LoginActivity.class), 0);
        }
    }
}

и в ResultReceiver

@Override
public void onClick(DialogInterface dialog, int which) {
    MyApp.factoryReset();
    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);
}

Спасибо в любом случае!

Ответ 7

Intent i = getBaseContext().getPackageManager().getLaunchIntentForPackage( getBaseContext().getPackageName() );
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);

Ответ 8

Я немного изменил ответ Ilya_Gazman на использование новых API (IntentCompat устарел, начиная API 26). Runtime.getRuntime(). Exit (0) кажется лучше, чем 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);
}

Ответ 9

Мое решение не перезапускает процесс/приложение. Это позволяет приложению "перезапускать" домашнюю деятельность (и увольнять все остальные действия). Он выглядит как перезапуск для пользователей, но процесс тот же. Я думаю, что в некоторых случаях люди хотят достичь этого эффекта, поэтому я просто оставляю его здесь FYI.

public void restart(){
    Intent intent = new Intent(this, YourHomeActivity.class);
    this.startActivity(intent);
    this.finishAffinity();
}

Ответ 10

Лучший способ полностью перезагрузить приложение - перезапустить его, а не просто перейти к активности с помощью FLAG_ACTIVITY_CLEAR_TOP и FLAG_ACTIVITY_NEW_TASK. Поэтому мое решение состоит в том, чтобы сделать это из вашего приложения или даже из другого приложения, единственным условием является знать имя пакета приложения (пример: "com.example.myProject" )

 public static void forceRunApp(Context context, String packageApp){
    Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(packageApp);
    launchIntent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS | Intent.FLAG_ACTIVITY_NEW_TASK);
    context.startActivity(launchIntent);
}

Пример перезапуска или запуска использования appA из appB:

forceRunApp(mContext, "com.example.myProject.appA");

Вы можете проверить, запущено ли приложение:

 public static boolean isAppRunning(Context context, String packageApp){
    ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    List<ActivityManager.RunningAppProcessInfo> procInfos = activityManager.getRunningAppProcesses();
    for (int i = 0; i < procInfos.size(); i++) {
        if (procInfos.get(i).processName.equals(packageApp)) {
           return true;
        }
    }
    return false;
}

Примечание. Я знаю, что этот ответ немного не по теме, но он может быть действительно полезен для кого-то.

Ответ 11

Единственный код, который не вызвал "неожиданное закрытие вашего приложения", выглядит следующим образом. Это также не устаревший код, который не требует внешней библиотеки. Он также не требует таймера.

public static void triggerRebirth(Context context, Class myClass) {
    Intent intent = new Intent(context, myClass);
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
    context.startActivity(intent);
    Runtime.getRuntime().exit(0);
}

Ответ 12

Попробуйте использовать FLAG_ACTIVITY_CLEAR_TASK

Ответ 13

Вот пример перезапуска приложения в общем виде с помощью PackageManager:

Intent i = getBaseContext().getPackageManager()
             .getLaunchIntentForPackage( getBaseContext().getPackageName() );
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);

Ответ 14

Непосредственно запустите начальный экран с помощью FLAG_ACTIVITY_CLEAR_TASK и FLAG_ACTIVITY_NEW_TASK.

Ответ 15

Мне пришлось добавить обработчик для задержки выхода:

 mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 200, mPendingIntent);
        final Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                Runtime.getRuntime().exit(0);
            }
        }, 100);

Ответ 16

Вы можете использовать startInstrumentation метод Activity. Вам нужно реализовать пустой Instrumentation и указать в манифесте. После этого вы можете вызвать этот метод для перезапуска приложения. Вот так:

try {           
    InstrumentationInfo info = getPackageManager().queryInstrumentation(getPackageName(), 0).get(0);
    ComponentName component = new ComponentName(this, Class.forName(info.name));
    startInstrumentation(component, null, null);
} catch (Throwable e) {             
    new RuntimeException("Failed restart with Instrumentation", e);
}

Я получаю имя класса Instrumentation динамически, но вы можете его жестко кодировать. Некоторым это нравится:

try {           
    startInstrumentation(new ComponentName(this, RebootInstrumentation.class), null, null); 
} catch (Throwable e) {             
    new RuntimeException("Failed restart with Instrumentation", e);
}

Вызовите startInstrumentation, чтобы перезагрузить приложение. Прочитайте описание этого метода. Но это может быть небезопасно, если вы играете, как убить приложение.

Ответ 17

Приложение, над которым я работаю, должно дать пользователю возможность выбирать фрагменты для отображения (фрагменты динамически изменяются во время выполнения). Лучшим решением для меня было полностью перезагрузить приложение.

Итак, я попробовал множество решений, и никто из них не работал у меня, но это:

final Intent mStartActivity = new Intent(SettingsActivity.this, Splash.class);
final int mPendingIntentId = 123456;
final PendingIntent mPendingIntent = PendingIntent.getActivity(SettingsActivity.this, mPendingIntentId, mStartActivity,
                    PendingIntent.FLAG_CANCEL_CURRENT);
final AlarmManager mgr = (AlarmManager) SettingsActivity.this.getSystemService(Context.ALARM_SERVICE);
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent);
this.finishAffinity(); //notice here
Runtime.getRuntime().exit(0); //notice here

В надежде, что это поможет кому-то еще!

Ответ 18

попробуйте следующее:

Intent intent = getPackageManager().getLaunchIntentForPackage(getPackageName());
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);

Ответ 19

Использование:

navigateUpTo(new Intent(this, MainActivity.class));

Он работает, начиная с уровня API 16 (4.1), я полагаю.

Ответ 20

Вы можете перезапустить свою текущую деятельность следующим образом:

Фрагмент:

activity?.recreate()

Деятельность:

recreate()