首先,我知道在Android上不应该真正地关闭/重启应用程序。在我的用例中,我想在服务器向客户机发送一条特定信息的特定情况下对应用程序进行工厂重置。

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

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

由于这种强制的可能性,我们需要始终检查一个具体实例是否拥有锁。这是在(几乎)对服务器的每个请求上完成的。服务器可能会发送一个“错误的锁定id”。如果检测到这种情况,客户机应用程序必须删除所有内容。


这就是用例。

我有一个活动A,启动登录活动L或应用程序的主活动B,这取决于一个sharedPrefs值。在启动L或B之后,它关闭自己,以便只有L或B在运行。在用户已经登录的情况下,B正在运行。

B启动C, C调用startService为IntentService服务。

(a) > b > c > d

从D的onHandleIntent方法,一个事件被发送到ResultReceiver R。

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

在工厂重置后,我想重新启动应用程序(关闭所有活动),只启动A,然后启动登录活动L并完成自己:

(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仍在运行。如果我点击登录活动的返回按钮,我看到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?

我不能使用活动的上下文,因为ServiceIntent D也可能从一个由AlarmManager启动的后台任务中调用。

那么我如何解决这个问题,使活动堆栈变成(A) >l呢?


当前回答

在我的案例中,Mikepenz的备选答案需要一些改变。https://stackoverflow.com/a/22345538/12021422 主要归功于Mikepenz的答案,我可以修改它。

这是对我有用的即插即用静态功能。

只要传递应用程序的上下文,这个函数就会处理重启。

    public static void doRestart(Context c) {
        try {
            // check if the context is given
            if (c != null) {
                // fetch the package manager 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);
                        
                        c.getApplicationContext().startActivity(mStartActivity);
                        // kill the application
                        System.exit(0);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            Log.e("restart", "Could not Restart");
        }
    }

其他回答

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

谢谢!

完全重新启动应用程序的最好方法是重新启动它,而不仅仅是用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);
}

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

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

Intent i = getBaseContext().getPackageManager().getLaunchIntentForPackage( getBaseContext().getPackageName() );
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
val i = baseContext.packageManager.getLaunchIntentForPackage(baseContext.packageName)
i!!.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
startActivity(i)
finish()

你可以使用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);