Summary and analysis of the method of one-click exit of Android APP

Source of this article: Flame Armor csdn blog: http://blog.csdn.net/totond Flame Armor Email: [email protected] This demo address: https://github.com/totond/TestAppExit This article is original, please reprint it Indicate the source of this! Preface - Does the APP need to exit the function?

  Google recommends that the APP does not need the exit function, because as long as the APP is switched to the background, the system's GC mechanism will automatically recycle the background process according to the memory situation. If the APP process is not recycled, the user can quickly switch back to the APP. Then the user's habits and some developers don't pay attention to this design (for example, the onTrimMemory() method in the Application I wrote in the last article is not used), and the previous mobile phone memory is not very large (GC sometimes It is too late to clean up the background, causing the phone to become very stuck sometimes), which leads to a lot of demand for exiting the APP (for example, many APPs double-click the back button to exit the function). Up to now, this issue is still debated by many people. Check this out. I'm neutral on this issue (if I said I didn't need it, I wouldn't have to write this blog ^_^), just do it when the need comes. There are also many articles on this topic on the Internet, and there are many implementations, but each method has its advantages and disadvantages. Let's test all these methods, and then analyze and summarize them.

The API version tested in this article is 23, Android 6.0, and minSDK is API 16. Preparations

  To test, you must write a demo first. Here, prepare the demo first. The Activity jump relationship inside is as follows:

  The startup mode of all activities is the default mode standard, which needs to be changed later for testing. Write an Application subclass BaseApplication, use registerActivityLifecycleCallbacks() to monitor the life cycle changes of each Activity, and output the current process ID (useful later):

registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) { Log.d(TAG, "onActivityCreated: " + activity.getLocalClassName()); Log.d(TAG, "Pid: " + + Process.myPid()); }

        [@Override](https://my.oschina.net/u/1162528)
        public void onActivityStarted(Activity activity) {
            Log.d(TAG, "onActivityStarted: " + activity.getLocalClassName());
        }

        [@Override](https://my.oschina.net/u/1162528)
        public void onActivityResumed(Activity activity) {
        }

        [@Override](https://my.oschina.net/u/1162528)
        public void onActivityPaused(Activity activity) {
        }

        @Override
        public void onActivityStopped(Activity activity) {
            Log.d(TAG, "onActivityStopped: " + activity.getLocalClassName());
        }

        @Override
        public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

        }

        @Override
        public void onActivityDestroyed(Activity activity) {
            Log.d(TAG, "onActivityDestroyed: " + activity.getLocalClassName());
        }
    });

  Each Activity has two Buttons, one is used to call the method to exit the APP (CloseButton):

