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