博客
关于我
Android-AsyncTask及UncaughtExceptionHandler捕获全局性异常(ANR、FC)
阅读量:71 次
发布时间:2019-02-26

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

Android应用程序的性能优化是一个复杂的课题,而线程管理是其中关键的一环。本文将深入探讨Android异步处理中的AsyncTask及其线程池实现原理,分析其优缺点,并探讨如何在实际应用中更高效地使用线程池资源。

AsyncTask线程池配置

Android的AsyncTask类内部使用了一个静态的线程池来管理异步任务的执行。线程池的核心配置参数如下:

  • CORE_POOL_SIZE:5个核心工作线程,这是线程池的最小运行线程数。
  • MAXIMUM_POOL_SIZE:128个工作线程,这是线程池的最大运行线程数。
  • KEEP_ALIVE:空闲线程的保活时间为1秒。

线程池使用的是一个大小为10的LinkedBlockingQueue作为任务队列,线程池的创建方式如下:

private static final ThreadPoolExecutor Executor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);

线程池是静态变量,所有的异步任务都会放到这个线程池中执行,线程池的工作原理是核心线程负责处理任务,非核心任务则会被排队等待。

线程池的容量限制

Android设备通常具有2个或更少的CPU核心,因此线程池的线程数量直接影响应用程序的性能表现。线程池的容量设置过高可能导致线程之间的切换频繁,消耗资源,而线程池容量过低则可能导致任务阻塞。

线程池已满时的处理方式

当线程池的工作线程已达到最大容量(128个线程),缓冲队列已满时,继续提交任务会抛出RejectedExecutionException。此时,如果不对异常进行处理,程序可能会因为未捕获异常而导致Force Close(FC)。

AsyncTask的局限性

