ฉันจะ "เริ่มต้นใหม่" แอป Android โดยทางโปรแกรมได้อย่างไร


231

ประการแรกฉันรู้ว่าไม่ควรฆ่า / รีสตาร์ทแอปพลิเคชันบน Android ในกรณีการใช้งานของฉันฉันต้องการรีเซ็ตแอปพลิเคชันจากโรงงานในกรณีเฉพาะที่เซิร์ฟเวอร์ส่งข้อมูลเฉพาะไปยังลูกค้า

ผู้ใช้สามารถลงชื่อเข้าใช้บนเซิร์ฟเวอร์ด้วยอินสแตนซ์ของแอปพลิเคชันเดียวเท่านั้น (เช่นไม่อนุญาตให้ใช้อุปกรณ์หลายเครื่อง) หากอินสแตนซ์อื่นได้รับการ "ล็อคอิน" - ล็อคอินสแตนซ์อื่น ๆ ทั้งหมดของผู้ใช้นั้นจะต้องลบข้อมูล (รีเซ็ตเป็นค่าจากโรงงาน) เพื่อรักษาความสอดคล้อง

เป็นไปได้ที่จะบังคับให้ล็อกเพราะผู้ใช้อาจลบแอพและติดตั้งใหม่ซึ่งจะทำให้เกิดอินสแตนซ์ - id อื่นและผู้ใช้จะไม่สามารถปลดล็อคได้อีก ดังนั้นจึงเป็นไปได้ที่จะบังคับให้ล็อค

เนื่องจากความเป็นไปได้ในการบังคับนั้นเราจำเป็นต้องตรวจสอบอินสแตนซ์ที่เป็นรูปธรรมเสมอว่ามันมีล็อค ที่ทำบน (เกือบ) แต่ละคำขอไปยังเซิร์ฟเวอร์ เซิร์ฟเวอร์อาจส่ง "รหัสล็อคผิด" หากตรวจพบว่าแอปพลิเคชันไคลเอนต์ต้องลบทุกอย่าง


นั่นคือกรณีใช้งาน

ฉันมีActivityA ที่เริ่มเข้าสู่ระบบActivityL หรือActivityB หลักของแอปขึ้นอยู่กับค่าที่ใช้ร่วมกัน Prefs หลังจากเริ่ม L หรือ B มันจะปิดตัวเองเพื่อให้เฉพาะ L หรือ B กำลังทำงาน ดังนั้นในกรณีที่ผู้ใช้เข้าสู่ระบบแล้ว B กำลังทำงานอยู่ในขณะนี้

B เริ่มต้น C. C เรียกstartServiceหาIntentServiceD ผลลัพธ์ในสแต็กนี้:

(A)> B> C> D

จากเมธอด onHandleIntent ของ D เหตุการณ์ถูกส่งไปที่ResultReceiver R

R ตอนนี้จัดการกับเหตุการณ์นั้นโดยให้ผู้ใช้โต้ตอบที่เขาสามารถเลือกรีเซ็ตแอปพลิเคชันจากโรงงาน (ลบฐานข้อมูล sharedPrefs ฯลฯ )

หลังจากรีเซ็ตเป็นค่าเริ่มต้นจากโรงงานฉันต้องการรีสตาร์ทแอปพลิเคชั่น (เพื่อปิดกิจกรรมทั้งหมด) และเริ่ม A อีกครั้งเท่านั้นจากนั้นจึงเปิดใช้งานการเข้าสู่ระบบ Activity L และเสร็จสิ้น:

(A)> L

วิธีการโต้ตอบของ Dialog มีลักษณะดังนี้:

@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 ได้อย่างไร

คำตอบ:


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 ครั้ง (main -> prefs) การกดย้อนกลับจะพาฉันไปที่แอพเก่ากลับไปที่หน้าจอเดียว
Mgamerz

5
ในกรณีของฉัน System.exit (0) ไม่ทำงานเนื่องจากธุรกรรมถูกย้อนกลับ ฉันใช้ activity.finish () แทน และมันใช้งานได้ดี
รวม

6
@Qulin, Guys! คุณไม่จริงจัง! ตัวอย่างนี้เป็นเหมือนทิศทางมากกว่าตัวอย่างชีวิตจริง คุณต้องแก้ไขข้อมูลโค้ดนี้ด้วยชื่อกิจกรรมเริ่มต้นรหัสเจตนาและออกจากกลไกทุกอย่างที่คุณมี อย่าคัดลอกวางอย่างเด็ดขาด
Oleg Koshkin

