自学Android开发 异步任务和线程池

目录

一、异步任务

二、AsyncTask

1、官方文档

2、AsyncTask的使用

三、Handler+ Looper. getMainLooper +Thread/ Executors

四、Thread/ Executors+ Handler+ HandlerThread

五、Thread/ Executors+ Activity. runOnUiThread

六、线程池

1、TheadPoolExecutor

2、newFixedThreadPool

3、newCachedThreadPool

4、newScheduledThreadPool

5、newScheduledThreadPool


一、异步任务

        Android开发中,控件的操作必须在主线程(UI线程)中操作,虽然主线程可以进行一些的耗时操作,但是如果超过5秒就会提示ANR错误。为避免影响UI正常绘制的流畅性,所以耗时性操作不要在主线线程中进行。既然主线程不能做太耗时的操作,那就开辟一个子线程操作耗时任务,但是子线程内是不能操作控件的,这样就需要异步处理。

二、AsyncTask

1、官方文档

        AsyncTask 旨在实现 UI 线程的正确和轻松使用。但是,最常见的用例是集成到 UI 中,这会导致 Context 泄漏、错过回调或配置更改时崩溃。它在不同版本的平台上也有不一致的行为,吞下来自 的异常doInBackground,并且没有提供比Executor直接使用更好的效用。

        AsyncTask的设计是一个辅助类各地Thread和Handler,并不构成通用线程框架。理想情况下,AsyncTasks 应该用于短期操作(最多几秒钟)。如果您需要让线程长时间运行,强烈建议您使用java.util.concurrent包提供的各种 API,例如Executor、ThreadPoolExecutor和FutureTask。

        异步任务由在后台线程上运行且其结果发布在 UI 线程上的计算定义。异步任务是由3种一般类型,称为定义Params,Progress和Result,和4个步骤,称为onPreExecute,doInBackground,onProgressUpdate和onPostExecute。

注意:此类在 API 级别 30 中已弃用。

2、AsyncTask的使用

 //AsyncTask 异步操作
private class DomeTask extends AsyncTask<String, Integer, Long> {

         //doInBackground是在AsyncTask的线程中调用 就是在这里进行耗时操作
        protected Long doInBackground(String... urls) {
             
            //调用publishProgress用于更新进度 
             publishProgress(Long);
            return null;
        }

         
        //onProgressUpdate是用于对异步任务是进度进行更新
        //onProgressUpdate在UI线程中 可以对控件进行操作
        protected void onProgressUpdate(Integer... progress) {
           

        }  // onPostExecute被执行说明任务已完成
          // onPostExecute在UI线程中 可以对控件进行操作
        protected void onPostExecute(Long result) {
            

        }
    }


//必须是在UI线程中调用
//executeOnExecutor 提供并行执行任务 就是多个任务一起执行
new DomeTask().executeOnExecutor(Executor exec, Params... params);
//execute是提供串行执行任务 ,就一个完成后接着下一个
new DomeTask().execute(Params... params);

        AsyncTask<Params, Progress, Result>有三个泛型类型 可以上面的源码知道,第一个Params是doInBackground的参数类型,第二个Progress是onProgressUpdate的参数类型,第三个Result是onPostExecute的参数类型,如果不使用可以使用Void替代。

        execute(Params... params)只有一个参数传给doInBackground的可变参数 ,意思是可以有多个参数,但一定是同一个类型。

        executeOnExecutor(Executor exec, Params... params)有两个参数 第一个是线程池对象,第二个同execute。

三、Handler+ Looper. getMainLooper +Thread/ Executors

        在UI主线程里实例化handler

    //默认使用主线程中的looper消息循环
    private Handler handler = new Handler(){

        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            
        }
    };

        在非UI主线程中实例化handler并且使用Looper. getMainLooper

 //因为不在主线程里所以需要设置一个looper消息循环