public void onClick(View v) {
    Log.d(BaseApplication.getTAG(), "按下Close———————————————————————————— ");
    exitAPP1();

// exitAPP2(); // exitAPP3(); // exitAPP4(); // exitAPP5();

  One is used to call the following methods that do not work, for testing and research (TestButton):

public void onClick(View v) {
    Log.d(BaseApplication.getTAG(), "按下Test———————————————————————————— ")
    System.exit(0);

// Runtime.getRuntime().exit(0);

// Process.killProcess(Process.myPid());

// ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); // activityManager.killBackgroundProcesses(context.getPackageName());

// activityManager.restartPackage(context.getPackageName()); }

  At this point, the preparation work is done. Due to the large amount of code, it will not be posted here. The specific implementation code can be downloaded from the demo address.

Several methods that do not work on the Internet

  There are some older materials on the Internet, and some methods that I have tested and found to be unworkable (I don’t know if it worked when they used it but it doesn’t work now).

System.exit(0) method

  This method is equivalent to Runtime.getRuntime().exit(0). Logically speaking, it ends the current virtual machine. After testing, it is found that the current virtual machine is closed after execution, but it also restarts a new virtual machine. , restart the other unclosed activities in sequence except the current activity. Simply put, it is to finish the current Activity, and then refresh the APP. As can be seen from the log below, the process ID of the APP has changed (only two activities are opened here to make the output simple, in fact, various combinations of fancy activity startup sequences and startup modes have been tested. The conclusion remains the same):

com.yanzhikai.testappexit D/TestAppExit: onActivityCreated: Activities.MainActivity com.yanzhikai.testappexit D/TestAppExit: Pid: 29579 com.yanzhikai.testappexit D/TestAppExit: onActivityStarted: Activities.MainitiActivity . : Activities.A0Activity com.yanzhikai.testappexit D/TestAppExit: Pid: 29579 com.yanzhikai.testappexit D/TestAppExit: onActivityStarted: Activities.A0Activity com.yanzhikai.testappexit D/TestAppExit: onActivityStoppedActivity. /TestAppExit: 按下Test——————————————————————————— com.yanzhikai.testappexit D/TestAppExit: onActivityCreated: Activities.MainActivity com. yanzhikai.testappexit D/TestAppExit: Pid: 29985 com.yanzhikai.testappexit D/TestAppExit: onActivityStarted: Activities.MainActivity

Process.killProcess(Process.myPid())方法

  This method is supposed to kill the current thread, but the test results found that the actual effect is the same as the above System.exit(0) method:

com.yanzhikai.testappexit D/TestAppExit: onActivityCreated: Activities.MainActivity com.yanzhikai.testappexit D/TestAppExit: Pid: 30082 com.yanzhikai.testappexit D/TestAppExit: onActivityStarted: Activities.MainActivity com. : Activities.A0Activity com.yanzhikai.testappexit D/TestAppExit: Pid: 30082 com.yanzhikai.testappexit D/TestAppExit: onActivityStarted: Activities.A0Activity com.yanzhikai.testappexit D/TestAppExit: onActivityStoppedi. /TestAppExit: 按下Test——————————————————————————— com.yanzhikai.testappexit D/TestAppExit: onActivityCreated: Activities.MainActivity com. yanzhikai.testappexit D/TestAppExit: Pid: 30603 com.yanzhikai.testappexit D/TestAppExit: onActivityStarted: Activities.MainActivity

Therefore, when the current Activity is the last Activity of the current task stack, and the APP has no other task stacks, calling these two methods can end the Activity normally, which is similar to calling finish in the first Activity, but the finish will not end. process, these two methods will. As for why the above two methods are like this, it does not match the description of the API. Some people say that it is the modification of the system manufacturer, and some people say that it is the security mechanism of Google. . ActivityManager.killBackgroundProcesses() method

  I wrote this method on the Internet, and even looking at the name, I thought it would not work, but of course it didn't work. This should be the method used to end the background process, but I don't know why it was used to end the current APP.

ActivityManager.restartPackage () method

  This is a very old method, it is now deprecated (it was said to be possible before), and now in my API24 version, it is just a vest that wraps the killBackgroundProcesses() method:

@Deprecated
public void restartPackage(String packageName) {
    killBackgroundProcesses(packageName);
}

1 2 3 4 How to exit the APP with one key

  After talking for so long, I finally got to the point. The so-called one-click means that as soon as this method is called, the APP can be closed. The following methods are to close all the activities in the current APP and end the APP process (if you do not want to end the APP process) Can not use System.exit(0)).

The first method - simple and rough method

  There are some methods on the Internet to simulate the Activity stack to manage the Activity. This method does not need to simulate, but directly calls the system API to obtain the current task stack, finishes all the activities in it, and then ends the process (if you do not want to end the process, you can not call System. exit(0)).

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void exitAPP1() {
    ActivityManager activityManager = (ActivityManager) context.getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
    List<ActivityManager.AppTask> appTaskList = activityManager.getAppTasks();
    for (ActivityManager.AppTask appTask : appTaskList) {
        appTask.finishAndRemoveTask();
    }

// appTaskList.get(0).finishAndRemoveTask(); System.exit(0); 1 2 3 4 5 6 7 8 9 According to the description of the API source code, the function of this ActivityManager.getAppTasks() method should be:

Get the list of tasks associated with the calling application.

1 But after testing, in many cases (there are multiple task stacks in one APP, and multiple processes are opened in one APP), there is only one AppTask in the obtained appTaskList, but only the current task stack is obtained. The comment does not say that the current task stack will be given. All tasks of the running APP, please let me know if you know the truth.

Advantages and disadvantages

advantage:

Simple and rude, just use the Context to obtain the ActivityManager to obtain the current task stack of the APP, and no other operations are required. shortcoming:

This method can only end the current task stack. For the case where the APP has multiple task stacks (the startup mode with Activity is singleInstance), it will not end other background task stacks, so you need to make your own logical judgment; API21 or above is required , that is, Android 5.0 or above. According to the data obtained from Android Studio 2.3.2, the current proportion of mobile phones and tablets with Android 5.0 or above is 40.5% (I don’t know if it is accurate, whether China’s national conditions are considered). The second method - preservation management method

  This method is the most popular on the Internet. It is to build a container by yourself to save the instance of the running Activity, write it in the onCreate method, write it in the onDestroy method, and take out all the Activity instances and finish them when you need to end the APP. . Here I use LinkedList, which is fast in addition and deletion. Store activityLinkedList in BaseApplication, and control the addition and deletion of all Activity instances through the registerActivityLifecycleCallbacks() method.

@Override
public void onCreate() {
    super.onCreate();

    activityLinkedList = new LinkedList<>();

    registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
        @Override
        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
            Log.d(TAG, "onActivityCreated: " + activity.getLocalClassName());
            Log.d(TAG, "Pid: " +  + Process.myPid());
            activityLinkedList.add(activity);
        }

        @Override
        public void onActivityStarted(Activity activity) {
            Log.d(TAG, "onActivityStarted: " + activity.getLocalClassName());
        }

        @Override
        public void onActivityResumed(Activity activity) {
        }

        @Override
        public void onActivityPaused(Activity activity) {
        }

        @Override
        public void onActivityStopped(Activity activity) {
            Log.d(TAG, "onActivityStopped: " + activity.getLocalClassName());
        }

        @Override
        public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

        }

        @Override
        public void onActivityDestroyed(Activity activity) {
            Log.d(TAG, "onActivityDestroyed: " + activity.getLocalClassName());
            activityLinkedList.remove(activity);
        }
    });
}


