[Android騷操作] RuntimeException與全局異常捕獲的巧妙結(jié)合

20170701100822333.jpg

前言

由RuntimeException的使用延申和滿(mǎn)腦子騷操作的Android開(kāi)發(fā)人員陰差陽(yáng)錯(cuò)的被全局異常捕獲拘留直到Application認(rèn)領(lǐng)才知道大水沖了龍王廟的Android開(kāi)發(fā)人員跟全局異常捕獲原來(lái)是遠(yuǎn)方親戚

這方法我已經(jīng)用上癮了

android_exception_use.gif

代碼POU析

實(shí)際上從上圖看,表面上是看不出來(lái)什么的,但是其內(nèi)容與處理方式已經(jīng)是天翻地覆的變化了,至于怎么天翻地覆,我們來(lái)看看代碼就知道了
當(dāng)然我只貼出部分核心代碼,上圖的進(jìn)入主界面的按鈕事件監(jiān)聽(tīng)在這里
事先聲明一下,第一個(gè)界面是 MainActivity ,第二個(gè)界面是 SignInActivity 第三個(gè)界面則是登陸后的 HomeActivity

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        /* 設(shè)置點(diǎn)擊事件 */
        findViewById(R.id.start_to_home).setOnClickListener(new View.OnClickListener() {

            /**
             * 你會(huì)看到很神奇的一幕,當(dāng) {@link com.var.intercept_android.data.Storage} 中的
             * {@link com.var.intercept_android.data.Storage#isSign} 為false的時(shí)候.跳轉(zhuǎn)到
             * {@link HomeActivity} 的代碼并沒(méi)有執(zhí)行
             */
            @Override
            public void onClick(View v) {

                /* 首先檢查用戶(hù)是否登陸 */
                Sign.isSign();

                /* 然后跳轉(zhuǎn)到HomeActivity */
                startActivity(new Intent(MainActivity.this, HomeActivity.class));
            }
        });
    }
}

然而你看到的是,我只是在上邊調(diào)用了 Sign 類(lèi)的 isSign() 方法,至于 isSign() 怎么實(shí)現(xiàn)的,我們等會(huì)在看
其實(shí)看到這里,你已經(jīng)發(fā)現(xiàn)有些不對(duì)勁了,是吧?正常情況下 isSign() 執(zhí)行完是要執(zhí)行跳轉(zhuǎn)到主界面的,然而并沒(méi)有執(zhí)行跳轉(zhuǎn)到主界面的那句代碼,這是為什么呢?我們?cè)賮?lái)看看 isSing() 方法里邊用了什么 騷操作

public class Sign {

    /**
     * 檢查是否登陸
     */
    public static void isSign() {
        /* 如果未登錄 */
        if (!Storage.isSign) {
            /* 拋出未登錄異常 */
            throw new NotSignException();
        }
    }
}

對(duì)的,你沒(méi)看錯(cuò) isSign() 方法拋了個(gè)名為 NotSignException 的自定義異常,那我們?cè)賮?lái)看看 NotSignException 是怎么寫(xiě)的

public class NotSignException extends RuntimeException {

    /**
     * 構(gòu)造方法,一個(gè)就夠了
     */
    public NotSignException() {
        super("用戶(hù)未登錄");
    }
}

居然是繼承的 RuntimeException ,想不到吧,這樣的程序居然沒(méi)崩潰?

原理POU析

靈感來(lái)源

事實(shí)上,這個(gè)騷操作是這樣來(lái)的,最開(kāi)始,我準(zhǔn)備做一個(gè)檢查用戶(hù)是否登陸的功能,但是呢,用返回值為布爾類(lèi)型的方法來(lái)做驗(yàn)證感覺(jué)太繁瑣,并且吧,if和else寫(xiě)多了也并不怎么美觀,所以尋思著怎么讓代碼執(zhí)行到某一句的時(shí)候不繼續(xù)執(zhí)行了

實(shí)現(xiàn)原理

