线程
- 线程池的好处与使用场景,和具体参数与底层实现
- Android中独有的线程类
- AsyncTask原理
- AsyncTask的参数
- 使用AsyncTask的注意事项
- AsyncTask使用不当的缺点
- HandlerThread的使用
- IntentService的使用
- 分析
- IntentService和Service的区别
线程池的好处与使用场景,和具体参数与底层实现
答:使用线程池的好处是减少在创建和销毁线程上所花费的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建同类线程而导致消耗完内存或有过度切换的情况。
- 重用存在的线程,减少对象的创建、消亡的开销,性能更好
- 可以有效地控制最大并发数,提高系统资源的使用率,同时避免过多的资源竞争,避免堵塞
- 提供定时执行、定期执行、单线程、并发控制等功能。
Android中的线程池都是直接或者间接通过配置ThreadPoolExecutor来实现不同特性的线程池,Android中最常见的类具有不同特性的线程分为
- newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
只有核心线程池,且数量固定,自定义的变量nThreads,所有线程都活动时,因为队列没有限制大小,新任务会等待执行,当线程池空闲时不会释放工作线程,还会占用一定的系统资源。
优点是可以更快地相应外界请求。
- newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
只有一个核心线程,确保所有的任务都在同一线程中按序完成
优点:不需要处理线程同步问题
3. newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
只有非核心线程,最大线程数为非常大,所有的线程都活动时会为新任务创建新线程,否则会利用空闲线程(60s空闲时间,过了会被回收,所以该线程可为0)来处理任务
优点:任何任务都会被立即执行(任务队列SynchronousQuue)相当于一个空集合,适合大量的耗时任务
- newScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
核心线程数固定,非核心线程数闲着没事干就会被回收
优点:执行定时任务以及周期的重复任务
Android中独有的线程类
- AsyncTask:底层封装了线程池和Handler,便于执行后台任务以及在子线程中进行UI操作
- HandlerThread:一种具有消息循环的线程,其内部可使用Handler
- IntentService:是一种异步的、会自动停止的服务,内部采用HandlerThread
AsyncTask原理
AsyncTask是一个抽象类,它是由Android封装的一个轻量级异步类,可以在线程池中执行后台任务,然后把执行的进度和最终结果返回在主线程并更新UI
AsyncTask中有两个线程池:SerialExecutor和THREAD_POOL_EXECUTOR和一个Handler:IntentHandler。其中线程池SerialExecutor用于任务的排队,让需要执行的多个耗时任务,按顺序排列,而线程池THREAD_POOL_EXECUTOR用于真正的执行任务,InternalHandler用于将执行环境从线程池切换到主线程
sHandler是一个静态的Handler对象,为了能够将执行环境切换到主线程,要求sHandler这个对象必须在主线程创建。由于静态成员会在加载类的时候进行初始化,因此这就要求AsyncTask的类必须在主线程中加载,否则同一个进程中AsyncTask都将无法正常工作。
AsyncTask的参数
/**
* AsyncTask是一个抽象泛型类
* @param <Params> 开始异步操作时传入的参数类型
* @param <Progress> 异步任务执行时传入的参数类型
* @param <Result> 异步任务执行过程中,返回的下载进度值的类型
* 如果AsyncTask确定不需要传入具体参数,这三个泛型类可以用void替代
*/
@Deprecated
public abstract class AsyncTask<Params, Progress, Result> {
/** @deprecated */
@Deprecated
public static final Executor SERIAL_EXECUTOR = null;
/** @deprecated */
@Deprecated
public static final Executor THREAD_POOL_EXECUTOR = null;
/** @deprecated */
@Deprecated
public AsyncTask() {
throw new RuntimeException("Stub!");
}
/** @deprecated */
@Deprecated
public final AsyncTask.Status getStatus() {
throw new RuntimeException("Stub!");
}
/**
* 此方法中的所有代码都会在子线程中运行,且有返回值!
* 在此处进行所有的耗时操作
* 任务一旦完成就可以通过return语句来将任务的执行结果返回
* 如果AsyncTask的第三个泛型是void
* 就可以不用返回参数
* 此方法不可进行UI操作,更新UI需调用publishProgress(progress)
*
* @param var1
* @return
*/
@Deprecated
protected abstract Result doInBackground(Params... var1);
/**
* 此方法会在后台任务开始执行之间调用
* 在主线程执行时
* 用于进行一些界面上的初始化操作,比如显示一个进度条对话框
*/
@Deprecated
protected void onPreExecute() {
throw new RuntimeException("Stub!");
}
/** @deprecated */
@Deprecated
protected void onPostExecute(Result result) {
throw new RuntimeException("Stub!");
}
/**
* 在后台任务调用publish就会调用此方法
* @param values
*/
@Deprecated
protected void onProgressUpdate(Progress... values) {
throw new RuntimeException("Stub!");
}
/** @deprecated */
@Deprecated
protected void onCancelled(Result result) {
throw new RuntimeException("Stub!");
}
/** @deprecated */
@Deprecated
protected void onCancelled() {
throw new RuntimeException("Stub!");
}
/** @deprecated */
@Deprecated
public final boolean isCancelled() {
throw new RuntimeException("Stub!");
}
/** @deprecated */
@Deprecated
public final boolean cancel(boolean mayInterruptIfRunning) {
throw new RuntimeException("Stub!");
}
/** @deprecated */
@Deprecated
public final Result get() throws ExecutionException, InterruptedException {
throw new RuntimeException("Stub!");
}
/** @deprecated */
@Deprecated
public final Result get(long timeout, TimeUnit unit) throws ExecutionException, InterruptedException, TimeoutException {
throw new RuntimeException("Stub!");
}
/** @deprecated */
@Deprecated
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
throw new RuntimeException("Stub!");
}
/** @deprecated */
@Deprecated
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params) {
throw new RuntimeException("Stub!");
}
/** @deprecated */
@Deprecated
public static void execute(Runnable runnable) {
throw new RuntimeException("Stub!");
}
/** @deprecated */
@Deprecated
protected final void publishProgress(Progress... values) {
throw new RuntimeException("Stub!");
}
/** @deprecated */
@Deprecated
public static enum Status {
/** @deprecated */
@Deprecated
PENDING,
/** @deprecated */
@Deprecated
RUNNING,
/** @deprecated */
@Deprecated
FINISHED;
private Status() {
}
}
}
这些方法的调运顺序: onPreExecute() -->doInBackgroup()—>publishProgress—>onProgressUpdate()—>onPostExecute()
使用AsyncTask的注意事项
- 异步任务的实例必须在UI线程中创建,即AsyncTask对象必须在UI线程中创建。
- execute(Params params)方法必须在UI线程中调用
- 不要手动调用onPreExecute()、doInBackground()、onProgressUpdate,onPostExecute这几个方法
- 不可以在doInBackground中更改UI组件信息
- 一个任务实例只能执行一次
AsyncTask使用不当的缺点
- 生命周期问题:
AsyncTask不与任何组件绑定生命周期,所以在Activity/Fragment中调用执行创建AsyncTask时,需要在OnDestory中调用Cancel。 - 内存泄漏问题
如果AsyncTask被声明为Activity的非静态的内部类,那么AsyncTask会保留一个对创建AsyncTask的Activity的引用。如果Activity已经被销毁,AsyncTask的后台线程还在执行,会继续在内存里保留这个引用,导致Activity无法被回收,引起内存泄漏 - 结果丢失
屏幕旋转或者Activity在后台被系统杀死等情况会导致Activity的重新创建,之前运行的Asynctask非静态的内部类会持有一个之前Activity的引用,这个引用已经无效,这时调用onPostExecute再去更新界面不生效。
HandlerThread的使用
HandlerThread是Android自带的,可以用来执行多个耗时任务操作,不需要多次开启线程,采用的是Handler+Looper实现的
public class HandlerThread extends Thread {
public HandlerThread(String name) {
throw new RuntimeException("Stub!");
}
public HandlerThread(String name, int priority) {
throw new RuntimeException("Stub!");
}
protected void onLooperPrepared() {
throw new RuntimeException("Stub!");
}
public void run() {
throw new RuntimeException("Stub!");
}
public Looper getLooper() {
throw new RuntimeException("Stub!");
}
public boolean quit() {
throw new RuntimeException("Stub!");
}
public boolean quitSafely() {
throw new RuntimeException("Stub!");
}
public int getThreadId() {
throw new RuntimeException("Stub!");
}
}
IntentService的使用
IntentService可用于执行后台耗时的任务,当任务执行完成后会自动停止,同时由于IntentService是服务的原因,不同于普通的Service,IntentService可自动创建子线程来执行任务,这导致他的优先级逼单出的线程要高,不容易被系统杀死,所以IntentService比较适合执行一些高级的后台任务。
若启动IntentService多次,那么每个耗时操作以队列的方式在IntentService的onHandlerIntent回调方法中依次运行,执行完自动结束。
@Deprecated
public abstract class IntentService extends Service {
/** @deprecated */
@Deprecated
public IntentService(String name) {
throw new RuntimeException("Stub!");
}
/** @deprecated */
@Deprecated
public void setIntentRedelivery(boolean enabled) {
throw new RuntimeException("Stub!");
}
/** @deprecated */
@Deprecated
public void onCreate() {
throw new RuntimeException("Stub!");
}
/** @deprecated */
@Deprecated
public void onStart(@Nullable Intent intent, int startId) {
throw new RuntimeException("Stub!");
}
/** @deprecated */
@Deprecated
public int onStartCommand(@RecentlyNullable Intent intent, int flags, int startId) {
throw new RuntimeException("Stub!");
}
/** @deprecated */
@Deprecated
public void onDestroy() {
throw new RuntimeException("Stub!");
}
/** @deprecated */
@Deprecated
@Nullable
public IBinder onBind(Intent intent) {
throw new RuntimeException("Stub!");
}
/** @deprecated */
@Deprecated
protected abstract void onHandleIntent(@Nullable Intent var1);
}
分析
使用场景:离线下载
IntentService本质是采用Handler和HandlerThread方式:
- 通过HandlerThread单独开启一个名为IntentService的线程
- 创建一个名为ServcieHandler的内部Handler
- 把内部Handler与HandlerThread对应的子线程进行绑定
- 通过onStartCommand传递给服务intent,依次插入到工作队列中,并逐个发送给onHandlerIntent
- 通过onHandlerIntent()来依次处理所有Intent请求对象所对应的任务
注意事项:工作队列是顺序执行的,比如一个任务正在IntentService中执行的,此时你再发送一个新的任务请求,这个新的任务会一直等待直到前面一个任务执行完毕才开始执行。 - 由于onCreate()方法只会调用一次,所以只会创建一个工作线程
- 由于多次调用startService(intent)时,onStartCommand也会调用多次,不会创建新的工作线程,只是把消息假如消息队列中等待执行,所以,多次启动intentService会按顺序执行任务
- 如果服务停止会清除队列中的消息,后续事件得不到执行
IntentService和Service的区别
从属性和作用上来说
Service:本质上还是主线程(不是独立的工作进程和线程)所以不建议在Service中编写耗时的逻辑和操作,否则会引起ANR
IntertService:创建一个工作线程来处理多线程任务
Service:需要主动调用stopSelf来结束服务,而IntentService不需要,在所有的intent被处理完后,系统会自动关闭服务
IntentService与其他线程的区别
IntentService内部采用HnadlerThread实现,作用类似于后台线程
与后台线程相比,InentService是一种后台服务,优势是:优先级高(不容易被系统杀死),保证了任务的进行