《Android开发艺术探索》--Android线程和线程池

简介

线程和线程池在Android开发中有着重要的地位。因为Android在主线程也就是UI线程中不能做太多耗时的操作(ANR问题),所以很多需要耗时的操作(文件读写,网络请求等)就需要在子线程中进行处理,完成之后在通知UI线程更新界面。

为什么只能在UI线程中更新UI:由于UI线程是非线程安全的,所以如果在子线程中更新UI容易导致未知的错误,但是也不能把UI线程设计成线程安全的,因为会进行频繁的更新,所以线程安全的话很消耗资源,而且不利于界面更新。

所以在Android,所有耗时的任务都需要子线程或者是线程池来进行处理。

子线程和线程池:

子线程是区别于主线程的一个线程,实现方式主要有三种方式:

  • 继承自Thread类,并重写其中的run()方法

  • 实现Runnable接口,实现其中的run()方法

  • 通过实现Callable接口,实现其中的call()方法,并用FutureTask进行封装,这种方式可以有返回值

线程池是可以维持一定数量的线程,实现线程重用的一种形式。Android中的线程池主要是来源于Java的线程池ThreadPoolExecutor,在Android中通过对ThreadPoolExecutor的配置来实现了多种不同特性的线程池,但是其本质仍是对于ThreadPoolExecutor的不同配置。

Android中的线程:

AsyncTask:

AsyncTask是一个已经封装好的轻量级的异步任务类,其核心实现就是对于Thread和Handler的封装,AsyncTask是一个抽象的泛型类。

public abstract class AsyncTask< Params,Progress, Result>

  • Params 传入的参数类型
  • Progress 任务进度类型
  • Result 返回值类型

例如:

package com.wei.rxjavademo;

import android.os.AsyncTask;
import android.util.Log;
import android.view.View;
import android.widget.ProgressBar;

/**
 * Created by WQC on 2016/12/9.
 */

public class MyAsyncTask extends AsyncTask<String,Integer,Long> {

    private static final String TAG = "MyAsyncTask";
    ProgressBar mProgressBar;

    public MyAsyncTask() {
    }

    //传进来的进度条用于更新界面
    public MyAsyncTask(final ProgressBar progressBar) {
        mProgressBar = progressBar;
    }

    /**
     * 任务开始前在主线程执行
     * 在这里进行一些前期的准备操作
     */
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        mProgressBar.setVisibility(View.VISIBLE);
        mProgressBar.setProgress(0);
        Log.i(TAG, "onPreExecute: is call");
    }

    /**
     * 在主线程中执行
     * 用于更新任务进度
     * @param values
     */
    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);

        mProgressBar.setProgress(values[0]);
        Log.i(TAG, "mProgressBar value"+mProgressBar.getProgress());

    }

    /**
     * 在线程池中执行
     * 主要在该方法中进行耗时任务的执行
     * @param params
     * @return
     */
    @Override
    protected Long doInBackground(String... params) {
        //执行耗时任务
        //然后使用publicProgress来更新进度,次方法会调用onProgressUpdate来在UI中更新界面

        float count = params.length;
        for (int i = 0;i<count;i++) {
            Log.i(TAG, "doInBackground: " + params[i]);
            publishProgress(i);
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return (long) count;
    }

    /**
     * 在主线程中执行
     *当任务取消之后调用,此时onPostExecute不会被调用
     */
    @Override
    protected void onCancelled() {
        super.onCancelled();
        mProgressBar.setVisibility(View.GONE);
        Log.i(TAG, "onCancelled is call ");
    }


    /**
     * 在主线程中执行
     * 任务顺利执行完之后调用
     * @param aLong
     */
    @Override
    protected void onPostExecute(Long aLong) {
        super.onPostExecute(aLong);
        mProgressBar.setVisibility(View.GONE);
        Log.i(TAG, "onPostExecute is call "+aLong);
    }


}

然后在主线程中使用AsyncTask执行任务:

//其中mProgressBar是主线程中进度条的引用
new MyAsyncTask(mProgressBar).execute("你好", "hello","word","hhhhh");

在AsyncTask中主要有五个方法需要实现:

  • onPreExecute():执行在主线程中,在异步任务执行前调用

  • onProgressUpdate(Integer… values):执行在主线程中,当任务进度更新时调用

  • doInBackground(String… params):执行在线程池中,主要用于处理后台任务,在这个方法中可以调用 publishProgress()来更新任务进度, publishProgress会调用onProgressUpdate方法

  • onCancelled():执行在主线程中,当任务被取消时调用

  • onPostExecute(Long aLong):执行在主线程中,当任务执行完毕之后调用

以上的代码就是AsyncTask的使用实例,但是AsyncTask有其使用的限制条件:

  • AsyncTask实例必须在主线程中进行实例化

  • execute方法必须在主线程中执行

  • 不可以直接在主线程中直接调用AsyncTask里边的五个方法

  • 一个AsyncTask智能执行一次,也就是说对于一个AsyncTask而言,execute方法只能执行一次

  • 在Android 1.6之前 AsyncTask是串行执行任务的,在1.6的时候采用并行执行任务,在Android3.0之后又改为串行执行任务,但是仍可以使用executeOnExecutor来指定并行执行任务。

