Xamarin.Android-捕獲未處理異常(全局異常)

1、前言java

android中若是出現了未處理的異常,程序會閃退,這是很是很差的用戶體驗,不少用戶會所以卸載APP,所以未處理的異常是應該盡力避免的。android

有些很難避免的異常(如:IO、網絡等),應在代碼中進行捕捉並作相應的處理,以阻止程序崩潰閃退。git

可是「沒有任何程序是完美的」,何況各式各樣的android終端也大大增長了異常的出現機率,就連強大的QQ、微信等不也會閃退嘛!github

這時就須要全局捕獲未處理的異常,並進行處理。(注意:本文中的處理方式並不能阻止APP閃退微信

處理方式:收集異常信息、當前場景[時間、硬件參數],在合適的時機上傳至服務端網絡

做用:一、便於下一版本修復bug           二、便於幫助用戶解決異常形成的困難app

 

2、參照java android的方式(這是坑啊)ide

xamarin.android在不少時候均可以參考java android的代碼,所以我按照java android的方式實現了一下「捕獲未處理異常」函數

    [Obsolete]
    public class CrashHandler:Thread.IUncaughtExceptionHandler
    {
        //系統默認的UncaughtException處理類 
        private Thread.IUncaughtExceptionHandler mDefaultHandler;
        //CrashHandler實例
        private static CrashHandler INSTANCE = new CrashHandler();
        //程序的Context對象
        private Context mContext;

        /// <summary>
        /// 保證只有一個CrashHandler實例
        /// </summary>
        private CrashHandler()
        {
        }

        /// <summary>
        /// 獲取CrashHandler實例 ,單例模式
        /// </summary>
        /// <returns></returns>
        public static CrashHandler GetInstance()
        {
            return INSTANCE;
        }

        public IntPtr Handle
        {
            get { return Thread.CurrentThread().Handle; }
        }

        public void Dispose()
        {
            this.Dispose();
        }

        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="context"></param>
        public void Init(Context context)
        {
            mContext = context;
            //獲取系統默認的UncaughtException處理器
            mDefaultHandler = Thread.DefaultUncaughtExceptionHandler;
            //設置該CrashHandler爲程序的默認處理器
            Thread.DefaultUncaughtExceptionHandler = this;
        }

        ///當UncaughtException發生時會轉入該函數來處理
        public void UncaughtException(Thread thread, Throwable ex)
        {
            if (!HandleException(ex) && mDefaultHandler != null)
            {
                //若是用戶沒有處理則讓系統默認的異常處理器來處理
                mDefaultHandler.UncaughtException(thread, ex);
            }
            else
            {
                //退出程序
                Android.OS.Process.KillProcess(Android.OS.Process.MyPid());
                JavaSystem.Exit(1);
            }
        }

        /// <summary>
        /// 異常處理
        /// </summary>
        /// <param name="ex"></param>
        /// <returns>若是處理了該異常信息返回true; 不然返回false.</returns>
        private bool HandleException(Throwable ex)
        {
            if (ex == null)
            {
                return false;
            }

            //處理程序(記錄 異常、設備信息、時間等重要信息)
            //************


            //提示
            Task.Run(() =>
            {
                Looper.Prepare();
                //能夠換成更友好的提示
                Toast.MakeText(mContext, "很抱歉,程序出現異常,即將退出.", ToastLength.Long).Show();
                Looper.Loop();
            });

            //停一會,讓前面的操做作完
            System.Threading.Thread.Sleep(2000);

            return true;
        }
    }
View Code
[Application(Label = "MyApplication")]
    public class MyApplication : Application
    {
        public MyApplication(IntPtr javaReference, JniHandleOwnership transfer)
            : base(javaReference, transfer)
        {
        }

        public override void OnCreate()
        {
            base.OnCreate();

            CrashHandler crashHandler = CrashHandler.GetInstance();
            crashHandler.Init(ApplicationContext);
        }

    }
View Code

經過實現Java.Lang.Thread.IUncaughtExceptionHandler接口自定義一個異常處理類CrashHandler,並替換掉Java.Lang.Thread.DefaultUncaughtExceptionHandler,oop

當UncaughtException發生時會轉入CrashHandler類中的UncaughtException方法中,在此處進行異常處理。

而後製造一個異常throw new Exception("我是異常!");,本覺得程序會進入CrashHandler類中的UncaughtException方法中,結果卻不是,也就是說這種方式失敗了,爲何? google後發現,IUncaughtExceptionHandler只能捕獲到Dalvik runtime的異常,mono runtime中的C#異常,這個不起做用。

所以這種方式不行,坑坑坑!

 

3、正確的捕捉方式

[Application(Label = "MyApplication")]
    public class MyApplication : Application
    {
        public MyApplication(IntPtr javaReference, JniHandleOwnership transfer)
            : base(javaReference, transfer)
        {
        }

        public override void OnCreate()
        {
            base.OnCreate();

            //註冊未處理異常事件
            AndroidEnvironment.UnhandledExceptionRaiser += AndroidEnvironment_UnhandledExceptionRaiser;

            //CrashHandler crashHandler = CrashHandler.GetInstance();
            //crashHandler.Init(ApplicationContext);
        }

        protected override void Dispose(bool disposing)
        {
            AndroidEnvironment.UnhandledExceptionRaiser -= AndroidEnvironment_UnhandledExceptionRaiser;
            base.Dispose(disposing);
        }

        void AndroidEnvironment_UnhandledExceptionRaiser(object sender, RaiseThrowableEventArgs e)
        {
            UnhandledExceptionHandler(e.Exception, e);
        }

        /// <summary>
        /// 處理未處理異常
        /// </summary>
        /// <param name="e"></param>
        private void UnhandledExceptionHandler(Exception ex, RaiseThrowableEventArgs e)
        {
            //處理程序(記錄 異常、設備信息、時間等重要信息)
            //**************

            //提示
            Task.Run(() =>
                {
                    Looper.Prepare();
                    //能夠換成更友好的提示
                    Toast.MakeText(this, "很抱歉,程序出現異常,即將退出.", ToastLength.Long).Show();
                    Looper.Loop();
                });

            //停一會,讓前面的操做作完
            System.Threading.Thread.Sleep(2000);

            e.Handled = true;
        }
    }
View Code

註冊未處理異常事件AndroidEnvironment.UnhandledExceptionRaiser += AndroidEnvironment_UnhandledExceptionRaiser; 在AndroidEnvironment_UnhandledExceptionRaiser中進行異常處理。

製造一個異常throw new Exception("我是異常!");,妥妥的進入了AndroidEnvironment_UnhandledExceptionRaiser,OK,成功!

 

說明:捕獲異常後的具體處理,無非就是讀取硬件信息、時間、異常信息,並保存至本地,在合適的時機上傳至服務端,爲了突出重點,我在這裏就不實現了。

 

源碼下載 

https://github.com/jordanqin/CatchException

 

參考:

http://forums.xamarin.com/discussion/4576/application-excepionhandler
http://blog.csdn.net/liuhe688/article/details/6584143

 

若是你以爲文章對你有幫助,能夠點擊旁邊的「推薦」按鈕,這樣會讓更多須要的人有機會看到

相關文章
相關標籤/搜索