一、AsyncTask
AsyncTask是一种轻量级的异步处理机制,适用于简单的后台任务,例如网络请求、文件读写等。AsyncTask可以自动处理线程切换,将结果回调到主线程。
1.1 使用方法
- 创建一个继承自AsyncTask的子类,定义三个泛型参数:Params(传入参数类型)、Progress(进度类型)和Result(返回结果类型)。
- 实现
doInBackground()
方法,执行后台任务。在这个方法中,可以调用publishProgress()
来更新进度。 - 如果需要,可以重写
onPreExecute()
、onProgressUpdate()
和onPostExecute()
方法,分别在任务开始前、进度更新和任务完成后执行。
1.2 技巧
- 避免在AsyncTask中引用Activity或其他可能导致内存泄漏的对象。可以使用WeakReference来避免内存泄漏。
- 不要在AsyncTask中执行长时间运行的任务,因为它可能会阻塞线程池中的其他任务。
二、HandlerThread
HandlerThread是一种使用Handler处理消息队列的线程。它继承自Thread类,内部封装了一个Looper,可以处理来自其他线程的消息。
2.1 使用方法
- 创建一个HandlerThread实例,并调用
start()
方法启动线程。 - 调用
getLooper()
方法获取HandlerThread的Looper,并用它创建一个Handler实例。 - 使用Handler的
post()
、postDelayed()
或sendMessage()
方法将任务发送到HandlerThread的消息队列。
2.2 技巧
- 在不再需要HandlerThread时,调用
quit()
或quitSafely()
方法来停止线程。 - 可以使用HandlerThread来处理多个相关任务,以避免频繁地创建和销毁线程。
- 在Android中,
IntentService
和JobIntentService
都是基于Service
组件实现的后台服务。它们的工作原理主要通过创建工作线程来在后台执行任务,以避免阻塞主线程。下面我们结合Android源码,来看一下它们是如何实现后台运行的。
三、IntentService和JobIntentService
3.1 IntentService
IntentService
在其内部创建了一个工作线程来执行任务。当你启动IntentService
时,它会创建一个工作线程,并在这个线程中调用onHandleIntent(Intent intent)
方法。你可以在onHandleIntent(Intent intent)
方法中执行耗时操作,而不会阻塞主线程。当所有任务执行完后,IntentService
会自动停止。
它的主要代码如下:
java
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
在onStart()
方法中,将Intent
封装到Message
中,然后发送到Handler
。Handler
在工作线程中处理Message
,调用onHandleIntent(Intent intent)
方法来执行任务,任务执行完后调用stopSelf(int startId)
方法来停止服务。
3.2 JobIntentService
JobIntentService
的实现方式取决于Android的版本。
-
在Android 7.1及以下版本,
JobIntentService
的行为与IntentService
相同,它在一个单独的线程中处理工作,当所有任务完成后,服务自动停止。 -
在Android 8.0及以上版本,由于后台服务的启动受到了限制,
JobIntentService
使用JobScheduler
来调度任务。JobScheduler
是Android 5.0引入的一种服务,它可以在满足特定条件(如设备充电、设备空闲等)时执行任务。
它的主要代码如下:
java
static final class JobServiceEngineImpl extends JobServiceEngine
implements JobIntentService.CompatJobEngine {
final JobIntentService mService;
final Object mLock = new Object();
JobParameters mParams;
JobServiceEngineImpl(JobIntentService service) {
super(service);
mService = service;
}
@Override
public IBinder compatGetBinder() {
return getBinder();
}
@Override
public boolean onStartJob(JobParameters params) {
synchronized (mLock) {
mParams = params;
}
mService.ensureProcessorRunningLocked(false);
return true;
}
@Override
public boolean onStopJob(JobParameters params) {
boolean result = mService.doStopCurrentWork();
synchronized (mLock) {
mParams = null;
}
return result;
}
}
在onStartJob(JobParameters params)
方法中,将JobParameters
保存起来,然后启动工作线程来处理任务。在onStopJob(JobParameters params)
方法中,停止当前的工作,并将JobParameters
置为null
。
总的来说,IntentService
和JobIntentService
都是通过在单独的线程中执行任务来实现后台运行的,而JobIntentService
还可以利用JobScheduler
在满足特定条件时执行任务,这使得JobIntentService
在Android 8.0及以上版本中能够更好地适应系统的后台限制。
四、常见的线程池类型
线程池是一种管理线程的高效方式,可以重用线程,减少线程创建和销毁的开销。在Android中,可以使用Java标准库中的java.util.concurrent
包中的ThreadPoolExecutor类或Executors工具类来创建线程池。
4.1 常见的线程池类型及其用法
4.1.1 FixedThreadPool(固定大小线程池)
FixedThreadPool是一个拥有固定数量线程的线程池。这种线程池可以用于执行并发数量有限的任务,例如CPU密集型任务。
用法:
java
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(4);
fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
// 执行任务
}
});
4.1.2 CachedThreadPool(缓存线程池)
CachedThreadPool是一个可以根据需要创建新线程的线程池。这种线程池适用于执行大量短时间任务的场景,例如I/O密集型任务。
用法:
java
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
cachedThreadPool.execute(new Runnable() {
@Override
public void run() {
// 执行任务
}
});
4.1.3 SingleThreadExecutor(单线程线程池)
SingleThreadExecutor是一个只有一个线程的线程池。这种线程池可以用于保证任务按顺序执行,例如需要顺序处理的任务。
用法:
java
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
singleThreadExecutor.execute(new Runnable() {
@Override
public void run() {
// 执行任务
}
});
4.1.4 ScheduledThreadPoolExecutor(定时线程池)
ScheduledThreadPoolExecutor是一个可以执行定时任务或周期性任务的线程池。
用法:
java
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(4);
// 延迟1秒后执行任务
scheduledThreadPool.schedule(new Runnable() {
@Override
public void run() {
// 执行任务
}
}, 1, TimeUnit.SECONDS);
// 延迟1秒后,每隔2秒执行一次任务
scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
// 执行任务
}
}, 1, 2, TimeUnit.SECONDS);
4.2 技巧
- 根据任务的特点选择合适的线程池类型。例如,对于CPU密集型任务,可以使用固定大小的线程池;对于I/O密集型任务,可以使用缓存线程池。
- 在不再需要线程池时,调用
shutdown()
或shutdownNow()
方法来关闭线程池,释放资源。
五、自定义线程池
ThreadPoolExecutor 是 Java 并发库中的一个类,用于创建和管理自定义线程池。它提供了灵活的线程池配置选项,适用于各种任务场景。以下是关于 ThreadPoolExecutor 的用法的详细阐述。
- 创建 ThreadPoolExecutor
要创建 ThreadPoolExecutor,请使用其构造函数:
java
ThreadPoolExecutor executor = new ThreadPoolExecutor(
int corePoolSize, // 核心线程数
int maximumPoolSize, // 最大线程数
long keepAliveTime, // 空闲线程的存活时间
TimeUnit unit, // 存活时间的单位
BlockingQueue<Runnable> workQueue, // 工作队列
ThreadFactory threadFactory, // 线程工厂
RejectedExecutionHandler handler // 拒绝策略
);
- 提交任务
使用 execute()
方法提交 Runnable 任务:
java
executor.execute(new Runnable() {
@Override
public void run() {
// 执行任务
}
});
或者使用 submit()
方法提交 Callable 任务,该方法会返回一个 Future
对象,可以用于获取任务的执行结果:
java
Future<String> future = executor.submit(new Callable<String>() {
@Override
public String call() {
// 执行任务并返回结果
return "Hello, ThreadPoolExecutor!";
}
});
- 关闭线程池
在不再需要线程池时,调用 shutdown()
方法来关闭线程池。这将等待所有任务完成后关闭线程池:
java
executor.shutdown();
如果需要立即关闭线程池,可以调用 shutdownNow()
方法。这将尝试停止所有正在执行的任务,并返回等待执行的任务列表:
java
List<Runnable> pendingTasks = executor.shutdownNow();
- 获取线程池状态
ThreadPoolExecutor 提供了一些方法来获取线程池的状态,例如:
getPoolSize()
:获取线程池中的当前线程数。getActiveCount()
:获取线程池中正在执行任务的线程数。getCompletedTaskCount()
:获取线程池已完成的任务数。getTaskCount()
:获取线程池已接收的任务总数(包括已完成、正在执行和等待执行的任务)。
示例:
java
System.out.println("线程池大小: " + executor.getPoolSize());
System.out.println("活跃线程数: " + executor.getActiveCount());
System.out.println("已完成任务数: " + executor.getCompletedTaskCount());
System.out.println("总任务数: " + executor.getTaskCount());
总之,ThreadPoolExecutor 提供了灵活的线程池配置选项,使得我们可以根据任务的特点创建合适的线程池。在使用 ThreadPoolExecutor 时,需要注意合理地配置参数,以及在适当的时机关闭线程池,以免造成资源泄漏。
六、总结
通过合理地使用AsyncTask、HandlerThread和线程池等子线程技术,可以有效地提高Android应用的性能和响应速度,从而提升用户体验。同时,要注意避免内存泄漏、线程阻塞等问题,确保应用的稳定运行。