public static void showList() {
    for (Activity activity : activityLinkedList) {
        Log.d(TAG, "showList: " + activity.getLocalClassName());
    }
}

public static void exitAppList() {
    for (Activity activity : activityLinkedList) {
        activity.finish();
    }
}

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 038 39 40 41 42 43 44 9 45 51 52 53 54 55 56 57 When ending the APP, just call the exitAppList() method and end the process. You can also use showList() to log the list of currently running Activity:

private void exitAPP2() {
    BaseApplication.showList();
    BaseApplication.exitAppList();
    System.exit(0);
}

1 2 3 4 5 Pros and cons

advantage:

This method does not need to take into account the fact that the Activity has multiple task stacks, no matter what the startup mode is, as long as the Activity goes through the normal life cycle when it is created and ended—that is, it goes through the onCreate method when it is created and the onDestroy method when it ends. Can't escape the "claw" of ActivityList. shortcoming:

As mentioned earlier, Activity is to go through the normal life cycle, so this method will not cause problems. Let's test it. First, enter B0Activity in order, and then press Test. The Test here is to execute System.exit(0) once, and then we will find that the Close method does not end all activities normally, but only ends the current Activity. :

  The above problem is not big, because no one should call System.exit(0) for no reason, but the following problem is relatively big, I artificially add a null pointer to B0Activity, so that a null pointer will be thrown when entering Abnormal, let the APP crash, and then found that the APP also restarted the process, and the Activity stack reverted to A0Activity. At this time, you can see that there is only one A0Activity in the activityLinkedList, and the effect of one-click exit cannot be achieved.

  That is to say, using this method, when the Activity is not created and ended through the normal life cycle, it cannot achieve the effect of exit (if the Activity ends abnormally and the APP process does not end, the activityLinkedList holds the Activity's Instances can cause memory leaks, but I haven't seen this happen so far, both of which are process restarts). The third method - the bottom line method

  This method allows the entry Activity of the APP to use the SingleTask startup mode, so that if the Activity at the bottom of the task stack is started in the following Activity, its onNewIntent() method will be called, and in this method, it is judged whether to call finish according to the Intent. , if yes, then the Activity of the entire task stack is over:

  Here, the onNewIntent() method is rewritten in MainActivity:

//exitApp3()方法使用
@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    if (intent != null) {
        boolean isExitApp = intent.getBooleanExtra("exit", false);
        if (isExitApp) {
            this.finish();
        }
    }
}

