跟閃退、程序崩潰說88


《程序的需求層次》這篇文章,我們可以看到程序可用性的重要性。當然,不讀這篇文章,我們也知道,它很重要。但是,可能沒覺得那么重要。在我寫過的所有程序里面,就有這么一個程序,由於前期不怎么重視異常處理,結果后期經常崩潰,導致公司所有人都對它不放心,即使后來已經改善了很多,也總會把它作為一個事提起(作為開發的我們,心里會很不爽的啊!)。更嚴重的是,程序崩潰可能丟失用戶數據,對用戶來說可能是災難性的。我也經常在下載App的時候,看用戶評論,里面有各種對閃退的恐懼、唾棄以及無奈。所以在這篇文章,我們主要看看在android 開發中,怎么避免程序崩潰。

在進入具體的討論之前,我想說說提高可用性的基本策略。我覺得可以從三個方面入手:

  • 減錯
  • 容錯
  • 糾錯

減錯就是減少錯誤,這部分應該是大家做得最多的,我們一般都有專門的測試部門,來幫忙發現錯誤,然后更改,目的就是減少產品正式面市后的錯誤。容錯,就是在錯誤發生后,盡可能的讓程序其他部分正常運行的策略。今天,我們說的避免程序crash就是屬於這種類型。它主要有三個子策略:限定范圍、回滾、使用備份。最后一個是糾錯,這個大家用的就更少了,就是程序發現錯誤,然后自動把錯誤解決的策略。也包括三個主要的子策略:重試、檢查依賴、重置。廢話就這么多了,更具體的以后有時間再說。今天我們主要是使用限定范圍策略來和閃退say byebye啦。

首先,我們來看看哪些異常會導致App崩潰。我們知道java異常有兩種,一種是Error,一種是Exception。Error是我們自己處理不來的,一般是虛擬機錯誤或者內存錯誤。還好,這種錯誤發生的概率不是很大。更多是Exception, 並且是運行時異常,導致的程序掛掉(還有一種Exception是非運行時異常,這個一般我們都會被強制處理,不然編譯器會報錯,所以不會導致程序掛掉)。下面的程序就是模擬程序崩潰的App。

它有4個按鈕,前三個按鈕點擊后,會在UI線程、異步任務、自定義線程里向外拋數組越界異常(一種運行時異常)。

 

public void onExceptionBt(View view)
{
  int[] array = new int[]{1,2,3};
  int a1 = array[5]; //這里會拋出異常
}

 

結果都導致了程序崩潰。

第四個按鈕對應的是用try – catch語句捕獲UI線程里的異常,限制它的擴散。

public void onExceptionInCatchBt(View view)
{
    try
    {
        onExceptionBt(view);
    }
    catch(Exception e)
    { 
        ExceptionUtil.log(e, this);
    }
}

 

結果,第四個按鈕點擊后,程序沒有崩潰,而是我們打印的出錯信息。

所以,我們的主要策略之一就是利用try – catch,限定異常范圍。

有人就說了,在UI線程的每個入口方法里寫上try-catch,是不是有點苦逼啊。的確是,為了簡化,我的建議是自定義一個Handler,重寫它的dispatchMessage方法,把try-catch放在里面。

package com.lanbeetou.android.avaiable;

import android.content.Context;
import android.os.Handler;
import android.os.Message;

public class ExceptionHandler extends Handler {
    
    private static ExceptionHandler handler;
    
    private Context context;
    private ExceptionHandler(Context context)
    {
        super();
        this.context = context;
    }
    
    public static ExceptionHandler getExceptionHandler(Context context)
    {
        if(handler == null)
        {
            handler = new ExceptionHandler(context);
        }
        return handler;
    }
    
    @Override
    public void dispatchMessage(Message msg) {
       try
       {
           super.dispatchMessage(msg);
       }
       catch(Exception e)
       {
           ExceptionUtil.log(e, context);
       }
    }
}

這樣,入口方法的調用都轉發給自定義的Handler,由Handler統一處理異常。

public void onExceptionBt(View view)
{
     ExceptionHandler.getExceptionHandler(this).post(new Runnable() {
            @Override
            public void run() {
                int[] array = new int[]{1,2,3};
                int a1 = array[5];
            }
      });
 }

還嫌麻煩?有更好的辦法?大家湊和着用吧。

好消息是異步操作或者自定義線程的異常捕獲,不用這么苦逼,可以通過Thread.setUncaughtExceptionHandler方法來設置默認的未捕獲異常處理方法。

package com.lanbeetou.android.avaiable;

import java.lang.Thread.UncaughtExceptionHandler;

import android.content.Context;

public class UncaughtHandler implements UncaughtExceptionHandler {

    private Context context;

    public void init(Context context) {

        this.context = context;
        Thread.setDefaultUncaughtExceptionHandler(this);

    }

    @Override
    public void uncaughtException(Thread arg0, Throwable e) {
        ExceptionUtil.log(e, context);
    }

}

 

設置成功后,比如說此時發生了一個異常,並且沒有捕獲,則該異常所在的線程會被終止,同時該異常處理方法會被調用。所以對於UI線程來說,這個方法不適用,不然UI線程被終止,和崩潰無異。而對應其他線程,雖然被停止,但是Ui線程還是活的,所以還有繼續工作的可能。如果這些其它線程也很重要,不能隨便終止,那么還是乖乖采用第一種方法吧。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM