1. 程式人生 > >Xamarin.Android-捕獲未處理異常(全域性異常)

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

一、前言

android中如果出現了未處理的異常,程式會閃退,這是非常不好的使用者體驗,很多使用者會因此解除安裝APP,因此未處理的異常是應該盡力避免的。

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

但是“沒有任何程式是完美的”,況且各式各樣的android終端也大大增加了異常的出現概率,就連強大的QQ、微信等不也會閃退嘛!

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

處理方式:收集異常資訊、當前場景[時間、硬體引數],在合適的時機上傳至服務端

作用:1、便於下一版本修復bug           2、便於幫助使用者解決異常造成的困難

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

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,

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

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

因此這種方式不行,坑坑坑!

三、正確的捕捉方式

[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,成功!

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

原始碼下載 

參考:

如果你覺得文章對你有幫助,可以點選旁邊的“推薦”按鈕,這樣會讓更多需要的人有機會看到