Android线程与线程池《Android开发艺术探索》笔记


参考文章:要点提炼|开发艺术之线程

概述

线程是CPU调度的最小单元。

线程是一种受限的资源,不能多次的创建与停止。

分类:
主线程:一般一个线程只有一个主线程。主线程中一般用于UI操作,用于与用户交互。
子线程:子线程中一般执行耗时操作,比如网络操作和IO操作。

线程形态:
AysncTask:一种异步执行类,便于执行后台任务以及在主线程更新UI。其中封装了两个线程池,一个Handler,其中一个线程池用于任务的排序,另一个线程池用于处理任务。Handler用于切换到主线程。

HandlerThread:可以持续的循环从消息队列中取消息的线程。(其实内部调用了Looper.prepare和Looper.loop方法)。

IntentService:是一个异步并会自动停止的服务,好处就是优先级比较高,不容易被回收。内部封装了一个HandlerThread和一个Handler,Handler与HandlerThread中的Looper绑定。

在这里插入图片描述

线程形态

AsyncTask

概述与使用

在Android中实现异步任务机制有两种方式:Handler和AsyncTask

Handler机制存在的问题:代码相对臃肿;多任务同时执行时不易精确控制线程

引入AsyncTask的好处:创建异步任务更简单,直接继承它可方便实现后台异步任务的执行和进度的回调更新UI,而无需编写任务线程和Handler实例就能完成相同的任务。

AsyncTask是一个泛型类,其中包括三个泛型参数 public abstract class AsyncTask<Void, Integer, Boolean>

  • params:传入的数据。(一般是传入URL,类型对应是String)
  • progress:任务进度(类型一般是Integer)
  • result:结果。

包含五个重要方法

  • onPreExecute运行在主线程中,在后台任务执行前会调用进行一些初始化操作
  • doInBackground(…params)运行在子线程中后台任务具体在这里会执行。如果在其中想要知道任务执行的进度需要调用publishProgress(progress)方法该方法会回调onProgressUpdate方法
  • onProgressUpdate(progress)运行在主线程,可以根据progress的值来更新UI
  • onPostExcute(result)运行在主线程,当任务运行结束后会调用,可以根据result值来更新UI。
  • onCanceled:运行在主线程,当任务被取消时会调用

开始和结束异步任务的方法

  • 开始异步任务:AysncTask.execute(Params...params)必须在主线程中使用,同时注意一个任务只能调用一次execute方法否则会报错。
  • 结束异步任务:AysncTask.cancel必须在主线程中使用,表示停止一个服务

使用注意:
(1)不要直接调用AysncTask中的方法比如直接调用onProgressUpdate方法来获取当前进度,而是应该使用其中的progress来更新UI。
(2)AysncTask的实例必须在主线程中创建。(具体原因在于其中有一个Handler,是用于转换到主线程的,它的一系列handle方法要在主线程使用,因此Handler必须在主线程中创建,进而AysncTask也必须在主线程创建)。

接下来是一个自定义AysncTask的实例

class DownloadTask extends AsyncTask<Void, Integer, Boolean> {  
  
    @Override//初始化一个ProgressDialog  
    protected void onPreExecute() {  
        progressDialog.show();  
    }  
  
    @Override//具体的下载逻辑
    protected Boolean doInBackground(Void... params) {  
        try {  
            while (true) {  
                int downloadPercent = doDownload();  
                publishProgress(downloadPercent);  
                if (downloadPercent >= 100) {  
                    break;  
                }  
            }  
        } catch (Exception e) {  
            return false;  
        }  
        return true;  
    }  
  
    @Override//显示当前的下载进度
    protected void onProgressUpdate(Integer... values) {  
        progressDialog.setMessage("当前下载进度:" + values[0] + "%");  
    }  
  
    @Override//提示任务的执行结果  
    protected void onPostExecute(Boolean result) {  
        progressDialog.dismiss();  
        if (result) {  
            Toast.makeText(context, "下载成功", Toast.LENGTH_SHORT).show();  
        } else {  
            Toast.makeText(context, "下载失败", Toast.LENGTH_SHORT).show();  
        }  
    }  
}  

开启和停止的代码如下:

// 开始任务  
DownloadTask mDownloadTask  = new DownloadTask();  
mDownloadTask .execute();  
// 停止任务  
mDownloadTask .cancel(true);  

原理

AysncTask包括两个线程池:SerialExecutor与THREAD_POOL_EXECUTOR;他们的作用分别是任务的排队与任务的实际执行。

AysncTask包含一个Handler,该Handler的作用是将执行环境由线程池切换至主线程并通过它来发送任务的执行情况的消息。

任务排队的原理是:
(1)params被封装为FutureTask;

(2)调用SerialExecutor.execute()将FutureTask插入到任务队列tasks

(3)判断当前是否没有任务在执行,没有的话会调用scheduleNext方法去执行下一个任务,同时当任务执行完毕后,也会调用scheduleNext方法去执行下一个任务。可以看出AysncTask是以串行的方式执行任务。

任务执行的原理:
(1)FutureTask的run函数会调用mWorker的call函数

(2)在call函数中调用doInbackground方法,接着将doInbackground的返回值传入postResult方法

(3)postResult方法中会调用sendMessage方法。

