如何以编程方式"重启"Android应用程序?

Stu*_*uck 211 android android-activity

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

用户只能使用应用程序的一个实例登录服务器(即不允许多个设备).如果另一个实例获得"登录"锁定,那么该用户的所有其他实例都必须删除其数据(工厂重置),以保持一致性.

可以强制获取锁定,因为用户可能会删除应用程序并重新安装它,这将导致不同的实例ID,并且用户将无法再释放锁定.因此可以强行锁定.

由于这种力的可能性,我们需要始终检查具有锁定的具体实例.这是(几乎)对服务器的每个请求完成的.服务器可能会发送"error-lock-id".如果检测到,则客户端应用程序必须删除所有内容.


那是用例.不执行问题:

我有一个ActivityA启动Login ActivityL或应用程序的主ActivityB,具体取决于sharedPrefs字段.在启动L或B后,它自行关闭,只有L或B运行.因此,在用户已登录的情况下,B现在正在运行.

B开始C. C调用startServiceIntentServiceD.导致这个堆栈:

(A)> B> C> D.

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

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

出厂重置后,我想重新启动应用程序(关闭所有活动),然后再次启动A,然后启动登录ActivityL并自行完成:

(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);
}
Run Code Online (Sandbox Code Playgroud)

这就是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() {
        // ...
    }
}
Run Code Online (Sandbox Code Playgroud)

现在的问题是,如果我使用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?
Run Code Online (Sandbox Code Playgroud)

我不能使用活动' Context,因为ServiceIntentD也可能是从后台任务调用的AlarmManager.

那么我怎么能解决这个活动堆栈变成(A)> L?

Ole*_*kin 274

您可以使用它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);
Run Code Online (Sandbox Code Playgroud)

  • 在4.3和4.4设备上(我已经测试过),这似乎会杀死当前的活动,然后在旧活动的顶部启动一个新活动.我是2个深度活动(主要 - > prefs).按回来带我到旧的应用程序,一个屏幕回来. (29认同)
  • 就我而言,System.exit(0)在事务回滚时不起作用.相反,我使用activity.finish(); 它工作得很好. (5认同)
  • @Qulin,伙计们!你不能认真!这个例子更像是方向而不是现实生活中的例子.您必须使用启动活动名称,意图ID和退出机制来修改此代码段.不要盲目复制粘贴它. (5认同)
  • 由于对后台活动的新限制,因此这在Android Q上不再起作用https://developer.android.com/preview/privacy/background-activity-starts (5认同)
  • 这对我来说非常合适!我刚刚使用了android.os.Process.killProcess(android.os.Process.myPid()); 通过System.exit(); (4认同)
  • 这个正确地重新启动应用程序,包括重新加载对我来说很重要的NDK库. (2认同)
  • 在执行此代码之前,您应该正确关闭活动堆栈OnActivityResult (2认同)
  • `activity.finishAffinity()`可能就是你在这里所做的事情 - 这将关闭与应用程序相关的所有活动,而不仅仅是当前的活动. (2认同)
  • @MarcoRighini 他们应该在删除旧功能的同时添加新功能。否则该平台将变得不太可用......我需要使用它作为另一个错误的解决方法。现在我少了一个选择。 (2认同)

mik*_*enz 97

你可以简单地打电话:

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);
}
Run Code Online (Sandbox Code Playgroud)

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");
        }
    }
Run Code Online (Sandbox Code Playgroud)

这也将重新初始化jni类和所有静态实例.

  • @blueware - 除了Ilya的解决方案不会重启该过程,因此静态数据或加载的NDK库将无法正确重新初始化. (3认同)
  • 这个解决方案很好,但即使您减少了 100 Millis,它也会延迟几秒钟直到重新启动您的应用程序。然而,Jack Wharton 的这个库 [ProcessPhoenix](https://github.com/JakeWharton/ProcessPhoenix) 做得更好更快,但不值得仅在应用程序内添加此功能的库。 (2认同)

TBi*_*iek 63

Jake Wharton最近发布了他的ProcessPhoenix库,它以可靠的方式完成了这项工作.你基本上只需要打电话:

ProcessPhoenix.triggerRebirth(context);
Run Code Online (Sandbox Code Playgroud)

库将自动完成调用活动,终止应用程序进程并重新启动默认应用程序活动.

  • @Shambhu 您必须将标签 `<category android:name="android.intent.category.DEFAULT" />` 添加到应用清单中的默认活动 <intent-filter> 中。 (4认同)
  • 这是迄今为止最好的解决方案。 (2认同)

and*_*dev 41

我稍微修改了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);
}
Run Code Online (Sandbox Code Playgroud)

  • 直接来自[docs](https://developer.android.com/reference/java/lang/System.html#exit%28int%29):"_ _ call_` System.exit(n)`_实际上等同于call:_`Runtime.getRuntime().exit(n)`".在内部,`System.exit()`只是转身并调用`Runtime.getRuntime().exit()`.关于一个或另一个没有什么"更好"(除非有人关心一个人做了多少打字或关于一个额外的方法调用层). (6认同)

Ily*_*man 36

IntentCompat.makeRestartActivityTask

这样做的新方法是使用IntentCompat.makeRestartActivityTask

创建一个可用于在其基本状态下重新启动应用程序任务的Intent.这类似于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);
Run Code Online (Sandbox Code Playgroud)

  • 这会重新启动任务,但它不会重新启动进程,甚至不会重新启动`Application`对象.因此,任何"静态"数据,在创建"应用程序"期间初始化的数据或jni类仍保持其当前状态,并且不会重新初始化. (5认同)
  • 不幸的是,[`IntentCompat.makeRestartActivityTask`现已弃用](https://developer.android.com/sdk/support_api_diff/26.0.0-alpha1/changes/android.support.v4.content.IntentCompat.html).如果您[检查源代码](https://android.googlesource.com/platform/frameworks/support/+/android-support-lib-19.1.0/v4/java/android/support/v4/content/IntentCompat .java#57),它就像简单地添加标志`Intent.FLAG_ACTIVITY_NEW_TASK |一样简单.Intent.FLAG_ACTIVITY_CLEAR_TASK`. (4认同)
  • @TedHopp哦我错过了那一部分.我添加了System.exit(0); 但我不确定100%它会起作用.我将在后面测试它 (2认同)
  • 不使用开源库的最佳解决方案。举起手来,感谢您提供此答案,+1 (2认同)
  • 有关完整的最新功能,请参阅下面的答案:/sf/answers/3279375851/ (2认同)

184*_*615 27

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

如果要观察此功能的效果,static请在活动中添加一个变量并按每个增量递增,例如按下按钮.如果退出应用程序活动然后再次调用应用程序,则此静态变量将保留其值.(如果确实退出了应用程序,则会为变量分配初始值.)

(而且我必须评论为什么我不想修复这个bug.这个库是几十年前编写的,从那时起就泄露了资源.管理层相信它总是有用的.提供修复而不是解决方法的成本 ......我想你应该已经明白了.)

现在,我怎样才能将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));
    }
}
Run Code Online (Sandbox Code Playgroud)