//Looper.getMainLooper()是可以获取主线程里的looper消息循环的
private Handler handler =new Handler(Looper.getMainLooper()){
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
        }
    };

        创建子线程执行耗时任务并通过handler异步更新控件

 private Handler handler =new Handler(Looper.getMainLooper()){
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
           //这里是处于 UI程序中的,是可以操作UI控件的
        }
    };
    
    //我这里就只使用newScheduledThreadPool创建一个固定4个核心线程的线程池做例子
    private void dome(){

        ExecutorService executorService = Executors.newScheduledThreadPool(4);
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                //从全局消息池返回一个Message对象
                Message message = handler.obtainMessage();
                handler.sendMessage(message);
            }
        });
    }

四、Thread/ Executors+ Handler+ HandlerThread

           实例化handler和开启handlerThread线程

  //实例化 HandlerThread
 HandlerThread thread = new HandlerThread("threadx");
 //开始HandlerThread线程
    thread.start();
    
// 从HandlerThread中获取一个looper消息循环
   Handler handler =new Handler(thread.getLooper()){
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
            }
        };

        同 Thread/ Executors+ Handler+ HandlerThread 一样创建子线程执行耗时任务并通过handler异步更新控件

 
    //我这里就只使用newScheduledThreadPool创建一个固定4个核心线程的线程池做例子
    private void dome(){

        ExecutorService executorService = Executors.newScheduledThreadPool(4);
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                //从全局消息池返回一个Message对象
                Message message = handler.obtainMessage();
                handler.sendMessage(message);
            }
        });
    }

五、Thread/ Executors+ Activity. runOnUiThread

        创建线程池并调用runOnUiThread更新UI控件

  //创建线程池
ExecutorService executorService = Executors.newScheduledThreadPool(4);
        executorService.execute(new Runnable() {
            @Override
            public void run() {
           
                //调用发送到UI线程中 
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                     //这里是在UI线程中执行的 使用这里是可以更新UI控件的
                    }
                });
            }
        });

注意: 在 UI 线程上运行指定的操作。 如果当前线程是 UI线程,则立即执行操作。 如果当前线程是不是 UI 线程,动作被发布到 UI 线程的事件队列中。

六、线程池

在上面经常用到线程池我就介绍一些Executors的静态方法

1、TheadPoolExecutor

ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue)

ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory)

        ThreadPoolExecutor是线程池的真正方法,其他创建线程池方法都是通过调用它实现的。

ThreadPoolExecutor有六个参数:

corePoolSize线程池的核心线程数,默认情况下会一直存活,即时它们在闲置状态。

maximumPoolSize 线程池能容纳最大的线程数,达到这个数后新的任务将会被阻塞。

keepAliveTime非核心线程的闲置状态下存活的时间,当ThreadPoolExecutor的allowCoreThreadTimeOuth属性为true时,同样作用于核心线程。

unit用于指定keepAliveTime的时间单位,常用有:TimeUnit.MILLISECONDS(毫秒),TimeUnit.SECONDS(秒),TimeUnit.MINUTES(分钟)。

workQueue线程中的队列,execute提交的Runnable对象就存储在这里

threadFactory线程工厂,为线程池提供创建新线程的功能,从上面知道它可以省略。

2、newFixedThreadPool

nnewFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>()
                                    );

        对比ThreadPoolExecutor可知newFixedThreadPool是一个固定线程数量的线程池,当线程闲置时,也不会回收,可以更快的响应新任务,但当所有线程都处于活动动态那么新任务将处于等待状态,直到有空闲线程。

3、newCachedThreadPool

newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());

        对比ThreadPoolExecutor可知newCachedThreadPool是一个只有非核心线程的,且最大数量任意大,线程的超时为60秒。

4、newScheduledThreadPool

newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);

ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue());

class ScheduledThreadPoolExecutor
        extends ThreadPoolExecutor

        因为 newScheduledThreadPool实例化ScheduledThreadPoolExecutor,ScheduledThreadPoolExecutor继承ThreadPoolExecutor,调用 super其实还是调用ThreadPoolExecutor ,对比ThreadPoolExecutor可知它是核心线程数量固定非核心线程没有限制。

5、newScheduledThreadPool

newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
      

        由上面源码可知还是调用ThreadPoolExecutor,它是只有一个核心线程为了所有任务都是在同一个线程内安顺序执行。

如果对您有一些意义,希望您给博主一些鼓励(点赞、关注、收藏),如果有错误欢迎大家评论。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码

)">
< <上一篇
下一篇>>