简介
线程和线程池在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()方法来通知调用者。
线程池工作流程:
如果线程池中活跃的线程数未达到最大的核心线程数,那么直接启动一个核心线程来执行任务
如果线程池中活跃的线程数达到最大核心线程数,那么任务会被插入到任务队列中排队等待执行
如果任务无法添加到任务队列中,而且尚未达到线程池的最大线程数,那么会启动一个非核心线程来执行任务
如果已经达到线程池的最大线程数,那么就会拒绝这个任务,并调用rejectedExecution()方法
Android中的线程池:
其实Android中的线程池都是ThreadPoolExecutor的的一些定制,通过对ThreadPoolExecutor进行配置来实现不同特性的线程池。
FixedThreadPool:
一种线程数量固定的线程池,他只有核心线程,而且没有超时机制,当线程空闲时并不会被收回,除非线程池被关闭了。当所有的线程都处于活动状态时,新到的任务会处于等待状态,直到有线程空闲出来。
CachedThreadPool:
一种线程数量不固定的线程池,他只有非核心线程,并且最大线程为Integer.MAX_VALUE。当线程池中的线程都处于活跃状态时会创建新的线程来处理任务,否则会利用空闲的线程来处理。每个线程都有超时机制,时长为60s。
ScheduledThreadPool:
这类线程池有着固定的核心线程数,非核心线程数没有限制,而且非核心线程处于闲置时会立即被收回,主要适合用于处理定时任务和具有固定周期的重复任务
SingleThreadPool:
线程池内部只有一个核心线程,可以确保所有的外界任务在传进来之后都可以顺序的执行,不必考虑线程同步的问题。