眾所周知,Java中的異常機(jī)制,只要發(fā)生異常,那從發(fā)生異那句代碼開(kāi)始到整個(gè)方法結(jié)束的代碼都不會(huì)執(zhí)行了,所以我就想到了使用異常的方式來(lái)簡(jiǎn)化代碼的騷操作,不過(guò),這個(gè)異常也是要有講究的,他不能在編譯的時(shí)候就直接被編譯器檢查出來(lái)了,所以要隱藏起來(lái),那就得使用Error或者RuntimeException,不過(guò)Error是不可捕獲的異常,那么就只能使用RuntimeException及其子類(lèi)

后遺癥處理

既然拋出了異常,那在Java中不處理的就會(huì)拋給虛擬機(jī),那虛擬機(jī)就會(huì)停掉,所以需要寫(xiě)一個(gè)異常捕獲

延申問(wèn)題

由于使用環(huán)境是在Android上,因?yàn)锳ndroid平臺(tái)的特殊機(jī)制,所以通常的異常捕獲是行不通的,那怎么處理呢?
Android的應(yīng)用程序里邊有一個(gè)叫 Looper 的好東西,我們可以通過(guò)直接接管 Looper 然后捕獲 Looper.loop(); 的異常來(lái)捕獲主線(xiàn)程的異常信息,然后其他的異常信息則是通過(guò)以往的全局異常捕獲來(lái)實(shí)現(xiàn),我們來(lái)看看全局異常捕獲是怎么實(shí)現(xiàn)的

public class GlobalCrashCapture implements Thread.UncaughtExceptionHandler {

    /* 靜態(tài)實(shí)例 */
    private static GlobalCrashCapture instance;
    /* 應(yīng)用程序的上下文參數(shù) */
    private Application context;
    /* looper狀態(tài) */
    private boolean running = true;

    /**
     * 構(gòu)造方法
     */
    private GlobalCrashCapture() {
    }

    /**
     * 初始化
     *
     * @param context 上下文參數(shù)
     */
    public void init(Application context) {
        this.context = context;
        this.looperException();
        Thread.setDefaultUncaughtExceptionHandler(this);
    }

    /**
     * 獲取當(dāng)前對(duì)象的靜態(tài)實(shí)例
     *
     * @return 返回 {@link GlobalCrashCapture} 對(duì)象
     */
    public static GlobalCrashCapture instance() {
        if (GlobalCrashCapture.instance == null) {
            GlobalCrashCapture.instance = new GlobalCrashCapture();
        }
        return GlobalCrashCapture.instance;
    }

    /**
     * 重點(diǎn)在這里,正常情況下主線(xiàn)程的異常就被捕獲也會(huì)導(dǎo)致虛擬機(jī)停止,為了不讓虛擬機(jī)停止(簡(jiǎn)單來(lái)說(shuō)是不讓APP崩潰)
     * 最好的方法就是接管Android的Looper,然后捕獲異常
     * <p>
     * 接管Android的Looper并捕獲異常
     */
    private void looperException() {
        new Handler(Looper.getMainLooper()).post(new Runnable() {
            @Override
            public void run() {
                while (running) {
                    try {
                        Looper.loop();
                    } catch (Throwable e) {
                        /* 如果是自己定義的異常 */
                        if (e instanceof NotSignException) {
                            /* 那就自己去處理吧 */
                            handleNotSignException();
                        } else {
                            /* 如果不是自己拋出的異常,那就用全局異常捕獲去處理 */
                            handleException(e);
                        }
                    }
                }
            }
        });
    }

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        this.handleException(e);
    }

    /**
     * 默認(rèn)異常信息處理
     *
     * @param ex 異常信息
     */
    private void handleException(final Throwable ex) {
        ex.printStackTrace();
        new Thread() {
            @Override
            public void run() {

                /* 彈個(gè)Toast告訴你程序出問(wèn)題了 */
                Looper.prepare();
                if (BuildConfig.DEBUG) {
                    Toast.makeText(context, "哦豁,程序問(wèn)題咯: " + ex.getLocalizedMessage(), Toast.LENGTH_LONG).show();
                }
                Looper.loop();

                /* 等三秒 */
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                /* 然后結(jié)束應(yīng)用程序 */
                android.os.Process.killProcess(android.os.Process.myPid());
                System.exit(0);
            }
        }.start();
    }

    /**
     * 未登錄異常處理
     */
    private void handleNotSignException() {
        /* 其實(shí)你可以定義一個(gè)接口,然后使用回調(diào)的方式處理,此處為了簡(jiǎn)單明了的說(shuō)明問(wèn)題,不做詳細(xì)解釋 */
        context.startActivity(new Intent(context, SignInActivity.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
    }
}

首先是保證全局異常捕獲的單例,然后是一貫的實(shí)現(xiàn) Thread.UncaughtExceptionHandler 接口來(lái)捕獲異常,重點(diǎn)在 looperException() 方法, looperException() 方法通過(guò)接管 Looper 來(lái)實(shí)現(xiàn)異常捕獲,并將捕獲到的異常進(jìn)行處理,然后將捕獲到的 NotSignException 異常信息交由 handleNotSignException() 方法處理,從而達(dá)到異常不拋給虛擬機(jī)又實(shí)現(xiàn)了異常信息收集和代碼執(zhí)行截?cái)嗟墓δ?是不是很爽?

