Как программно "перезапустить" приложение Android?
Во-первых, я знаю, что не нужно действительно убивать / перезапускать приложение на Android. В моем случае использования я хочу сбросить заводские настройки моего приложения в конкретном случае, когда сервер отправляет конкретную информацию клиенту.
Пользователь может быть авторизован на сервере только с ОДНЫМ экземпляром приложения (т.е. несколько устройств не допускаются). Если другой экземпляр получает этот "вход в систему", тогда все остальные экземпляры этого пользователя должны удалить свои данные (сброс к заводским настройкам), чтобы сохранить согласованность.
Можно принудительно получить блокировку, потому что пользователь может удалить приложение и переустановить его, что приведет к другому идентификатору экземпляра, и пользователь больше не сможет снять блокировку. Поэтому можно принудительно получить блокировку.
Из-за этой форсированной возможности мы должны всегда проверять в конкретном случае, что он имеет блокировку. Это делается для (почти) каждого запроса к серверу. Сервер может отправить "неправильный идентификатор блокировки". Если это обнаружено, клиентское приложение должно удалить все.
Это был вариант использования. Нет для реализации-вопрос:
у меня есть Activity
А, который запускает Логин 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 обрабатывает это событие, предоставляя пользователю диалоговое окно, в котором он может выбрать заводскую настройку приложения (удалить базу данных, sharedPrefs и т. Д.).
После сброса к заводским настройкам я хочу перезапустить приложение (чтобы закрыть все действия) и только снова запустить A, который затем запускает логин Activity
Л и заканчивает себя:
(A)> L
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 все еще выполняются. Если я нажму кнопку "Назад" при входе в систему 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?
35 ответов
Ты можешь использовать 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);
Я немного изменил ответ 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);
}
Вы можете просто позвонить:
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 и все статические экземпляры.
Джейк Уортон недавно опубликовал свою библиотеку ProcessPhoenix, которая делает это надежным способом. Вам в основном нужно только позвонить:
ProcessPhoenix.triggerRebirth(context);
Библиотека автоматически завершит вызывающее действие, завершит процесс приложения и впоследствии перезапустит действие приложения по умолчанию.
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);
Мое решение не перезапускает процесс / приложение. Это только позволяет приложению "перезапустить" домашнюю активность (и отклонить все остальные действия). Это выглядит как перезапуск для пользователей, но процесс такой же. Я думаю, что в некоторых случаях люди хотят добиться этого эффекта, поэтому я просто оставляю это здесь, к вашему сведению.
public void restart(){
Intent intent = new Intent(this, YourHomeActivity.class);
this.startActivity(intent);
this.finishAffinity();
}
Единственный код, который не вызвал "Ваше приложение неожиданно закрылось", выглядит следующим образом. Это также не осуждаемый код, который не требует внешней библиотеки. Это также не требует таймера.
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);
}
Я обнаружил, что это работает с API 29 и более поздними версиями - с целью уничтожения и перезапуска приложения, как если бы пользователь запустил его, когда оно не было запущено.
public void restartApplication(final @NonNull Activity activity) {
// Systems at 29/Q and later don't allow relaunch, but System.exit(0) on
// all supported systems will relaunch ... but by killing the process, then
// restarting the process with the back stack intact. We must make sure that
// the launch activity is the only thing in the back stack before exiting.
final PackageManager pm = activity.getPackageManager();
final Intent intent = pm.getLaunchIntentForPackage(activity.getPackageName());
activity.finishAffinity(); // Finishes all activities.
activity.startActivity(intent); // Start the launch activity
System.exit(0); // System finishes and automatically relaunches us.
}
Это было сделано, когда активность запуска в приложении имеет следующее:
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
Я видел комментарии, в которых утверждалось, что необходима категория DEFAULT, но я не обнаружил, что это так. Я подтвердил, что объект Application в моем приложении воссоздан, поэтому я считаю, что процесс действительно был остановлен и перезапущен.
Единственная цель, для которой я использую это, - перезапустить приложение после того, как пользователь включил или отключил отчеты о сбоях для Firebase Crashlytics. Согласно их документам, приложение должно быть перезапущено (процесс завершен и создан заново), чтобы это изменение вступило в силу.
Есть действительно хороший трюк. Моя проблема заключалась в том, что в какой-то действительно старой C++ jni библиотеке произошла утечка ресурсов. В какой-то момент он перестал функционировать. Пользователь попытался выйти из приложения и запустить его снова - безрезультатно, потому что завершение действия - это не то же самое, что завершение (или уничтожение) процесса. (Кстати, пользователь может перейти к списку запущенных приложений и остановить его оттуда - это будет работать, но пользователи просто не знают, как завершать приложения.)
Если вы хотите наблюдать эффект от этой функции, добавьте static
изменяйте свою активность и увеличивайте ее, скажем, нажатием кнопки. Если вы выйдете из активности приложения, а затем снова вызовете приложение, эта статическая переменная сохранит свое значение. (Если приложение действительно было закрыто, переменной будет присвоено начальное значение.)
(И я должен прокомментировать, почему я не хотел вместо этого исправлять ошибку. Библиотека была написана десятилетия назад и с тех пор утечка ресурсов. Руководство считает, что это всегда работало. Стоимость предоставления исправления вместо обходного пути... Я думаю, вы поняли.)
Теперь, как я могу сбросить совместно используемую библиотеку jni (она же динамическая, .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 что-то изменилось.
Хорошо, я провела рефакторинг своего приложения, и я не закончу A автоматически. Я позволяю этому бежать всегда и заканчиваю это через 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);
}
Спасибо, в любом случае!
Intent i = getBaseContext().getPackageManager().getLaunchIntentForPackage( getBaseContext().getPackageName() );
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
Лучший способ полностью перезапустить приложение - это перезапустить его, а не просто перейти к активности с 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;
}
Примечание: я знаю, что этот ответ немного не в тему, но он может быть действительно полезным для кого-то.
Все еще работает сейчас
public void resetApplication() {
Intent resetApplicationIntent = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName());
if (resetApplicationIntent != null) {
resetApplicationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
}
context.startActivity(resetApplicationIntent);
((Activity) context).overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
}
Этот код прошел тест Android 8–13.
public void restartApp() {
Intent i = getBaseContext().getPackageManager().getLaunchIntentForPackage( getBaseContext().getPackageName() );
startActivity(Intent.makeRestartActivityTask(i.getComponent()));
Runtime.getRuntime().exit(0);
}
Мой лучший способ перезапустить приложение - это использовать finishAffinity();
Поскольку, finishAffinity();
может использоваться только в версиях JELLY BEAN, поэтому мы можем использовать ActivityCompat.finishAffinity(YourCurrentActivity.this);
для более низких версий.
Тогда используйте Intent
запустить первое действие, поэтому код будет выглядеть так:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
finishAffinity();
Intent intent = new Intent(getApplicationContext(), YourFirstActivity.class);
startActivity(intent);
} else {
ActivityCompat.finishAffinity(YourCurrentActivity.this);
Intent intent = new Intent(getApplicationContext(), YourFirstActivity.class);
startActivity(intent);
}
Надеюсь, поможет.
Вот пример для перезапуска вашего приложения в общем виде с помощью PackageManager:
Intent i = getBaseContext().getPackageManager()
.getLaunchIntentForPackage( getBaseContext().getPackageName() );
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
Попробуй это:
Intent intent = getPackageManager().getLaunchIntentForPackage(getPackageName());
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
Котлинская версия этого ответа:
val intent = Intent(this, YourActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
startActivity(intent)
Runtime.getRuntime().exit(0)
Для Котлина — издание 2023 г.
fun Context.restartApplication() {
val intent = packageManager.getLaunchIntentForPackage(packageName)
val componentName = intent?.component
val mainIntent = Intent.makeRestartActivityTask(componentName)
startActivity(mainIntent)
Runtime.getRuntime().exit(0)
}
Использование:
navigateUpTo(new Intent(this, MainActivity.class));
Это работает, начиная с уровня API 16 (4.1), я считаю.
Прямой запуск начального экрана с FLAG_ACTIVITY_CLEAR_TASK
а также FLAG_ACTIVITY_NEW_TASK
,
Мне пришлось добавить обработчик, чтобы задержать выход:
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);
fun triggerRestart(context: Activity) {
val intent = Intent(context, MainActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(intent)
if (context is Activity) {
(context as Activity).finish()
}
Runtime.getRuntime().exit(0)
}
Ты можешь использовать 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
перезагрузите ваше приложение. Прочитайте описание этого метода. Но это может быть небезопасно, если вы ведете себя как приложение для уничтожения.
Я получил этот путь.
Intent i = getBaseContext().getPackageManager().getLaunchIntentForPackage(
getBaseContext().getPackageName() );
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
finish();
Приложение, над которым я работаю, должно дать пользователю возможность выбирать, какие фрагменты отображать (фрагменты динамически изменяются во время выполнения). Лучшим решением для меня было полностью перезапустить приложение.
Поэтому я перепробовал множество решений, и ни одно из них мне не помогло, но вот:
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
Надеюсь, что это поможет кому-то еще!
val i = baseContext.packageManager.getLaunchIntentForPackage(baseContext.packageName)
i!!.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
startActivity(i)
finish()
Попробуй это:
private void restartApp() {
Intent intent = new Intent(getApplicationContext(), YourStarterActivity.class);
int mPendingIntentId = MAGICAL_NUMBER;
PendingIntent mPendingIntent = PendingIntent.getActivity(getApplicationContext(), mPendingIntentId, intent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager mgr = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent);
System.exit(0);
}
Применить отложенный перезапуск
startDelay Задержка запуска (в миллисекундах)
public static void reStartApp(Context context, long startDelay) {
//Obtain the startup Intent of the application with the package name of the application
Intent intent = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName());
PendingIntent restartIntent = PendingIntent.getActivity(context.getApplicationContext(), -1, intent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager mgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (mgr != null) {
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + startDelay, restartIntent);
}
}