尽管AsyncTask提供了便捷的异步任务编写方式,但其线程池的默认配置存在以下问题:

  • 线程池的核心线程数量过少(仅5个),这意味着同一时间最多只有5个线程可以同时运行,其他任务将被阻塞。
  • 线程池的最大线程数量过多(128个),在多核设备上,这种设置可能导致线程切换过频繁,影响性能。
  • 并发AsyncTask的实现

    关于能否同时并发执行100+个AsyncTask,这取决于线程池的容量和任务的执行压力。AsyncTask使用线程池机制,容量为128,最多同时运行5个核心线程,剩余任务将被排队等待。

    异常处理与程序稳定性

    Force Close(FC)是开发者在调试和优化应用程序时需要特别注意的问题。FC通常由以下原因引起:

    • 空指针异常(NullPointerException)
    • 类未找到异常(ClassNotFoundException)
    • 资源未找到异常(ResourceNotFoundException)
    • Android API使用错误

    对于如何避免程序因未捕获异常而导致FC,可以通过实现UncaughtExceptionHandler接口并注册为程序的默认未捕获异常处理器来实现。

    全局异常捕获

    要实现全局异常捕获,可以通过以下方式实现:

  • UncaughtExceptionHandler:这是一个接口,用于处理未捕获异常。通过实现该接口并注册为程序的默认未捕获异常处理器,可以对程序中的未捕获异常进行全局处理。
  • Thread.setDefaultUncaughtExceptionHandler:可以通过设置默认的未捕获异常处理器为一个自定义的异常处理器来实现全局异常捕获。
  • 自定义异常处理器

    以下是一个实现全局异常捕获的示例代码:

    public class AppCrashHandler implements UncaughtExceptionHandler {    private Context mContext;    private Thread.UncaughtExceptionHandler mDefaultHandler;    private Lock lock = null;    public static AppCrashHandler shareInstance(Context context) {        if (mContext == null) {            lock = new ReentrantLock(true);            AppCrashHandler handler = new AppCrashHandler();            handler.initCrashHandler(context);            return handler;        }        return instance;    }    private void initCrashHandler(Context context) {        if (!hasInitialized()) {            mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();            Thread.setDefaultUncaughtExceptionHandler(this);            mContext = context;        }    }    @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("CrashHandler", "Interrupted while waiting for 3 seconds", e);            }            android.os.Process.killProcess(android.os.Process.myPid());            System.exit(10);        }    }    private boolean handleExceptionMessage(Throwable ex) {        if (ex == null) {            return false;        }        try {            Toast.makeText(mContext, "程序出错啦,即将退出", Toast.LENGTH_LONG).show();            Looper.prepare();        } finally {            Looper.loop();        }        String fileName = mContext.getPackageName() + "-" + "appCrash-Exception" + ".crash";        String crashFileName = saveExceptionToFile(ex, fileName);        SharedPreferences.Editor editor = mContext.getSharedPreferences("app_crash_pref", Context.MODE_PRIVATE).edit();        editor.putString("stack_trace", crashFileName).commit();        Log.d("CrashLog", "errorLogPath=" + crashFileName);        return true;    }    private boolean hasInitialized() {        return mContext != null;    }    private String saveExceptionToFile(Throwable ex, String fileName) {        File saveFile = null;        try {            lock.lock();            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {                File sdCardDir = Environment.getExternalStorageDirectory();                saveFile = new File(sdCardDir, fileName);            } else {                saveFile = new File(mContext.getFilesDir(), fileName);            }            if (!saveFile.exists()) {                saveFile.createNewFile();            }            PrintWriter printWriter = new PrintWriter(saveFile);            printWriter.write(formatException(ex));            printWriter.flush();            return saveFile.getAbsolutePath();        } catch (Exception e) {            e.printStackTrace();        } finally {            if (printWriter != null) {                printWriter.close();            }            lock.unlock();        }        return saveFile != null ? saveFile.getAbsolutePath() : null;    }    @Override    public void onCreate() {        synchronized (this) {            if (instance == null) {                instance = this;            }            initCrashHandler(instance.mContext);        }        super.onCreate();    }}

    总结

    通过以上方法,我们可以有效地管理线程池资源,避免因线程过多而导致的性能问题,同时通过全局异常捕获机制,确保程序在出现未捕获异常时能够优雅地处理,从而避免应用程序因Force Close而退出。

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

    你可能感兴趣的文章
    NLog 自定义字段 写入 oracle
    查看>>
    NLog类库使用探索——详解配置
    查看>>
    NLP 基于kashgari和BERT实现中文命名实体识别(NER)
    查看>>
    NLP 模型中的偏差和公平性检测
    查看>>
    Vue3.0 性能提升主要是通过哪几方面体现的?
    查看>>
    NLP 项目:维基百科文章爬虫和分类【01】 - 语料库阅读器
    查看>>
    NLP_什么是统计语言模型_条件概率的链式法则_n元统计语言模型_马尔科夫链_数据稀疏(出现了词库中没有的词)_统计语言模型的平滑策略---人工智能工作笔记0035
    查看>>
    NLP三大特征抽取器:CNN、RNN与Transformer全面解析
    查看>>
    NLP学习笔记:使用 Python 进行NLTK
    查看>>
    NLP度量指标BELU真的完美么?
    查看>>
    NLP的不同研究领域和最新发展的概述
    查看>>
    NLP的神经网络训练的新模式
    查看>>
    NLP采用Bert进行简单文本情感分类
    查看>>
    NLP问答系统:使用 Deepset SQUAD 和 SQuAD v2 度量评估
    查看>>
    NLP项目:维基百科文章爬虫和分类【02】 - 语料库转换管道
    查看>>
    NLP:使用 SciKit Learn 的文本矢量化方法
    查看>>
    nmap 使用方法详细介绍
    查看>>
    Nmap扫描教程之Nmap基础知识
    查看>>
    nmap指纹识别要点以及又快又准之方法
    查看>>
    Nmap渗透测试指南之指纹识别与探测、伺机而动
    查看>>