1 2 3 4 5 6 7 8 9 10 11 Called on exit:

private void exitAPP3() {
    Intent intent = new Intent(context, MainActivity.class);
    intent.putExtra("exit", true);
    context.startActivity(intent);
    System.exit(0);
}

1 2 3 4 5 6 Pros and cons

advantage

I'm not afraid that the Activity like the second method does not follow the normal life cycle, and the implementation is relatively simple. shortcoming

To limit the startup mode of MainActivity to singleTask; this method can only end the Activity of the current task stack. If there is an Activity whose startup mode is SingleInstance, you must write your own logic judgment. The fourth method - RxBus exit method

  Use RxBus as the event bus, and register the subscription when the Activity is onCreate():

//exitApp4()方法使用
private Disposable disposable;

//exitApp4()方法使用注册订阅
private void initRxBusExit(){
    disposable = RxBus.getInstance().toObservable(String.class)
            .subscribe(new Consumer<String>() {
                @Override
                public void accept(String s) throws Exception {
                    if (s.equals("exit")){
                        finish();
                    }
                }
            });
}

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Unsubscribe in Activity's onDestroy():

    //exitApp4()方法使用取消订阅
    if (!disposable.isDisposed()){
        disposable.dispose();;
    }

1 2 3 4 Send an event when it is time to exit:

private void exitAPP4() {
    RxBus.getInstance().post("exit");
    System.exit(0);
}

1 2 3 4 The implementation of RxBus here is based on RxJava2.0.1. Refer to this article to prevent the article from being too long and will not be posted here. The specific implementation can be seen in my demo. Advantages and disadvantages

advantage

Can be combined with RxJava and RxBus and can be easily used for projects using RxBus and RxJava. shortcoming

Requires RxJava and RxBus. It is not recommended for APPs that do not use RxJava. Other methods can be used. It is necessary to register and unsubscribe in the onCreate() method and onDestroy() method of each Activity, which is more troublesome, but it can be solved by using a unified base class of Activity, but it also has the trouble of inheriting the unified base class. Like the second one, if a crash occurs and the process is restarted, it will still fail. The fifth method - broadcast monitoring method

  This approach is similar to the previous one, by having each Activity register and unregister a broadcast receiver on onCreate() and onDestroy():

public class CloseReceiver extends BroadcastReceiver { private Activity activity;

public CloseReceiver(Activity activity){
    this.activity = activity;
}

@Override
public void onReceive(Context context, Intent intent) {
    activity.finish();
}

} 1 2 3 4 5 6 7 8 9 10 11 12 protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //exitApp5()方法使用 closeReceiver = new CloseReceiver(this); registerReceiver(closeReceiver,new IntentFilter(BaseApplication.EXIT)); }

protected void onDestroy() {
    super.onDestroy();
    //exitApp5()方法使用
    unregisterReceiver(closeReceiver);
}

1 2 3 4 5 6 7 8 9 10 11 12 13 Called when exit is required:

private void exitAPP5() {
    context.sendBroadcast(new Intent(BaseApplication.EXIT));
}

1 2 3 Pros and cons

advantage

As with the second method, there is no need to take into account the fact that the Activity has multiple task stacks. shortcoming

It is more troublesome to register a broadcast receiver for each open Activity, but it can be solved by using a unified base class of Activity, but it also has the trouble of inheriting the unified base class. Like the second one, if a crash occurs and the process is restarted, it will still fail. This method cannot be followed by System.exit(0) to end the process, because after executing the method of sending the broadcast, the program will not wait until the broadcast receiver receives the broadcast, and the program will start executing the next sentence System.exit(0), and then It directly becomes the effect of executing System.exit(0). Summarize

  In general, the second method - saving management method is more practical, but when Android 5.0 is popularized, the first method should be used more, because there should be fewer APPs that use the SingleInstance startup mode Activity. When the number of APPActivity is small, and there is no special requirement for the startup mode (that is, when you are lazy), you can choose the third method. The fourth method is more suitable for use when RxJava is used.

Reference article

http://www.jianshu.com/p/8cd954b43eed http://johnnyshieh.me/posts/rxbus-rxjava2/ http://www.imooc.com/article/3300

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325014517&siteId=291194637