摘要:退出應用的幾種方式退出應用的方式很多,常見的也就下面四種。當發生時會轉入該函數來處理如果用戶沒有處理則讓系統默認的異常處理器來處理如果需要重啟重啟應用,得使用重啟應用結束應用我們的方法用于彈出和收集信息。
寫在前面
這是最近一些朋友問我的問題,我把它整理成了一個庫,供大家享用,GitHub 地址:https://github.com/nanchen2251/AppManager
從四個應用場景說起退出應用
相信各位朋友或多或少都會有遇到過需要在某個特定的地方退出應用的需求,這個場景一定非常普遍。
崩潰后重啟
程序總是無法做到盡善盡美,有時候你也不知道因為什么原因導致了 APP 的崩潰,這無疑是非常糟糕的用戶體驗。這時候我們可以采用重啟機制來增強用戶舒適體驗感。
莫名其妙重啟
然而心細的小伙伴肯定會發現,在部分手機上會出現莫名其妙的崩潰后重啟(后面會講原因),而且最要命的是,假設你有三個 Activity,他們分別是 Act1, Act2, Act3,它們的啟動順序是 Act1 -> Act2 -> Act3,而如果在 Act3 發生了崩潰,這時候極有可能應用重啟后進入的是 Act2,而 Act2 中需要某個來源于 Act1 (或者在 Act1 中通過接口獲取) 的參數,當沒有這個參數的時候會引發崩潰(或者數據不全)。這時候你可能最直觀的想法就是禁止應用重啟,但或許這并不是最佳的方式。
崩潰時彈出一個對話框
在部分手機上,當崩潰的時候,會彈出一個提示對話框。在這種情況下,用戶只有點擊 “強行關閉” 來結束程序。當該對話框出現,對用戶來說是相當不友好的。或許我們可以通過某種方式攔截掉系統的處理,讓應用出錯時不再顯示它。
Andorid 退出應用的方式很多,常見的也就下面四種。
System.exit(0) 使用系統的方法,強制退出
System.exit(0) 表示的是終止程序,終止當前正在運行的?Java 虛擬機,在 Java 中我們也使用這種方式來關閉整個應用,在前期很多開發人員都是使用這種方式,我自己在開發項目過程中也用過這種方式來退出,但是有時候會在部分機型中,當退出應用后彈出應用程序崩潰的對話框,有時退出后還會再次啟動,少部分的用戶體驗不太好。但現在也依舊還會有少部分的開發人員會使用這種方式,因為使用方式很簡單,只需要在需要退出的地方加上這句代碼就行。
拋出異常,強制退出
這種方式現在基本上已經看不到了,用戶體驗比第一種方式更差,就是讓拋出異常、是系統崩潰、從而達到退出應用的效果
使用 Application 退出
目前比較常用方法之一,我們都知道 Application 是 Android 的系統組件,當應用程序啟動時,會自動幫我們創建一個 Application,而且一個應用程序只能存在一個 Application ,它的生命周期也是最長的,如果需要使用自己創建的 Application 時,這個時候我們只需要在 Androidmanifest.xml 中的
使用廣播退出
使用廣播來實現退出應用程序,其實實現的思路相對于第三種更簡單,我們編寫一個 BaseActivity,讓其他的 Activity 都繼承于它,當我需要退出時,我們就銷毀 BaseActivity,那么其他繼承與它的 Activity 都會銷毀。
四種方式的代碼也就不多提,需要的自己去Android:銷毀所有的Activity退出應用程序幾種方式
莫名其妙重啟?上面的場景中說到了,再部分手機上會出現崩潰后自動重啟的情況,這讓我們很不好控制。經本人測試,在 Android 的 API 21 ( Android 5.0 ) 以下,Crash 會直接退出應用,但是在 API 21 ( Android 5.0 ) 以上,系統會遵循以下原則進行重啟:
包含 Service,如果應用 Crash 的時候,運行著 Service,那么系統會重新啟動 Service。
不包含 Service,只有一個 Activity,那么系統不會重新啟動該 Activity。
不包含 Service,但當前堆棧中存在兩個 Activity:Act1 -> Act2,如果 Act2 發生了 Crash ,那么系統會重啟 Act1。
不包含 Service,但是當前堆棧中存在三個 Activity:Act1 -> Act2 -> Act3,如果 Act3 崩潰,那么系統會重啟 Act2,并且 Act1 依然存在,即可以從重啟的 Act2 回到 Act1。
在這樣的情況下,我們或許會有兩種需求:
崩潰后不允許重啟
崩潰后需要重啟
怎么辦翻看 API 我們發現,Java 中存在一個 UncaughtExceotionHandler 的接口,而在 Android 中我們沿用了它,我們可以采用這個接口實現我們想要的功能。
(為了方便,我把它做成了庫,傳送門:https://github.com/nanchen2251/AppManager)
首先是我們的 CrashApplication 類,因為我們崩潰的時候需要結束程序后再重啟,所以我們需要退出應用,這里我們采用上面的第三種方式。
public class CrashApplication extends Application { private ListUncaughtExceptionHandlerImplmActivityList; @Override public void onCreate() { super.onCreate(); mActivityList = new ArrayList<>(); } /** * 添加單個Activity */ public void addActivity(Activity activity) { // 為了避免重復添加,需要判斷當前集合是否滿足不存在該Activity if (!mActivityList.contains(activity)) { mActivityList.add(activity); // 把當前Activity添加到集合中 } } /** * 銷毀單個Activity */ public void removeActivity(Activity activity) { // 判斷當前集合是否存在該Activity if (mActivityList.contains(activity)) { mActivityList.remove(activity); // 從集合中移除 if (activity != null){ activity.finish(); // 銷毀當前Activity } } } /** * 銷毀所有的Activity */ public void removeAllActivity() { // 通過循環,把集合中的所有Activity銷毀 for (Activity activity : mActivityList) { if (activity != null){ activity.finish(); } } //殺死該應用進程 android.os.Process.killProcess(android.os.Process.myPid()); } }
我們當然少不了新建一個 UncaughtExceptionHandlerImpl 類去實現我們的 UncaughtExceptionHandler 接口,它必須實現我們的 uncaughtException(thread, throwable) 方法,我們接下來可以在這中間作文章。需要特別注意的是:重啟必須清除堆棧內的 Activity。
/** * 當 UncaughtException 發生時會轉入該函數來處理 */ @SuppressWarnings("WrongConstant") @Override public void uncaughtException(Thread thread, Throwable ex) { if (!handleException(ex) && mDefaultHandler != null) { // 如果用戶沒有處理則讓系統默認的異常處理器來處理 mDefaultHandler.uncaughtException(thread, ex); } else { try { Thread.sleep(2000); } catch (InterruptedException e) { Log.e(TAG, "error : ", e); } if (mIsRestartApp) { // 如果需要重啟 Intent intent = new Intent(mContext.getApplicationContext(), mRestartActivity); AlarmManager mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); //重啟應用,得使用PendingIntent PendingIntent restartIntent = PendingIntent.getActivity( mContext.getApplicationContext(), 0, intent, Intent.FLAG_ACTIVITY_NEW_TASK); mAlarmManager.set(AlarmManager.RTC, System.currentTimeMillis() + mRestartTime, restartIntent); // 重啟應用 } // 結束應用 ((CrashApplication) mContext.getApplicationContext()).removeAllActivity(); } }
我們的 handleException(throwable) 方法用于彈出 Toast 和收集 Crash 信息。
/** * 自定義錯誤處理,收集錯誤信息,發送錯誤報告等操作均在此完成 * * @param ex * @return true:如果處理了該異常信息;否則返回 false */ private boolean handleException(final Throwable ex) { if (ex == null) { return false; } // 使用 Toast 來顯示異常信息 new Thread() { @Override public void run() { Looper.prepare(); Toast.makeText(mContext, getTips(ex), Toast.LENGTH_LONG).show(); Looper.loop(); } }.start(); // 如果用戶不賦予外部存儲卡的寫權限導致的崩潰,會造成循環崩潰 // if (mIsDebug) { // // 收集設備參數信息 // collectDeviceInfo(mContext); // // 保存日志文件 // saveCrashInfo2File(ex); // } return true; }封裝好的使用 1、添加依賴
allprojects { repositories { ... maven { url "https://jitpack.io" } } }
dependencies { compile "com.github.nanchen2251:AppManager:1.0.1" }2、在需要使用的地方使用
// 設置崩潰后自動重啟 APP UncaughtExceptionHandlerImpl.getInstance().init(this, BuildConfig.DEBUG, true, 0, MainActivity.class);3、你也可以禁止重啟
// 禁止重啟UncaughtExceptionHandlerImpl.getInstance().init(this,BuildConfig.DEBUG);
歡迎關注我的技術公眾號(公眾號搜索nanchen),每天一篇 Android 資源分享。
效果圖文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/67454.html
摘要:前言這將是一個分為兩部分,內容是關于在生產環境下,跑應用的最佳實踐。第一部分會關注安全性,第二部分則會關注性能和可靠性。關于第一部分,請參閱在生產環境下的最佳實踐安全性。 前言 這將是一個分為兩部分,內容是關于在生產環境下,跑Express應用的最佳實踐。第一部分會關注安全性,第二部分則會關注性能和可靠性。當你讀這篇文章時,會假設你已經對Node.js和web開發有所了解,并且對生產環...
摘要:二需要處理哪些異常對于前端來說,我們可做的異常捕獲還真不少。總結一下,大概如下語法錯誤代碼異常請求異常靜態資源加載異常異常異常跨域崩潰和卡頓下面我會針對每種具體情況來說明如何處理這些異常。 前端一直是距離用戶最近的一層,隨著產品的日益完善,我們會更加注重用戶體驗,而前端異常卻如鯁在喉,甚是煩人。一、為什么要處理異常?異常是不可控的,會影響最終的呈現結果,但是我們有充分的理由去做這樣的事...
閱讀 1456·2021-11-24 09:39
閱讀 3635·2021-09-29 09:47
閱讀 1580·2021-09-29 09:34
閱讀 3077·2021-09-10 10:51
閱讀 2544·2019-08-30 15:54
閱讀 3224·2019-08-30 15:54
閱讀 880·2019-08-30 11:07
閱讀 1013·2019-08-29 18:36