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


231

首先,我知道不应真正终止/重启Android上的应用程序。在我的用例中,我想在服务器将特定信息发送到客户端的特定情况下,将应用程序恢复出厂设置。

用户只能使用一个应用程序实例登录到服务器上(即,不允许使用多个设备)。如果另一个实例获得了“登录”锁定,则该用户的所有其他实例都必须删除其数据(恢复出厂设置),以保持一致性。

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

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


那是用例。

我有一个ActivityA,它根据sharedPrefs值启动登录ActivityL或应用程序的主ActivityB。启动L或B后,它会自行关闭,因此只有L或B在运行。因此,在用户已登录的情况下,B现在正在运行。

B开始。C呼吁startServiceIntentServiceD.导致这个堆栈:

(A)> B> C> D

从D的onHandleIntent方法将事件发送到ResultReceiverR

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

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

(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,因为ServiceIntentD也可能从由发起的后台任务中调用AlarmManager

那么如何解决这个问题,使活动堆栈变为(A)> L?

Answers:


284

您可以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);

5
这对我来说很完美!我只是使用android.os.Process.killProcess(android.os.Process.myPid()); 通过System.exit();
FDIM

29
在4.3和4.4设备(我已经测试过)上,这似乎终止了当前活动,然后在旧活动之上启动了一个新活动。我深2项活动(主要->首选项)。按下可将我带回旧应用程序,一个屏幕。
Mgamerz 2014年

5
就我而言,由于事务回滚,System.exit(0)无法正常工作。相反,我使用了activity.finish();。而且效果很好。
统一

6
@Qulin,伙计们!你不能当真!这个例子比实际例子更像是方向。您必须使用开始活动名称,意图ID和退出机制来修改此代码段。请勿盲目复制粘贴。
Oleg Koshkin

19
这不会再与Android Q工作,由于新的限制,以背景活动developer.android.com/preview/privacy/...
马可Righini

103

您可以简单地致电:

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


1
这个解决方案很好,但是即使您减少了100 Millis,它也会延迟几秒钟直到重新启动您的应用程序。但是,杰克·沃顿(Jack Wharton)编写的库ProcessPhoenix可以更快更好地完成它,但是不值得在应用程序中仅为此功能添加库。
blueware's

@blueware我已经更新了答案,并添加了在ProcessPhonix内部使用的代码
mikepenz '16

@mikepenz,这个“ Ilya_Gazman”家伙的表现要好得多,而且无需使用这样的库。
blueware's

3
@blueware-除了Ilya的解决方案不会重新启动该过程之外,因此不会正确地重新初始化静态数据或已加载的NDK库。
泰德·霍普

AlarmManager如果使用此解决方案,则某些华为和三星设备会受到限制并且行为不当,还有更好的方法吗?
blueware's

69

杰克·沃顿(Jake Wharton)最近发布了他的ProcessPhoenix库,该库以可靠的方式进行。您基本上只需致电:

ProcessPhoenix.triggerRebirth(context);

该库将自动完成调用活动,终止应用程序进程,然后再重新启动默认的应用程序活动。


这似乎可行,但出现崩溃(得到报告)。不确定这是否理想。
BK-

1
我从来没有与任何的lib的问题,但随时报告bug github.com/JakeWharton/ProcessPhoenix/issues
TBieniek

抱歉,由于我没有足够仔细地查看邮件,因此我撤回了评论。我缺少默认启动活动的意图过滤器。可能需要注意所需的确切意图过滤器。
BK-

1
到目前为止,这是最好的解决方案。
yongsunCN

1
@Shambhu,您必须将标签添加<category android:name="android.intent.category.DEFAULT" />到应用清单中的默认活动<intent-filter>。
穆罕默德·里法(Muhammed Refaat)'18年

57

我对Ilya_Gazman的答案进行了稍微修改,以使用新的API(从API 26开始不推荐使用IntentCompat)。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);
}

8
直接来自文档:“ 该调用 System.exit(n) 实际上等效于该调用: Runtime.getRuntime().exit(n) ”。在内部,System.exit()只需转身并致电Runtime.getRuntime().exit()。彼此之间没有“更好”的关系(除非人们担心一种类型的执行量或方法调用的额外一层)。
Ted Hopp

何时何地调用上述方法?
马文

1
@Makvin,您决定在哪里调用它。我的情况是更改语言后重新启动应用程序。
android_dev '18

@TedHopp对每个答案都说“不好”,您有可行的解决方案吗?不用讽刺,确实需要重新创建应用程序而不会留下任何痕迹。从静态变量到类实例。
Farid

1
@FARID-任何涉及调用Runtime.getRuntime().exit(0)(或System.exit(0))的解决方案都可能有效。我的一些“不好”评论是针对答案的(例如,Ilya Gazman对此进行了编辑,以纳入这样的电话。)
Ted Hopp