19
สิ่งนี้ไม่สามารถใช้งานได้กับ Android Q อีกต่อไปเนื่องจากข้อ จำกัด ใหม่ในการทำกิจกรรมเบื้องหลังdeveloper.android.com/preview/privacy//
Marco 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 มิลลิวินาที อย่างไรก็ตามไลบรารีนี้ProcessPhoenixโดย Jack Wharton ทำได้ดีกว่าและรวดเร็ว แต่มันก็ไม่คุ้มที่จะเพิ่มไลบรารี่สำหรับฟังก์ชั่นนี้ภายในแอพเท่านั้น
blueware

@blueware ฉันได้อัปเดตคำตอบของฉันและเพิ่มรหัสซึ่งใช้ภายใน ProcessPhonix
mikepenz

@mikepenz ผู้ชายคนนี้ "Ilya_Gazman" ทำได้ดีขึ้นมากและไม่ต้องใช้ห้องสมุด
blueware

3
@blueware - ยกเว้นโซลูชันของ Ilya จะไม่เริ่มกระบวนการใหม่ดังนั้นข้อมูลคงที่หรือไลบรารี NDK ที่โหลดจะไม่ได้รับการกำหนดค่าเริ่มต้นใหม่อย่างถูกต้อง
Ted Hopp

อุปกรณ์ Huawei และ Samsung บางรุ่นมีข้อ จำกัด ในการใช้งานAlarmManagerผิดวิธีหากใช้วิธีนี้จะดีกว่านี้ไหม?
blueware

69

Jake Wharton เพิ่งเผยแพร่ไลบรารี่ของProcessPhoenixซึ่งทำในลักษณะที่น่าเชื่อถือ โดยทั่วไปคุณต้องโทร:

ProcessPhoenix.triggerRebirth(context);

ไลบรารีจะเสร็จสิ้นกิจกรรมการโทรโดยอัตโนมัติฆ่ากระบวนการแอปพลิเคชันและเริ่มต้นกิจกรรมแอปพลิเคชันเริ่มต้นใหม่หลังจากนั้น


ดูเหมือนว่าจะใช้งานได้ แต่ฉันได้รับความผิดพลาด (ซึ่งได้รับรายงาน) ไม่แน่ใจว่านี้เหมาะ
BK-

1
ฉันไม่เคยมีปัญหาใด ๆ กับ lib แต่อย่าลังเลที่จะรายงานข้อผิดพลาดที่github.com/JakeWharton/ProcessPhoenix/issues
TBieniek

โอ๊ะฉันเพิกถอนความคิดเห็นของฉันเพราะฉันไม่ได้ดูข้อความที่อยู่ใกล้พอ ฉันขาดตัวกรองความตั้งใจในกิจกรรมเริ่มต้นของฉัน มันอาจจะคุ้มค่าที่จะต้องทราบถึงตัวกรองเจตนาที่ต้องการ
BK-

1
นี่คือทางออกที่ดีที่สุด
yongsunCN

1
@Shambhu คุณต้องเพิ่มแท็ก<category android:name="android.intent.category.DEFAULT" />ในกิจกรรมเริ่มต้นของคุณ <intent-filter> ในรายการแอป
Muhammed Refaat

57

ฉันได้แก้ไขคำตอบ 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);
}

8
โดยตรงจากเอกสาร : " การโทร System.exit(n) เทียบเท่ากับการโทรอย่างมีประสิทธิภาพ: Runtime.getRuntime().exit(n) " ภายในSystem.exit()เพียงแค่หันไปรอบ ๆ Runtime.getRuntime().exit()และบริการโทร ไม่มีสิ่งใด "ดีกว่า" เกี่ยวกับอันใดอันหนึ่ง (ยกเว้นกรณีที่มีข้อกังวลเกี่ยวกับจำนวนการพิมพ์ที่จะทำได้หรือการเรียกเมธอดที่เพิ่มขึ้นหนึ่งเลเยอร์)
Ted Hopp

ที่ไหนและเมื่อใดวิธีการโทรข้างต้น?
Makvin

1
@ Makvin คุณตัดสินใจว่าจะโทรหาที่ไหน เคสของฉันคือรีสตาร์ทแอพหลังจากเปลี่ยนภาษา
android_dev

@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

4
แต่น่าเสียดายที่จะเลิกตอนนี้IntentCompat.makeRestartActivityTask หากคุณตรวจสอบรหัสที่มาIntent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASKมันเป็นง่ายๆเป็นเพียงการเพิ่มธง
พอลแลมเมอร์มา