HandlerThread:

HandlerThread继承自Thread,是一种可以使用Handler的线程,也就集成了Handler的Thread。使用HandlerThread处理任务时需要通过handler来发送消息,然后HandlerThread中的handleMessage方法根据传来的消息进行任务的处理。由于HandlerThread中的run()方法是一个死循环,所以在不使用HandlerThread时候需要调用其quit()或者是quitSafely()方法终止线程的执行。

quit()方法会直接终止行程的执行,而quitSafely()则是会等其中执行的任务全部结束之后在终止线程。

IntentService:

这是一种特殊的Service,常常被用于执行耗时的后台任务,由于其属于Service,因此有较高的优先级,不容易在后台执行任务时被系统杀死。在他的内部其实是封装了HandlerThread+Handler,通过Handler来接受任务,然后将接收到的任务以Message的形式发给HandlerThread去执行。

工作流程:

  • 首先在IntentService在第一次启动时会在onCreate()方法中初始化一个HandlerThread,用于任务的处理;

  • 然后在onStartCommand()方法中将传入的Intent包装成一个message发送给内部的HandlerThread去处理;

  • 内部的HandlerThread中的Handler在接收到message之后会调用onHandleIntent()方法来处理任务,而这个onHandleIntent()方法是个抽象方法,需要我们自己实现。

  • 当onHandleIntent()方法执行结束之后就会调用stopSelf(int startId)方法来尝试停止服务

stopSelf(int startId)和stopSelf()的区别:stopSelf(int startId)会等到所有的任务都执行完之后才会停止服务,而stopSelf()则是立即停止服务。

因为内部实际是使用HandlerThread来处理任务,所以任务的执行都是串行执行的,依据任务发起的顺序来依次执行。

Android中的线程池:

Android中的线程池主要来自于Java中的ThreadPoolExecutor这个类。

ThreadPoolExecutor(int corePoolSize,int maxmumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue< Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler rejectedExecutionHandler);

这个方法主要是通过不同的配置来实现不同的功能,其各个参数详情如下:

  • int corePoolSize:核心线程数,默认情况下核心线程会在线程池中始终存货,即使是在空闲的情况下,除非设置allowCoreThreadTimeOut为true,并指定了其存活时间。

  • int maxmumPoolSize:线程池中最大的线程数量,当线程池中活动的线程数量达到这个值之后,后续的任务会被阻塞

  • long keepAliveTime:非核心线程的存活时间,当线程池中的非核心线程空闲时间达到这个值之后就会被系统收回。

  • TimeUnit unit:存活时间的时间单位,有毫秒,秒,分等时间单位

  • BlockingQueue< Runnable> workQueue:线程池中的任务队列,向线程池提交的Runnable对象都会存储在这个任务队列中。

  • ThreadFactory threadFactory:Thread的一个工厂类,用于给线程池提供创建线程的能力

RejectedExecutionHandler rejectedExecutionHandler:不常用,当提交给线程池的任务被拒绝之后就会调用该handler的rejectedExecution()方法来通知调用者。

线程池工作流程:

  1. 如果线程池中活跃的线程数未达到最大的核心线程数,那么直接启动一个核心线程来执行任务

  2. 如果线程池中活跃的线程数达到最大核心线程数,那么任务会被插入到任务队列中排队等待执行

  3. 如果任务无法添加到任务队列中,而且尚未达到线程池的最大线程数,那么会启动一个非核心线程来执行任务

  4. 如果已经达到线程池的最大线程数,那么就会拒绝这个任务,并调用rejectedExecution()方法

Android中的线程池:

其实Android中的线程池都是ThreadPoolExecutor的的一些定制,通过对ThreadPoolExecutor进行配置来实现不同特性的线程池。

FixedThreadPool:

一种线程数量固定的线程池,他只有核心线程,而且没有超时机制,当线程空闲时并不会被收回,除非线程池被关闭了。当所有的线程都处于活动状态时,新到的任务会处于等待状态,直到有线程空闲出来。

CachedThreadPool:

一种线程数量不固定的线程池,他只有非核心线程,并且最大线程为Integer.MAX_VALUE。当线程池中的线程都处于活跃状态时会创建新的线程来处理任务,否则会利用空闲的线程来处理。每个线程都有超时机制,时长为60s。

ScheduledThreadPool:

这类线程池有着固定的核心线程数,非核心线程数没有限制,而且非核心线程处于闲置时会立即被收回,主要适合用于处理定时任务和具有固定周期的重复任务

SingleThreadPool:

线程池内部只有一个核心线程,可以确保所有的外界任务在传进来之后都可以顺序的执行,不必考虑线程同步的问题。

猜你喜欢

转载自blog.csdn.net/wqc_csdn/article/details/53541312