如何以编程方式“重新启动”Android应用程序?

首先,我知道不应该在Android上杀死/重启应用程序。 在我的使用案例中,我希望在特定的情况下,在服务器向客户端发送特定信息的情况下,重置我的应用程序。

用户只能使用应用程序的一个实例在服务器上login(即不允许多个设备)。 如果另一个实例获取“login”锁,那么该用户的所有其他实例都必须删除其数据(出厂重置),以保持一致性。

有可能强行获取锁,因为用户可能会删除应用程序并重新安装,这将导致不同的实例id,用户将无法再解锁。 因此有可能强行取得locking。

由于这种可能性,我们需要经常检查一个具体实例是否有锁。 这是(几乎)对服务器的每个请求完成的。 服务器可能会发送一个“错误的lockingID”。 如果检测到,客户端应用程序必须删除所有内容。


这是用例。 否,执行问题:

我有一个Activity A,根据sharedPrefs字段启动loginActivity L或应用程序的主要Activity B. 启动L或B后,自行closures,只有L或B正在运行。 所以在用户已经login的情况下,B现在正在运行。

B启动C. C调用IntentService startService D.导致这个堆栈:

(A)> B> C> D

从D的onHandleIntent方法将事件发送到ResultReceiver R.

R现在通过为用户提供一个对话框来处理这个事件,在这个对话框中他可以select在工厂重置应用程序(删除数据库,sharedPrefs等)

出厂重置后,我想重新启动应用程序(closures所有活动),只需重新启动A,然后启动loginActivity 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仍在运行。 如果我点击loginActivity上的后退button,我会看到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启动的后台任务AlarmManager

那么我怎样才能解决这个问题呢?(A)> L?

您可以使用PendingIntent设置将来启动您的开始活动,然后closures您的应用程序

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

你可以简单地调用:

 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类和所有静态实例。

杰克·沃顿(Jake Wharton)最近发布了他的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); 

有一个非常好的技巧。 我的问题是,一些非常古老的C ++ jni库泄露了资源。 在某个时候,它停止运作。 用户试图退出应用程序并重新启动它 – 没有任何结果,因为完成某个活动与完成(或终止)该过程并不相同。 (顺便说一句,用户可以到正在运行的应用程序的列表并停止它 – 这是可行的,但用户只是不知道如何终止应用程序。)

如果你想观察这个特性的效果,可以给你的活动添加一个staticvariables,然后每增加一个,比如按下button。 如果您退出应用程序活动,然后再次调用应用程序,则此静态variables将保留其值。 (如果应用程序确实退出,variables将被赋予初始值。)

(而且我必须评论为什么我不想修复这个bug,而这个库是几十年前写的,从此泄漏了资源,pipe理层认为它一直在运行 ,提供修复的代价而不是解决方法。我想,你明白了。)

现在,我怎么能重新设置jni共享(akadynamic,.so)库到初始状态? 我select重新启动应用程序作为一个新的过程。

诀窍是System.exit()closures了当前的活动,Android重新创build了一个活动less的应用程序。

所以代码是:

 /** 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); ,调用activity的onPause()被执行,然后进程被重新创build。 别忘了在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); } 

不pipe怎么说,还是要谢谢你!

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

我的解决scheme不会重新启动进程/应用程序。 它只允许应用程序“重新启动”家庭活动(并closures所有其他活动)。 它看起来像是重新启动给用户,但是过程是一样的。 我认为在某些情况下,人们想要达到这个效果,所以我只是把它留在这里。

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

完全重新启动应用程序的最佳方式是重新启动应用程序,而不是跳转到具有FLAG_ACTIVITY_CLEAR_TOPFLAG_ACTIVITY_NEW_TASK的活动。 所以我的解决scheme是从您的应用程序,甚至从另一个应用程序,唯一的条件是知道应用程序包的名称(例如:' 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); } 

使用重新启动或从appB启动appA示例

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

注意 :我知道这个答案是有点脱离主题,但它可以真正有帮助的人。

尝试使用FLAG_ACTIVITY_CLEAR_TASK

以下是使用PackageManager以通用方式重新启动应用程序的示例:

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

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

我稍微修改了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); } 

你可以使用Activity startInstrumentation方法。 您需要实现空的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); } 

我dynamic获取Instrumentation类的名称,但可以对其进行硬编码。 有些像这样:

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

调用startInstrumentation重新加载您的应用程序。 阅读这个方法的描述。 但是,如果像杀应用程序一样行事可能是不安全的。

我正在处理的应用程序必须让用户select要显示哪些片段(在运行时dynamic更改片段)。 对我来说最好的解决scheme是完全重启应用程序。

所以我尝试了很多解决scheme,而且没有一个为我工作,但是这个:

 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 

希望这会帮助别人!

使用:

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

我相信它从API级别16(4.1)开始。

尝试这个:

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