Android 앱을 프로그래밍 방식으로 “다시 시작”하는 방법은 무엇입니까? 다시 설치하여 다른 인스턴스

첫째, 안드로이드에서 실제로 응용 프로그램을 죽이거나 다시 시작해서는 안된다는 것을 알고 있습니다. 유스 케이스에서 서버가 클라이언트에 특정 정보를 보내는 특정 경우 애플리케이션을 공장 초기화합니다.

사용자는 하나의 응용 프로그램 인스턴스가있는 서버에서만 로그인 할 수 있습니다 (즉, 여러 장치는 허용되지 않음). 다른 인스턴스가 해당 “로그인 된”잠금을 받으면 일관성을 유지하기 위해 해당 사용자의 다른 모든 인스턴스가 데이터를 삭제 (공장 재설정)해야합니다.

사용자가 앱을 삭제하고 다시 설치하여 다른 인스턴스 ID를 초래하고 더 이상 잠금을 해제 할 수 없기 때문에 강제로 잠금을 얻을 수 있습니다. 따라서 강제로 잠금을 얻을 수 있습니다.

그 힘 가능성 때문에 우리는 항상 잠금이있는 구체적인 인스턴스를 확인해야합니다. 서버에 대한 거의 모든 요청에 ​​대해 수행됩니다. 서버가 “잘못된 잠금 ID”를 보낼 수 있습니다. 이것이 감지되면 클라이언트 애플리케이션이 모든 것을 삭제해야합니다.


그것은 유스 케이스였습니다.

sharedPrefs 값에 따라 Activity로그인 ActivityL 을 시작 하는 A 또는 앱의 기본 ActivityB가 있습니다. L 또는 B를 시작한 후에는 L 또는 B 만 실행되도록 자체 종료됩니다. 따라서 사용자가 이미 로그인 한 경우 B가 현재 실행 중입니다.

B는 C를 시작한다. C startServiceIntentServiceD를 요구한다 .

(A)> B> C> D

D의 onHandleIntent 메소드에서 이벤트가 ResultReceiver R로 전송됩니다 .

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가 여전히 실행 중이라는 것입니다. 로그인시 뒤로 버튼을 누르면 ActivityC가 표시되지만 홈 화면으로 돌아가고 싶습니다.

설정하지 않으면 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?

D는 또한에 의해 시작된 백그라운드 작업에서 호출 될 수 Context있기 때문에 Activities ‘를 사용할 수 없습니다 .ServiceIntentAlarmManager

어떻게 이것을 (A)> L이되는 액티비티 스택으로 해결할 수 있습니까?



답변

당신이 사용할 수있는 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);


답변

간단하게 전화를 걸 수 있습니다.

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

라이브러리는 자동으로 호출 활동을 종료하고 응용 프로그램 프로세스를 종료 한 후 기본 응용 프로그램 활동을 다시 시작합니다.


답변

새로운 API를 사용하도록 Ilya_Gazman 답변을 약간 수정했습니다 (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);
}


답변

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 라이브러리가 리소스를 유출한다는 것입니다. 어느 시점에서, 그것은 작동을 멈췄습니다. 활동 종료는 프로세스 종료 (또는 종료)와 동일하지 않으므로 사용자가 앱을 종료했다가 다시 시작하려고했습니다. (그러면 사용자는 실행중인 응용 프로그램 목록으로 이동하여 그 위치에서 중지 할 수 있습니다-작동하지만 사용자는 응용 프로그램을 종료하는 방법을 모릅니다.)

이 기능의 효과를 관찰하려면 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에서는 변경되었습니다.


답변

내 솔루션이 프로세스 / 응용 프로그램을 다시 시작하지 않습니다. 앱이 홈 활동을 “다시 시작”하고 다른 모든 활동을 해제 할 수 있습니다. 사용자가 다시 시작하는 것처럼 보이지만 프로세스는 동일합니다. 어떤 경우에는 사람들이이 효과를 달성하기를 원한다고 생각합니다.

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