37

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

6
这将重新启动任务,但不会重新启动进程,甚至不会重新启动Application对象。因此,任何static数据(在创建Application或jni类的过程中初始化的数据)均保持其当前状态,并且不会重新初始化。
Ted Hopp

2
@TedHopp哦,我错过了这一部分。我添加了System.exit(0); 但是我不确定100%是否会奏效。我将稍后在
Ilya Gazman

1
最好的解决方案,而无需使用开源库来完成。举起手来,谢谢你提供这个答案,+ 1
blueware's

4
不幸的是,IntentCompat.makeRestartActivityTask现在不推荐使用。如果您检查源代码,那么就像简单地添加标志一样简单Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK
Paul Lammertsma

IntentCompat.makeRestartActivityTask已删除
luckyhandler

28

有一个非常好的技巧。我的问题是某些真正古老的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中已经有所改变。


3
我用了activity.startActivity(i); System.exit(0); 天才解决方案
max4ever 2012年

5
此解决方案为我关闭了该应用程序,但没有重启。至少在Android 4.3上。
Kirill Rakhman 2013年

1
在三星银河超级android 4.2.2上,它导致重新启动的无限循环。因此,该应用程序将不会再次启动。
Gunhan

@Gunhan 1)如果替换System.exit(0)为会发生什么android.os.Process.killProcess(android.os.Process.myPid());?2)无限循环很可能意味着他们在重新启动应用程序时不会删除最顶层的活动。原则上,您可以添加一个静态布尔变量,在调用重新启动活动之前将其设置为true,在重新启动之后将其设置为false。因此,活动可以找出重新启动是否已经发生(如果已经发生,则只需完成())。太太,您的报告意味着该技巧并非在所有设备上都一样起作用。
18446744073709551615

@Gunham如果您正在启动导致重启的相同活动,则它将在任何设备上无限循环。
卢卡斯·哈纳塞克

23

我的解决方案不会重新启动进程/应用程序。它仅允许应用“重新启动”家庭活动(并关闭所有其他活动)。对用户来说,这看起来像是重新启动,但是过程是相同的。我认为在某些情况下,人们希望达到这种效果,因此我只供参考。

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

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

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

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


23
不会重新启动应用程序,仅重新创建类。因此,类中的任何静态变量都将保留先前运行的值。
布莱恩·怀特

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

24
不会重新启动应用程序,仅重新创建类。因此,类中的任何静态变量都将保留先前运行的值。
布莱恩·怀特

14

唯一未触发“您的应用程序意外关闭”的代码如下。它也是不需要使用的代码,不需要外部库。它还不需要计时器。

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

8

我发现这适用于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崩溃报告后重新启动应用程序。根据他们的文档,该应用程序必须重新启动(进程已终止并重新创建),该更改才能生效。


7

完全重新启动应用程序,最好的办法是重振它,不只是跳转到的活动与FLAG_ACTIVITY_CLEAR_TOPFLAG_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);
}

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

注意:我知道这个答案有点超出主题,但是对某些人真的很有帮助。


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

希望能帮助到你。


1
这将结束当前任务中的所有活动,但不会重新启动该过程,甚至不会重新创建Application对象。因此,任何静态数据(在应用程序的创建过程中初始化的数据或通过jni类初始化的数据)将保持其当前状态,并且不会重新初始化。
泰德·霍普


3

这是一个使用PackageManager以通用方式重启应用的示例:

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

这将重新启动任务,但不会重新启动进程,甚至不会重新启动Application对象。因此,任何静态数据,在创建Application或jni类期间初始化的数据均保持其当前状态,并且不会重新初始化。
泰德·霍普

3

试试这个:

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

1
就像这里的其他所有答案都已经暗示了同样的事情一样,这不会重新启动应用程序,只会重新创建类。因此,该过程中的任何静态数据都不会被重置。
泰德·霍普

2

使用FLAG_ACTIVITY_CLEAR_TASK和直接启动初始屏幕FLAG_ACTIVITY_NEW_TASK


2

我必须添加一个处理程序以延迟退出:

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

2

用:

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

我认为它可以从API级别16(4.1)开始工作。


1

您可以使用的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您的应用的重新加载。阅读此方法的说明。但是,如果像kill app那样行事可能并不安全。


1

我正在使用的应用程序必须使用户能够选择要显示的片段(片段在运行时动态更改)。对我来说最好的解决方案是完全重新启动应用程序。

因此,我尝试了很多解决方案,但没有一个对我有用,但这是:

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

希望对别人有帮助!


0

试试这个:

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


-3

您可以像这样重新启动当前活动:

片段:

activity?.recreate()

活动内容:

recreate()

3
这根本不是OP想要做的。
泰德·霍普
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.