调用活动只执行代码MagicAppRestart.doRestart(this);,执行调用活动onPause(),然后重新创建进程.不要忘记在AndroidManifest.xml中提及此活动

这种方法的优点是没有延迟.

UPD:它在Android 2.x中运行,但在Android 4中有些变化.

  • 此解决方案为我关闭应用程序,但它不会重新启动.至少在Android 4.3上. (5认同)
  • 我用了activity.startActivity(i); System.exit(0); 天才解决方案 (3认同)

Mar*_*ers 23

我发现这适用于 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.
}
Run Code Online (Sandbox Code Playgroud)

这是在应用程序中的启动器活动具有以下内容时完成的:

<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>
Run Code Online (Sandbox Code Playgroud)

我看到评论声称需要一个 DEFAULT 类别,但我还没有发现这种情况。我已经确认我的应用程序中的 Application 对象是重新创建的,因此我相信该进程确实已被杀死并重新启动。

我使用它的唯一目的是在用户启用或禁用 Firebase Crashlytics 的崩溃报告后重新启动应用程序。根据他们的文档,必须重新启动应用程序(进程终止并重新创建)才能使更改生效。


yon*_*nCN 22

我的解决方案不会重新启动进程/应用程序.它只允许应用程序"重启"主页活动(并解除所有其他活动).它看起来像是重启用户,但过程是一样的.我认为在某些情况下人们想要达到这个效果,所以我就把它留在这里.

public void restart(){
    Intent intent = new Intent(this, YourHomeActivity.class);
    this.startActivity(intent);
    this.finishAffinity();
}
Run Code Online (Sandbox Code Playgroud)


Stu*_*uck 15

好的,我重构了我的应用程序,我不会自动完成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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

并在 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);
}
Run Code Online (Sandbox Code Playgroud)

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

  • 这将****不重启应用程序,只重新创建类.因此,类中的任何静态变量都将保留先前运行的值. (23认同)

Nir*_*ara 14

Intent i = getBaseContext().getPackageManager().getLaunchIntentForPackage( getBaseContext().getPackageName() );
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
Run Code Online (Sandbox Code Playgroud)

  • 这将****不重启应用程序,只重新创建类.因此,类中的任何静态变量都将保留先前运行的值. (24认同)

小智 8

唯一没有触发"您的应用程序意外关闭"的代码如下所示.它也是不弃用的代码,不需要外部库.它也不需要计时器.

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);
}
Run Code Online (Sandbox Code Playgroud)


Cho*_*ski 7

完全重启应用程序的最佳方法是重新启动它,而不仅仅是跳转到带有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);
}
Run Code Online (Sandbox Code Playgroud)

使用重启或启动的实例的appA程序appB:

forceRunApp(mContext, "com.example.myProject.appA");
Run Code Online (Sandbox Code Playgroud)

您可以检查应用是否正在运行:

 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;
}
Run Code Online (Sandbox Code Playgroud)

注意:我知道这个答案有点偏离主题,但它对某些人来说真的很有帮助.


Kum*_*anu 7

现在仍在工作

 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);
}
Run Code Online (Sandbox Code Playgroud)

  • 这将重新启动活动(和任务堆栈),但不会重新启动整个应用程序。具体来说,如果您有一个 Application 子类,或者您有带有状态数据的静态类,那么这些对象将不会被重新初始化。 (6认同)

min*_*lee 6

此代码通过了Android 8-13测试

public void restartApp() {
    Intent i = getBaseContext().getPackageManager().getLaunchIntentForPackage( getBaseContext().getPackageName() );  
    startActivity(Intent.makeRestartActivityTask(i.getComponent()));  
    Runtime.getRuntime().exit(0);
}
Run Code Online (Sandbox Code Playgroud)


Hus*_*ine 5

重新启动应用程序的最佳方法是使用,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);
}
Run Code Online (Sandbox Code Playgroud)

希望能帮助到你。

  • 这会结束当前任务中的所有活动,但不会重新启动进程,甚至不会重新创建 Application 对象。因此,任何静态数据、在应用程序创建期间初始化的数据或由 jni 类初始化的数据都保持其当前状态并且不会重新初始化。 (4认同)

Ped*_*iro 2

尝试使用FLAG_ACTIVITY_CLEAR_TASK