(4)在handleMessage方法中根据发送的msg可以判断当前的情况,进而去调用onPostExcute或onCancel或onProgressUpdate方法

HandlerThread

该类是一个线程类,与普通线程的区别在于,内部对Looper进行了创建,因此可以持续的接收和查找消息。内部调用了Looper.prepare;Looper.loop方法。

实现方法:
1.创建一个HandlerThread实例,并传入该线程的名字。

2.通过 HandlerThread.start()开启线程;

3.创建Handler,并在构造函数中传入HandlerThread的Looper对象,完成与Handler的绑定。

4.当不需要HandlerThread时,通过HandlerThread.quit()/quitSafely()方法来终止线程的执行

IntentService

IntentService是一个服务,可以在其中执行耗时的任务,IntentService内部包含了一个HandlerThread和一个Handler。
优点:由于是服务,优先级比较高,不会轻易被杀死,所以适合去做一些优先级高的耗时任务;
和普通线程相比,可自动创建子线程来执行任务,且任务执行完毕后自动退出

工作原理

1.在服务onCreate中会首先创建一个HandlerThread的对象并开启线程,之后通过传入Looper对象的方式创建Handler,将Handler与HandlerThread绑定。
在这里插入图片描述
2.当调用onStartCommand方法后,会调用Handler的sendMessage(将intent对象放入message的obj字段中),将消息发送到MessageQueue中。Looper持续调用MessageQueue的next函数将msg取出,并找到msg对应的Handler。

3.handleMessage中包含两个函数:onHandleIntent(intent),以及stopself(startId)

onHandleIntent方是具体处理Intent的异步请求,stopself是用于停止服务的

而无参数stopSelf方法时立刻停止服务,有参数stopSelf则不同,该方法会在尝试停止服务之前会判断启动服务的次数与startId,是否相等。相等才会停止服务,这样做的好处在于可以当所有消息对处理之后才停止服务。

具体流程如下:
在这里插入图片描述
IntentService是一个抽象类,需要我们自己实现其中的onHandleIntent抽象方法,下面是一个实例。
在使用上,我们需要对Intent实例,然后进行intent.putExtra("task_action","具体任务")操作,最后在调用startService(intent)

public class LocalIntentService extends IntentService {

    private static final String TAG = "LocalIntentService";

    public LocalIntentService() {
        super(TAG);
    }

    @Override
    protected void onHandleIntent( Intent intent) {
        String action = intent.getStringExtra("task_action");
        Log.i(TAG,"receiver action:" + action);
        SystemClock.sleep(3000);
        if("com.lgl.test.ACTION".equals(action)){
            Log.i(TAG,"handler action:" + action);
        }
    }

    @Override
    public void onDestroy() {
        Log.i(TAG,"onDestroy");
        super.onDestroy();
    }
}

线程池

线程池的优点与构成

线程池的优点:
(1)可以重用线程池中的线程,避免线程的多次创建和销毁以节省资源。
(2)可以控制线程池中的最大并发数,避免大量的线程之间因互相抢占系统资源而导致阻塞现象。
(3)进行线程管理,提供定时/循环间隔执行等功能

线程池实现了Executor接口,Android中的一个具体实现是ThreadPoolExcutor,其中包含了一些参数。

//构造参数
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {

1.CorePoolSize核心线程数,空闲下的核心线程常规情况下是不会被销毁的,但是可以设置一个boolean变量allowCoreThreadTimeOut,如果该变量为true的情况下,核心线程空闲时间超过AliveTime就会被销毁。

2.maxiumPoolSize:最大线程数,当活动线程超过这个数时,新任务会进入阻塞。

3.keepAliveTime非核心线程下的允许空闲时间,非核心线程如果超过该时间会销毁该线程。当allowCoreThreadTimeOut为true时,也会对核心线程有效。

4.workQueue任务队列通过线程池的execute()方法提交的Runnable对象会存储在这个参数中

5.threadFactory线程工厂。是一个抽象类,用于创建线程。其中只包含了一个方法Thread newThread(Runnable r)

6.handler当线程池无法执行新任务时进行调度,一般会抛出异常

ThreadPoolExecutor的工作原理:
当一个任务到来后,
如果当前所使用的线程数小于核心线程数,就直接开启一个核心线程来执行这个任务;
否则当当前所使用的线程数大于等于核心线程数,则让任务进入任务队列等待;
如果任务队列已经满了的话,会启动一个非核心线程;
如果当前所使用的线程数等于最大线程数,那么就拒绝执行该任务,并通过Handler来抛出异常。

线程池的分类

1.FixThreadPool:线程数量固定的线程池,其中全部是核心线程,核心线程始终存活。
能快速响应外界请求。

2.:CachedThreadPool:该线程池全部为非核心线程,最大线程量为Integer.MAX_VALUE。时间超过60s线程会被回收。当整个线程池处于闲置状态时,所有线程都会因为超时而被回收,就变成了一个空的线程池,几乎不会占系统资源;
适合于执行大量的耗时短的任务。

3.ScheduledThreadPool:核心线程数量固定,非核心线程数量不固定,非核心线程只要处于空闲就会销毁。
适合于定时任务和具有固定周期的任务。

4.SingeleThreadExcutor:只有一个核心线程,确保所有的任务都在一个线程中按顺序进行
将外界任务统一到一个线程中,无需考虑线程同步问题。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章