博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android-AsyncTask及UncaughtExceptionHandler捕获全局性异常(ANR、FC)
阅读量:68 次
发布时间:2019-02-26

本文共 8271 字,大约阅读时间需要 27 分钟。

 为了不阻塞UI线程(亦称主线程),提高应用的响应性,我们经常会使用新开线程的方式,异步处理那些导致阻塞的任务(如要了解Android异步处理的实现方式和原理,请先
异步处理系列文章索引》)。

 AsyncTask是Android为我们提供的方便编写异步任务的工具类,但是,在了解AsyncTask的实现原理之后,发现AsyncTask并不能满足我们所有的需求,使用不当还有可能导致应用FC。

 》AsyncTask类包含一个全局静态的线程池,线程池的配置参数如下:
private static final int CORE_POOL_SIZE =5;//5个核心工作线程  
   private static final int MAXIMUM_POOL_SIZE = 128;//最多128个工作线程  
   private static final int KEEP_ALIVE = 1;//空闲线程的超时时间为1秒  
  
   private static final BlockingQueue<Runnable> sWorkQueue = 
           new LinkedBlockingQueue<Runnable>(10);//等待队列  
   private static final ThreadPoolExecutorsExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE, 
           MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue,sThreadFactory);//线程池是静态变量,所有的异步任务都会放到这个线程池的工作线程内执行。 

》 问题:Android的设备一般不超过2个cpu核心,过多的线程会造成线程间切换频繁,消耗资源。

。。线程池中已经有128个线程,缓冲队列已满,如果此时向线程提交任务,将会抛出RejectedExecutionException。

问题:抛出的错误不catch的话会导致程序FC。

AsyncTask可能存在新开大量线程消耗系统资源和导致应用FC的风险,因此,我们需要根据自己的需求自定义不同的线程池。

(1)、AsyncTask是封装好的线程池,比起Thread+Handler的方式,AsyncTask在操作UI线程上更方便,因为onPreExecute()、onPostExecute()及更新UI方法onProgressUpdate()均运行在主线程中,这样就不用Handler发消息处理了; 

(2)、我不太同意封装好就会影响性能的说法,在我实际的运用中,真正的缺点来自于AsyncTask的全局线程池只有5个工作线程,也就是说,一个APP如果运用AsyncTask技术来执行线程,那么同一时间最多只能有5个线程同时运行,其他线程将被阻塞(注:不运用AsyncTask执行的线程,也就是自己new出来的线程不受此限制),所以AsyncTask不要用于多线程取网络数据,因为很可能这样会产生阻塞,从而降低效率。

》能否同时并发100+asynctask呢? 

      AsyncTask用的是线程池机制,容量是128,最多同时运行5个core线程,剩下的排队。 

》 导致出现Force Close的原因有很多,常见的有比如空指针啦,类没有找到啦,资源没找到,就连Android API使用的顺序错误也可能导致(比如     setContentView()之前进行了findViewById()操作)

     Force Close有的人说可以用来让应用完全退出 而故意导致这个问题,让程序强制关闭,这种做法我还是不常用。

如何避免弹出Force Close窗口 可以实现Thread.UncaughtExceptionHandler接口的uncaughtException方法 代码如下

    importjava.lang.Thread.UncaughtExceptionHandler;

    import android.app.Application;

    public class MyApplication  extends Application implements UncaughtExceptionHandler {

    @Override

    public voidonCreate() {

      // TODOAuto-generated method stub

      super.onCreate();

    }

    @Override

    public void  uncaughtException(Thread thread, Throwable ex) {

      thread.setDefaultUncaughtExceptionHandler(this)

    }

    }

想要哪个线程可以处理未捕获异常,thread.setDefaultUncaughtExceptionHandler( this); 这句代码都要在那个线程中执行一次

我们需要进行全局性的异常捕获,那么如何捕获全局异常呢?

答案是 UncaughtExceptionHandler+Thread.setDefaultUncaughtExceptionHandler

1.UncaughtExceptionHandler是未捕获异常的处理接口,该类率先捕获异常

 UncaughtExceptionHandler: 线程未捕获异常控制器是用来处理未捕获异常的。                              如果程序出现了未捕获异常默认情况下则会出现强行关闭对话框                             实现该接口并注册为程序中的默认未捕获异常处理                              这样当未捕获异常发生时,就可以做些异常处理操作                             例如:收集异常信息,发送错误报告 等。

对于这个接口,我们需要进行实现