IntentCompat.makeRestartActivityTask ถูกลบออก
luckyhandler

28

มีเคล็ดลับที่ดีจริงๆ ปัญหาของฉันคือการที่ C ++ jni ไลบรารี่เก่า ๆ รั่วไหลออกมา เมื่อถึงจุดหนึ่งมันก็หยุดทำงาน ผู้ใช้พยายามออกจากแอพและเปิดใช้อีกครั้ง - โดยไม่มีผลลัพธ์เนื่องจากการทำกิจกรรมไม่เหมือนกับกระบวนการเสร็จสิ้น (หรือการฆ่า) (โดยวิธีการที่ผู้ใช้สามารถไปที่รายการของแอปพลิเคชันที่กำลังรันและหยุดการทำงานจากที่นั่น - สิ่งนี้จะใช้งานได้ แต่ผู้ใช้ไม่ทราบวิธีการยกเลิกแอปพลิเคชัน)

หากคุณต้องการสังเกตผลของคุณสมบัตินี้ให้เพิ่มstaticตัวแปรลงในกิจกรรมของคุณและเพิ่มขึ้นทีละตัวพูดกดปุ่ม หากคุณออกจากกิจกรรมแอปพลิเคชันแล้วเรียกใช้แอปพลิเคชันอีกครั้งตัวแปรแบบคงที่นี้จะเก็บค่าไว้ (หากออกจากแอปพลิเคชันจริง ๆ ตัวแปรจะถูกกำหนดเป็นค่าเริ่มต้น)

(และผมต้องแสดงความคิดเห็นในเหตุผลที่ผมไม่ต้องการที่จะแก้ไขข้อผิดพลาดแทน. ห้องสมุดที่เขียนมานานหลายทศวรรษที่ผ่านมาและการรั่วไหลทรัพยากรนับตั้งแต่. ผู้บริหารเชื่อมันก็ทำงาน . the ค่าใช้จ่ายในการให้การแก้ไขแทนการแก้ปัญหา ... ฉันคิดว่าคุณได้รับความคิด)

ตอนนี้ฉันจะรีเซ็ตไลบรารี jni ที่ใช้ร่วมกัน (aka ไดนามิก, .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

5
โซลูชันนี้ปิดแอปสำหรับฉัน แต่ไม่ได้เริ่มต้นใหม่ อย่างน้อยบน Android 4.3
คิริลล์ Rakhman

1
บน samsung galaxy mega android 4.2.2 มันทำให้เกิดการวนซ้ำไม่สิ้นสุดของการรีสตาร์ท ดังนั้นแอปจะไม่เริ่มต้นอีกครั้ง
Gunhan

@Gunhan 1) จะเกิดอะไรขึ้นถ้าคุณแทนที่System.exit(0)ด้วยandroid.os.Process.killProcess(android.os.Process.myPid());? 2) การวนซ้ำไม่สิ้นสุดมีแนวโน้มมากที่สุดซึ่งหมายความว่าพวกเขาจะไม่ลบกิจกรรมสูงสุดเมื่อรีสตาร์ทแอป โดยหลักการคุณสามารถเพิ่มตัวแปรบูลีนสแตติกตั้งค่าเป็นจริงก่อนที่จะเรียกใช้กิจกรรมรีสตาร์ทและหลังจากการรีสตาร์ทจะเป็นเท็จ ดังนั้นกิจกรรมสามารถค้นหาว่าการรีสตาร์ทเกิดขึ้นแล้วหรือไม่ (และถ้ามันเกิดขึ้นเพียงแค่เสร็จสิ้น () ) OTOH รายงานของคุณหมายความว่าเคล็ดลับไม่ทำงานเหมือนกันในทุกอุปกรณ์
18446744073709551615

@Gunham หากคุณเริ่มต้นกิจกรรมเดียวกันกับที่ทำให้เกิดการรีสตาร์ทมันจะวนซ้ำไม่ จำกัด บนอุปกรณ์ใด ๆ
Lukas Hanacek

23

โซลูชันของฉันไม่รีสตาร์ทกระบวนการ / แอปพลิเคชัน มันช่วยให้แอป "รีสตาร์ท" กิจกรรมที่บ้าน (และยกเลิกกิจกรรมอื่น ๆ ทั้งหมด) ดูเหมือนว่าจะเริ่มต้นใหม่ให้กับผู้ใช้ แต่กระบวนการนี้เหมือนกัน ฉันคิดว่าในบางกรณีผู้คนต้องการบรรลุผลนี้ดังนั้นฉันจึงทิ้งมันไว้ที่นี่ FYI

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
สิ่งนี้จะไม่รีสตาร์ทแอปพลิเคชันเพียงสร้างคลาสใหม่เท่านั้น ดังนั้นตัวแปรสแตติกใด ๆ ภายในคลาสจะเก็บค่าจากการรันก่อนหน้านี้
Brian White

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

