前言
- 在
Android
開發(fā)中捅厂,會經常存在 “一鍵退出App
” 的需求 - 但市面上流傳著 太多不可用的“一鍵退出
App
”功能實現 - 本文將全面總結“一鍵退出
App
”的實現方式贯卦,并為你一一實踐,希望你們會喜歡焙贷。
目錄
1. 需求本質
一鍵退出 App
其實是 兩個需求:
- 一鍵結束當前
App
所有的Activity
- 一鍵結束當前
App
進程
即 需要2個步驟 才可 完成 一鍵退出 App
需求撵割。下面,我將根據這兩個步驟進行功能實現講解辙芍。
2. 功能實現
2.1 (步驟1)一鍵結束當前 App 所有 Activity
2.1.1 實現方法類型
- 主要分為2類:通過
Android
組件 & 自身實現 - 具體如下圖:
注:上述方法僅僅只是結束當前App
所有的Activity
(在用戶的角度確實是退出了 App
)啡彬,但實際上該App
的進程還未結束。
2.1.2 具體介紹
a. 通過 Android 組件:Activity
方法1:采用Activity啟動模式:SingleTask
-
原理
- 將
App
的入口Activity
采用SingleTask
啟動模式
a. 入口
Activity
此時處于棧底
b. 關于SingleTask
的原理如下:
- 當需要退出
App
時啟動入口Activity
此時入口
Activity
上層的Activity
實例都將自動關閉移除 & 自身被放置在棧頂(這是SingleTask
啟動模式的特點)- 通過在入口
Activity
回調的onNewIntent()
中關閉自身即可
若在后面的
Activity
啟動 任務棧底的Activity
時,就會調用任務棧底Activity
的onNewIntent()
- 將
具體實現
步驟1:將 App
的入口 Activity
設置成 SingleTask
啟動模式
// AndroidMainifest.xml中的Activity配置進行設置
<activity
android:launchMode="singleTask"
//屬性
//standard:標準模式
//singleTop:棧頂復用模式
//singleTask:棧內復用模式
//singleInstance:單例模式
//如不設置,Activity的啟動模式默認為 標準模式(standard)
</activity>
步驟2:在入口 Activity
重寫 onNewIntent()
// 在該方法傳入一標志位標識是否要退出App & 關閉自身
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (intent != null) {
// 是否退出App的標識
boolean isExitApp = intent.getBooleanExtra("exit", false);
if (isExitApp) {
// 關閉自身
this.finish();
}
}
}
步驟3:在需要退出時調用 exitApp()
private void exitApp() {
Intent intent = new Intent(context, MainActivity.class);
intent.putExtra("exit", true);
context.startActivity(intent);
// 結束進程
// System.exit(0);
}
優(yōu)點
使用簡單 & 方便-
缺點
- 規(guī)定 App的入口Activity采用SingleTask啟動模式
- 使用范圍局限:只能結束當前任務棧的Activity数初,若出現多任務棧(即采用
SingleInstance
啟動模式)則無法處理
應用場景
Activity單任務棧
方法2:采用Activity啟動標記位
- 原理:對入口
Activity
采用 2 標記位:-
Intent.FLAG_ACTIVITY_CLEAR_TOP
:銷毀目標Activity
和它之上的所有Activity
脓斩,重新創(chuàng)建目標Activity
-
Intent.FLAG_ACTIVITY_SINGLE_TOP
:若啟動的Activity
位于任務棧棧頂,那么此Activity
的實例就不會重建留美,而是重用棧頂的實例( 調用onNewIntent()
)
-
- 具體使用(從
MainActivity
(入口Activity
) 跳轉到Activity2
& 一鍵退出)
步驟1:在MainActivity
中設置 重寫 onNewIntent()
MainActivity.java
// 設置 按鈕 跳轉到Activity2
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
startActivity(new Intent(MainActivity.this, Activity2.class));
}
});
}
// 在onNewIntent()傳入一標識符
// 作用:標識是否要退出App
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (intent != null) {
// 是否退出App的標識
boolean isExitApp = intent.getBooleanExtra("exit", false);
if (isExitApp) {
// 關閉自身
this.finish();
}
}
// 結束進程
// System.exit(0);
}
}
步驟2:在需要退出的地方(Activity2
)啟動MainActivity
& 設置標記位
// 當需要退出時,啟動入口Activity
Intent intent = new Intent();
intent.setClass(Activity2.this, MainActivity.class);
// 設置標記位
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
// 步驟1:該標記位作用:銷毀目標Activity和它之上的所有Activity,重新創(chuàng)建目標Activity
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
// 步驟2:若啟動的Activity位于任務棧棧頂菲语,那么此Activity的實例就不會重建,而是重用棧頂的實例( 調用實例的 onNewIntent() )
// 在步驟1中:MainActivity的上層的Activity2會被銷毀惑灵,此時MainActivity位于棧頂山上;由于步驟2的設置,所以不會新建MainActivity而是重用棧頂的實例&調用實onNewIntent()
// 傳入自己設置的退出App標識
intent.putExtra("exit", true);
startActivity(intent);
優(yōu)點
使用簡單 & 方便缺點
使用范圍局限:只能結束當前任務棧的Activity英支,若出現多任務棧(即采用SingleInstance
啟動模式)則無法處理應用場景
Activity單任務棧
方法3:通過系統(tǒng)任務棧
原理:通過
ActivityManager
獲取當前系統(tǒng)的任務棧 & 把棧內所有Activity
逐個退出具體使用
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
// 1. 通過Context獲取ActivityManager
ActivityManager activityManager = (ActivityManager) context.getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
// 2. 通過ActivityManager獲取任務棧
List<ActivityManager.AppTask> appTaskList = activityManager.getAppTasks();
// 3. 逐個關閉Activity
for (ActivityManager.AppTask appTask : appTaskList) {
appTask.finishAndRemoveTask();
}
// 4. 結束進程
// System.exit(0);
優(yōu)點
使用簡單佩憾、方便-
缺點
- 使用范圍局限:只能結束當前任務棧的Activity,若出現多任務棧(即采用
SingleInstance
啟動模式)則無法處理 - 對
Android
版本要求較高:Android 5.0
以上
- 使用范圍局限:只能結束當前任務棧的Activity,若出現多任務棧(即采用
- 應用場景
Android 5.0
以上的Activity
單任務棧
b. 通過 Android 組件: BroadcastReceiver
即使用 BroadcastReceiver
廣播監(jiān)聽
原理:在每個
Activity
里注冊廣播接收器(響應動作 = 關閉自身);當需要退出App
時 發(fā)送廣播請求即可具體實現
步驟1:自定義廣播接收器
public class ExitAppReceiver extends BroadcastReceiver {
private Activity activity;
public ExitAppReceiver(Activity activity){
this.activity = activity;
}
@Override
public void onReceive(Context context, Intent intent) {
activity.finish();
}
}
步驟2:在每個 Activity
里注冊廣播接收器(響應動作 = 關閉自身)
public class Activity extends AppCompatActivity {
private ExitAppReceiver mExitAppReceiver妄帘;
// 1. 在onCreate()中注冊廣播接收器
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mExitAppReceiver = new ExitAppReceiver(this);
registerReceiver(mExitAppReceiver,new IntentFilter(BaseApplication.EXIT));
}
// 1. 在onDestroy()中注銷廣播接收器
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(mExitAppReceive);
}
步驟3:當需要退出App時 發(fā)送廣播請求
context.sendBroadcast(new Intent(BaseApplication.EXIT));
// 注:此處不能使用:System.exit(0);結束進程
// 原因:發(fā)送廣播這個方法之后楞黄,不會等到廣播接收器收到廣播,程序就開始執(zhí)行下一句System.exit(0)抡驼,然后就直接變成執(zhí)行System.exit(0)的效果了鬼廓。
- 優(yōu)點
應用場景廣泛:兼顧單 / 多任務棧 & 多啟動模式的情況
缺點
實現復雜:需要在每個Activity
里注冊廣播接收器應用場景
任意情況下的一鍵退出 App,但無法終止App
進程
所以該方法僅僅是在用戶的角度來說 “一鍵退出App”
c. 自身實現
方法1:創(chuàng)建 鏈表
原理:通過在
Application
子類中建立一個Activity
鏈表:保存正在運行的Activity
實例致盟;當需要一鍵退出App
時把鏈表內所有Activity
實例逐個退出即可具體使用
步驟1:在BaseApplication
類的子類里建立Activity
鏈表
Carson_BaseApplicaiton.java
public class Carson_BaseApplicaiton extends Application {
// 此處采用 LinkedList作為容器碎税,增刪速度快
public static LinkedList<Activity> activityLinkedList;
@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());
activityLinkedList.add(activity);
// 在Activity啟動時(onCreate()) 寫入Activity實例到容器內
}
@Override
public void onActivityDestroyed(Activity activity) {
Log.d(TAG, "onActivityDestroyed: " + activity.getLocalClassName());
activityLinkedList.remove(activity);
// 在Activity結束時(Destroyed()) 寫出Activity實例
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
});
}
public void exitApp() {
Log.d(TAG, "容器內的Activity列表如下 ");
// 先打印當前容器內的Activity列表
for (Activity activity : activityLinkedList) {
Log.d(TAG, activity.getLocalClassName());
}
Log.d(TAG, "正逐步退出容器內所有Activity");
// 逐個退出Activity
for (Activity activity : activityLinkedList) {
activity.finish();
}
// 結束進程
// System.exit(0);
}
}
// 記得在Manifest.xml中添加
<application
android:name=".Carson_BaseApplicaiton"
....
</application>
步驟2:需要一鍵退出 App
時,獲取該 Applicaiton
類對象 & 調用exitApp()
private Carson_BaseApplicaiton app;
app = (Carson_BaseApplicaiton)getApplication();
app.exitApp();
- 效果圖
優(yōu)點
應用場景廣泛:兼顧單 / 多任務棧 & 多啟動模式的情況缺點
需要Activity
經歷正常的生命周期馏锡,即創(chuàng)建時調用onCreate()
雷蹂,結束時調用onDestroy()
因為只有這樣經歷正常的生命周期才能將
Activity
正確寫入 & 寫出 容器內
- 應用場景
任意情況下的一鍵退出App
實現
方法2:RxBus
原理:使用
RxBus
當作事件總線,在每個Activity
里注冊RxBus
訂閱(響應動作 = 關閉自身)眷篇;當需要退出App時 發(fā)送退出事件請求即可萎河。具體使用
步驟1:在每個 Activity
里注冊RxBus
訂閱(響應動作 = 關閉自身)
public class Activity extends AppCompatActivity {
private Disposable disposable;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity2);
// 注冊RxBus訂閱
disposable = RxBus.getInstance().toObservable(String.class)
.subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
// 響應動作 = 關閉自身
if (s.equals("exit")){
finish();
}
}
});
}
// 注意一定要取消訂閱
@Override
protected void onDestroy() {
if (!disposable.isDisposed()){
disposable.dispose();;
}
}
步驟2:當需要退出App時 發(fā)送退出事件
RxBus.getInstance().post("exit");
System.exit(0);
優(yōu)點
可與RxJava
&RxBus
相結合缺點
實現復雜:RxBus
本身的實現難度 & 需要在每個Activity
注冊和取消訂閱RxBus
使用應用場景
需要與RxJava
結合使用時
若項目中沒有用到
RxJava
&RxBus
不建議使用
- 至此,一鍵結束當前
App
的所有Activity
的 方法 講解完畢蕉饼。 - 注:上述方法僅僅只是結束當前
App
所有的Activity
(在用戶的角度確實是退出了App
)虐杯,但實際上該App
的進程還未結束
2.2 (步驟2)一鍵結束當前 App 進程
主要采用 Dalvik VM
本地方法
- 作用
結束當前Activity
& 結束進程
即 在 (步驟1)結束當前
App
所有的Activity
后,調用該方法即可一鍵退出App
(更多體現在結束進程上)
- 具體使用
// 方式1:android.os.Process.killProcess()
android.os.Process.killProcess(android.os.Process.myPid()) 昧港;
// 方式2:System.exit()
// System.exit() = Java中結束進程的方法:關閉當前JVM虛擬機
System.exit(0);
// System.exit(0)和System.exit(1)的區(qū)別
// 1. System.exit(0):正常退出擎椰;
// 2. System.exit(1):非正常退出,通常這種退出方式應該放在catch塊中创肥。
- 特別注意
假設場景:當前Activity
≠ 當前任務棧最后1個Activity
時达舒,調用上述兩個方法會出現什么情況呢?(即Activity1
-Activity2
-Activity3
(在Activity3
調用上述兩個方法))
答:
- 結束
Activity3
(當前Activity
)& 結束進程 - 再次重新開啟進程 & 啟動
Activity1
叹侄、Activity2
即在Android
中巩搏,調用上述Dalvik VM
本地方法結果是:
- 結束當前
Activity
& 結束進程 - 之后再重新開啟進程 & 啟動 之前除當前
Activity
外的已啟動的Activity
- 原因:**
Android
中的ActivityManager
時刻監(jiān)聽著進程**。一旦發(fā)現進程被非正常結束趾代,它將會試圖去重啟這個進程贯底。
- 應用場景
當任務棧只剩下當前Activity
(即退出了其余Activity
后),調用即可退出該進程撒强,即在(步驟1)結束當前 App 所有的 Activity 后禽捆,調用該方法即可一鍵退出App(更多體現在結束進程上)
注: 與 “在最后一個
Activity
調用finish()
”的區(qū)別:finish()
不會結束進程,而上述兩個方法會
至此飘哨,關于 ** 一鍵退出App
** 的兩個步驟講解完畢胚想。
3. Demo地址
關于上述說的方法Demo
都在Carson_Ho的Github地址:一鍵退出App
4. 總結
- 在 需要實現 一鍵退出
App
功能時,實際上是需要完成2個步驟:
步驟1:一鍵結束當前App
所有的Activity
步驟2:一鍵結束當前App
進程 - 每個步驟的方法總結如下
- 下一篇文章我將對講解
Android
的相關知識芽隆,感興趣的同學可以繼續(xù)關注本人的簡書哦浊服。
相關系列文章閱讀
Carson帶你學Android:學習方法
Carson帶你學Android:四大組件
Carson帶你學Android:自定義View
Carson帶你學Android:異步-多線程
Carson帶你學Android:性能優(yōu)化
Carson帶你學Android:動畫
歡迎關注Carson_Ho的簡書
不定期分享關于安卓開發(fā)的干貨统屈,追求短、平牙躺、快鸿吆,但卻不缺深度。