最重要的一點(diǎn)是,千萬(wàn)要記得在你的Application的onCeate()方法中初始化全局異常捕獲,否則你的應(yīng)用程序點(diǎn)哪那崩,加上這句就是點(diǎn)哪都不會(huì)崩

public class MainApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        /* 初始化全局異常捕獲,這個(gè)很重要,不加這句你的應(yīng)用就是點(diǎn)哪哪崩 */
        GlobalCrashCapture.instance().init(this);
    }
}

14988115776887403.png

上邊的代碼,只要 Storage 中的 isSignfalse , 你在任何地方調(diào)用 Sign.isSign() 方法,其后邊的代碼都不會(huì)執(zhí)行,用在 登陸驗(yàn)證,輸入檢查,接口請(qǐng)求,Bean對(duì)象非空驗(yàn)證等 地方簡(jiǎn)直就是爽歪歪,當(dāng)然適用場(chǎng)景不止這些,更多的還有待小伙伴們?nèi)ヌ剿?/p>

干貨分享

上邊的源碼我已經(jīng)上傳Github,有需要的小伙伴可以自己拉下來(lái)研究研究,注釋到都還是有的,只不過(guò)這只是寫(xiě)個(gè)Demo而已,有些地方代碼沒(méi)寫(xiě)很詳細(xì),也就簡(jiǎn)單的表述一下我要實(shí)現(xiàn)的功能而已
源碼地址: https://github.com/scvax/Intercept_Android

如果有疑問(wèn),可以在本文下方留言,在我的能力范圍內(nèi)我還是很樂(lè)意解答的,我上簡(jiǎn)書(shū)很勤的哦~~

14988115779718031.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,351評(píng)論 25 708
  • 用兩張圖告訴你,為什么你的 App 會(huì)卡頓? - Android - 掘金 Cover 有什么料? 從這篇文章中你...
    hw1212閱讀 14,119評(píng)論 2 59
  • 【Android Handler 消息機(jī)制】 前言 在Android開(kāi)發(fā)中,我們都知道不能在主線(xiàn)程中執(zhí)行耗時(shí)的任務(wù)...
    Rtia閱讀 5,105評(píng)論 1 28
  • 懶人活該錯(cuò)過(guò)愛(ài)情嗎,我懶得說(shuō),以為你都懂,你也懶得說(shuō),以為我會(huì)懂,然而,錯(cuò)過(guò)就是那么容易…… ...
    茯苓濃湯閱讀 817評(píng)論 4 4
  • 思維導(dǎo)圖是孩子們愛(ài)上學(xué)習(xí),喜歡上學(xué)習(xí)的法寶。孩子們?nèi)绻麑W(xué)習(xí)了思維導(dǎo)圖,并能掌握,在實(shí)際學(xué)習(xí)生活中靈活運(yùn)用,家長(zhǎng)和老...
    徐露桐閱讀 333評(píng)論 1 0

友情鏈接更多精彩內(nèi)容