public class AppCrashHandler implements UncaughtExceptionHandler {		private Context mContext;    private Thread.UncaughtExceptionHandler mDefaultHandler;    /**防止多线程中的异常导致读写不同步问题的lock**/    private Lock lock = null;	/**本地保存文件日志**/	private final String CRASH_REPORTER_EXTENSION = ".crash";    /**日志tag**/    private final String STACK_TRACE = "logStackTrance";    /**保存文件名**/	private final String crash_pref_path ="app_crash_pref.xml";    private AppCrashHandler()    {        lock = new ReentrantLock(true);       }    /**     * 获得单例对象     * @param context     * @return AppCrashHandler     */	public static AppCrashHandler shareInstance(Context context){		 AppCrashHandler crashhandler = AppCrashHandler.InstanceHolder.crashHandler;		 crashhandler.initCrashHandler(context);		 return crashhandler;	}	/**	 * 使用初始化方法初始化,防止提前初始化或者重复初始化	 * @param cxt	 */	private void initCrashHandler(Context cxt)	{		if(!hasInitilized()){			mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();			Thread.setDefaultUncaughtExceptionHandler(this);			mContext = cxt; 		}		}	public interface InstanceHolder	{		public static AppCrashHandler crashHandler = new AppCrashHandler();	}	@Override	public void uncaughtException(Thread thread, Throwable ex) {		 if (!handleExceptionMessage(ex) && mDefaultHandler != null) {  	            // 如果用户没有处理则让系统默认的异常处理器来处理  	            mDefaultHandler.uncaughtException(thread, ex);  	        } else {  	            try {  	                Thread.sleep(3000);  	            } catch (InterruptedException e) {  	                Log.e(STACK_TRACE, "Error : ", e);  	            }  	            android.os.Process.killProcess(android.os.Process.myPid());  	            System.exit(10);  	     }  	}	/**      * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成. 开发者可以根据自己的情况来自定义异常处理逻辑      * @param ex      * @return true:如果处理了该异常信息;否则返回false      */      private boolean handleExceptionMessage(Throwable ex)     {          if (ex == null)         {              return false;          }          // 使用Toast来显示异常信息          new Thread() {              @Override              public void run() {                  // Toast 显示需要出现在一个线程的消息队列中                  Looper.prepare();                  Toast.makeText(mContext, "程序出错啦,即将退出", Toast.LENGTH_LONG).show();                  Looper.loop();              }          }.start();          String fileName = mContext.getPackageName()+"-"+"appCrash-Exception"+ CRASH_REPORTER_EXTENSION;          String crashFileName = saveExceptionToFile(ex,fileName); 		SharedPreferences.Editor editor = mContext.getSharedPreferences(crash_pref_path , Context.MODE_PRIVATE).edit();		editor.putString(STACK_TRACE, crashFileName);		editor.commit();		Log.d(STACK_TRACE, "errorLogPath="+crashFileName);        return true;      }      /**     * 是否已初始化     * @return     */    public boolean hasInitilized()    {    	return mContext!=null;    }    /**      * 保存错误信息到文件中      * @param ex      * @return      * @throws IOException      */  	private String saveExceptionToFile(Throwable ex,String fileName) 	{  		File saveFile = null;		PrintWriter printWriter = null;              try {            	lock.tryLock();				if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))				{					File sdCardDir = Environment.getExternalStorageDirectory();//获取SDCard目录				    saveFile = new File(sdCardDir, fileName);   				}else{					 saveFile =new File(mContext.getFilesDir(),fileName);				}				if(!saveFile.exists())			    {			    	saveFile.createNewFile();			    }			    printWriter = new PrintWriter(saveFile);			    String result = formatException(ex);			    printWriter.write(result);			    printWriter.flush();				Log.e("CrashException", result);            }catch(Exception e){				e.printStackTrace();			} finally{				if(printWriter!=null)				{					printWriter.close();				}				lock.unlock();			}                       return saveFile!=null?saveFile.getAbsolutePath():null;      }      /**     * 格式化异常信息     * @param e     * @return     */    @SuppressLint("SimpleDateFormat")	private  String  formatException(Throwable e)	{    	StringBuilder sb = new StringBuilder();		StackTraceElement[] stackTrace = e.getStackTrace();		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");		if (stackTrace!=null)		{			String  timeStramp =  sdf.format(new Date(System.currentTimeMillis()));			String format = String.format("DateTime:%s\nExceptionName:%s\n\n",timeStramp,e.getLocalizedMessage());			sb.append(format);			for (int i = 0; i < stackTrace.length; i++) 			{				StackTraceElement traceElement = stackTrace[i];				String 	fileName 	= traceElement.getFileName();				int 	lineNumber 		= traceElement.getLineNumber();				String 	methodName 	= traceElement.getMethodName();				String 	className = traceElement.getClassName();				sb.append(String.format("%s\t%s[%d].%s \n",className,fileName,lineNumber,methodName));			}			sb.append(String.format("\n%s",e.getMessage()));			Writer stringWriter = new StringWriter();			PrintWriter pw = new PrintWriter(stringWriter);			e.printStackTrace(pw);			pw.flush();			pw.close();						sb.append("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");			sb.append(stringWriter.toString());		}		return sb.toString();	}}

这里只保存了文件,一般来说,当app第二次启动时我们需要将该文件上传到网络,时间不是很充裕,这里上传暂时不贴代码了,时间充裕的话会及时补充,请保持关注吧

2.初始化,监听全局异常信息,这里需要继承Application,并替换系统默认的Application

public class BaseApplication extends Application {	private  static BaseApplication instance = null;	private AppCrashHandler appCrashHandler = null;	@Override	public void onCreate() {		synchronized (this)		{			if(instance==null)			{				instance = this;			}			appCrashHandler =  AppCrashHandler.shareInstance(instance);		}		super.onCreate();		}	@Override	public void onConfigurationChanged(Configuration newConfig) {		super.onConfigurationChanged(newConfig);	}	}

修改清单文件

        
 

转载地址:http://awfz.baihongyu.com/

你可能感兴趣的文章