24
สิ่งนี้จะไม่รีสตาร์ทแอปพลิเคชันเพียงสร้างคลาสใหม่เท่านั้น ดังนั้นตัวแปรสแตติกใด ๆ ภายในคลาสจะเก็บค่าจากการรันก่อนหน้านี้
Brian White

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 แต่ฉันไม่พบว่าเป็นเช่นนั้น ฉันยืนยันว่าแอปพลิเคชันวัตถุในแอปของฉันถูกสร้างขึ้นใหม่ดังนั้นฉันเชื่อว่ากระบวนการถูกฆ่าและเริ่มต้นใหม่จริงๆ

วัตถุประสงค์เดียวที่ฉันใช้เพื่อเริ่มต้นแอพใหม่หลังจากผู้ใช้เปิดใช้งานหรือปิดใช้งานการรายงานข้อขัดข้องสำหรับ Firebase Crashlytics ตามเอกสารของพวกเขาแอปจะต้องเริ่มต้นใหม่ (กระบวนการถูกฆ่าและสร้างใหม่) เพื่อให้การเปลี่ยนแปลงนั้นมีผล


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

ตัวอย่างการใช้งานเริ่มต้นใหม่หรือเรียกใช้appAจากappB :

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
สิ่งนี้จะจบกิจกรรมทั้งหมดในงานปัจจุบัน แต่จะไม่รีสตาร์ทกระบวนการหรือแม้แต่สร้างแอปพลิเคชันวัตถุขึ้นมาใหม่ ดังนั้นข้อมูลสแตติกใด ๆ ที่เริ่มต้นระหว่างการสร้างแอปพลิเคชันหรือโดยคลาส jni จะยังคงอยู่ในสถานะปัจจุบันและไม่ได้กำหนดค่าเริ่มต้นใหม่
Ted Hopp


3

นี่คือตัวอย่างในการรีสตาร์ทแอปของคุณด้วยวิธีทั่วไปโดยใช้ PackageManager:

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

สิ่งนี้จะเปิดใช้งานอีกครั้ง แต่ไม่รีสตาร์ทกระบวนการหรือแม้แต่Applicationวัตถุ ดังนั้นข้อมูลสแตติกใด ๆ ที่เริ่มต้นระหว่างการสร้างApplicationหรือคลาส jni ยังคงอยู่ในสถานะปัจจุบันและไม่ได้กำหนดค่าเริ่มต้นใหม่
Ted Hopp

3

ลองนี้:

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

1
เช่นเดียวกับคำตอบอื่น ๆ ทั้งหมดที่นี่แล้วแนะนำสิ่งเดียวกันนี้จะไม่รีสตาร์ทแอพลิเคชันเพียงสร้างคลาส ดังนั้นข้อมูลสแตติกใด ๆ ในกระบวนการจะไม่ถูกรีเซ็ต
Ted Hopp


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


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 แบบไดนามิก แต่คุณสามารถ hardcode ได้ บางคนชอบสิ่งนี้:

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

โทรstartInstrumentationโหลดแอปของคุณใหม่ อ่านคำอธิบายของวิธีนี้ แต่มันอาจไม่ปลอดภัยหากทำตัวเหมือนแอป kill


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

-1

ด้วยห้องสมุด Process Phoenixกระบวนการห้องสมุดฟินิกซ์กิจกรรมที่คุณต้องการเปิดใหม่มีชื่อว่า "A"

รสชวา

// Java
public void restart(){
    ProcessPhoenix.triggerRebirth(context);
}

รสชาติ Kotlin

// kotlin
fun restart() {
    ProcessPhoenix.triggerRebirth(context)
}

นี่เป็นผลลัพธ์ที่โชคร้ายในการยกเลิกการเชื่อมต่อดีบักเกอร์ของคุณ
DrSatan1

-3

คุณสามารถเริ่มกิจกรรมปัจจุบันของคุณเช่นนี้:

ส่วน:

activity?.recreate()

กิจกรรม:

recreate()

3
นี่ไม่ใช่สิ่งที่ OP ต้องการทำ
Ted Hopp
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.