前言
- 在
Android
開發中,會經常存在 “一鍵退出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);
}
複製程式碼
-
優點 使用簡單 & 方便
-
缺點
- 規定 App的入口Activity採用SingleTask啟動模式
- 使用範圍侷限:只能結束當前任務棧的Activity,若出現多工棧(即採用
SingleInstance
啟動模式)則無法處理
-
應用場景 Activity單任務棧
方法2:採用Activity啟動標記位
-
原理:對入口
Activity
採用 2 標記位:Intent.FLAG_ACTIVITY_CLEAR_TOP
:銷燬目標Activity
和它之上的所有Activity
,重新建立目標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,重新建立目標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);
複製程式碼
-
優點 使用簡單 & 方便
-
缺點 使用範圍侷限:只能結束當前任務棧的Activity,若出現多工棧(即採用
SingleInstance
啟動模式)則無法處理 -
應用場景 Activity單任務棧
方法3:通過系統任務棧
-
原理:通過
ActivityManager
獲取當前系統的任務棧 & 把棧內所有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);
複製程式碼
-
優點 使用簡單、方便
-
缺點
- 使用範圍侷限:只能結束當前任務棧的Activity,若出現多工棧(即採用
SingleInstance
啟動模式)則無法處理 - 對
Android
版本要求較高:Android 5.0
以上
- 使用範圍侷限:只能結束當前任務棧的Activity,若出現多工棧(即採用
-
應用場景
Android 5.0
以上的Activity
單任務棧
b. 通過 Android 元件: BroadcastReceiver
即使用 BroadcastReceiver
廣播監聽
-
原理:在每個
Activity
裡註冊廣播接收器(響應動作 = 關閉自身);當需要退出App
時 傳送廣播請求即可 -
具體實現
步驟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時 傳送廣播請求
context.sendBroadcast(new Intent(BaseApplication.EXIT));
// 注:此處不能使用:System.exit(0);結束程式
// 原因:傳送廣播這個方法之後,不會等到廣播接收器收到廣播,程式就開始執行下一句System.exit(0),然後就直接變成執行System.exit(0)的效果了。
複製程式碼
-
優點 應用場景廣泛:兼顧單 / 多工棧 & 多啟動模式的情況
-
缺點 實現複雜:需要在每個
Activity
裡註冊廣播接收器 -
應用場景 任意情況下的一鍵退出 App,但無法終止
App
程式
所以該方法僅僅是在使用者的角度來說 “一鍵退出App”
c. 自身實現
方法1:建立 連結串列
-
原理:通過在
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();
複製程式碼
- 效果圖
-
優點 應用場景廣泛:兼顧單 / 多工棧 & 多啟動模式的情況
-
缺點 需要
Activity
經歷正常的生命週期,即建立時呼叫onCreate()
,結束時呼叫onDestroy()
因為只有這樣經歷正常的生命週期才能將
Activity
正確寫入 & 寫出 容器內
- 應用場景
任意情況下的一鍵退出
App
實現
方法2:RxBus
-
原理:使用
RxBus
當作事件匯流排,在每個Activity
裡註冊RxBus
訂閱(響應動作 = 關閉自身);當需要退出App時 傳送退出事件請求即可。 -
具體使用
步驟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時 傳送退出事件
RxBus.getInstance().post("exit");
System.exit(0);
複製程式碼
-
優點 可與
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)的區別
// 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
時刻監聽著程式**。一旦發現程式被非正常結束,它將會試圖去重啟這個程式。 -
應用場景 當任務棧只剩下當前
Activity
(即退出了其餘Activity
後),呼叫即可退出該程式,即在(步驟1)結束當前 App 所有的 Activity 後,呼叫該方法即可一鍵退出App(更多體現在結束程式上)
注: 與 “在最後一個
Activity
呼叫finish()
”的區別:finish()
不會結束程式,而上述兩個方法會
至此,關於 一鍵退出App
的兩個步驟講解完畢。
3. Demo地址
關於上述說的方法Demo
都在Carson_Ho的Github地址:一鍵退出App
4. 總結
- 在 需要實現 一鍵退出
App
功能時,實際上是需要完成2個步驟: 步驟1:一鍵結束當前App
所有的Activity
步驟2:一鍵結束當前App
程式 - 每個步驟的方法總結如下
- 下面我將繼續對
Android
中的知識進行深入講解 ,有興趣可以繼續關注Carson_Ho的安卓開發筆記