• 跟闪退、程序崩溃说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线程还是活的,所以还有继续工作的可能。如果这些其它线程也很重要,不能随便终止,那么还是乖乖采用第一种方法吧。

     

  • 相关阅读:
    HTC G7 搜索和感光按键修改
    Delphi开源组件SynEdit
    (转)Delphi获取windows系统版本信息
    TDateTime转UTC的时间差
    Windows7 C盘无法读写文件
    Convert UTC string to TDatetime in Delphi
    delphi抓全屏图,游戏窗口,游戏Client窗口
    ADO Table Locate
    Delphi与管道操作
    Delphi从UTC (GMT)返回时差
  • 原文地址:https://www.cnblogs.com/simplevita/p/3859277.html
Copyright © 2020-2023  润新知