Programing

Android 앱을 프로그래밍 방식으로 "다시 시작"하는 방법은 무엇입니까?

lottogame 2020. 5. 8. 08:13
반응형

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

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


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

새로운 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);
}

정말 좋은 트릭이 있습니다. 내 문제는 정말 오래된 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에서이 활동을 언급하는 것을 잊지 마십시오

The advantage of this method is that there is no delays.

UPD: it worked in Android 2.x, but in Android 4 something has changed.


My solution doesn't restart the process/application. It only lets the app "restart" the home activity (and dismiss all other activities). It looks like a restart to users, but the process is the same. I think in some cases people want to achieve this effect, so I just leave it here FYI.

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

Ok, I refactored my app and I will not finish A automatically. I let this run always and finish it through the onActivityResult event. In this way I can use the FLAG_ACTIVITY_CLEAR_TOP + FLAG_ACTIVITY_NEW_TASK flags to get what I want:

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

and in the 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);
}

Thanks anyway!


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

The best way to fully restart an app is to relaunch it, not just to jump to an activity with FLAG_ACTIVITY_CLEAR_TOP and FLAG_ACTIVITY_NEW_TASK. So my solution is to do it from your app or even from another app, the only condition is to know the app package name (example: '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);
}

Example of usage restart or launch appA from appB:

forceRunApp(mContext, "com.example.myProject.appA");

You can check if the app is running:

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

Note: I know this answer is a bit out of topic, but it can be really helpful for somebody.


The only code that did not trigger "Your app has closed unexpectedly" is as follows. It's also non-deprecated code that doesn't require an external library. It also doesn't require a timer.

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

My best way to restart application is to use finishAffinity();
Since, finishAffinity(); can be used on JELLY BEAN versions only, so we can use ActivityCompat.finishAffinity(YourCurrentActivity.this); for lower versions.

Then use Intent to launch first activity, so the code will look like this:

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

Hope it helps.


Try using FLAG_ACTIVITY_CLEAR_TASK


Here is an example to restart your app in a generic way by using the PackageManager:

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

try this:

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

Directly start the Initial Screen with FLAG_ACTIVITY_CLEAR_TASK and FLAG_ACTIVITY_NEW_TASK.


I had to add a Handler to delay the exit:

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

Use:

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

It works starting from API level 16 (4.1), I believe.


You can use startInstrumentation method of Activity. You need implement empty Instrumentation and pointed in manifest. After that you can call this method for restart your app. Like this:

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

I get Instrumentation class name dynamically but you can hardcode it. Some like this:

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

Call startInstrumentation make reload of your app. Read description of this method. But it can be not safe if acting like kill app.


The application I'm working on has to give the user the possibility to choose which fragments to display (fragments are dynamically changed at run-time). The best solution for me was to restart completely the application.

So I tried plenty of solutions and none of them has worked for me, but this:

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

Hoping that is going to help someone else!


You can restart your current activity like this:

Fragment :

activity?.recreate()

Activity :

recreate()

참고URL : https://stackoverflow.com/questions/6609414/how-do-i-programmatically-restart-an-android-app

반응형