Android开发实践:掌握AsyncTask、HandlerThread和线程池的使用方法与技巧

一、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中,IntentServiceJobIntentService都是基于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中,然后发送到HandlerHandler在工作线程中处理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

总的来说,IntentServiceJobIntentService都是通过在单独的线程中执行任务来实现后台运行的,而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 的用法的详细阐述。

  1. 创建 ThreadPoolExecutor

要创建 ThreadPoolExecutor,请使用其构造函数:

java 复制代码
ThreadPoolExecutor executor = new ThreadPoolExecutor(
        int corePoolSize, // 核心线程数
        int maximumPoolSize, // 最大线程数
        long keepAliveTime, // 空闲线程的存活时间
        TimeUnit unit, // 存活时间的单位
        BlockingQueue<Runnable> workQueue, // 工作队列
        ThreadFactory threadFactory, // 线程工厂
        RejectedExecutionHandler handler // 拒绝策略
);
  1. 提交任务

使用 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!";
    }
});
  1. 关闭线程池

在不再需要线程池时,调用 shutdown() 方法来关闭线程池。这将等待所有任务完成后关闭线程池:

java 复制代码
executor.shutdown();

如果需要立即关闭线程池,可以调用 shutdownNow() 方法。这将尝试停止所有正在执行的任务,并返回等待执行的任务列表:

java 复制代码
List<Runnable> pendingTasks = executor.shutdownNow();
  1. 获取线程池状态

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应用的性能和响应速度,从而提升用户体验。同时,要注意避免内存泄漏、线程阻塞等问题,确保应用的稳定运行。

相关推荐
Eastsea.Chen2 小时前
MTK Android12 user版本MtkLogger
android·framework
长亭外的少年9 小时前
Kotlin 编译失败问题及解决方案:从守护进程到 Gradle 配置
android·开发语言·kotlin
建群新人小猿11 小时前
会员等级经验问题
android·开发语言·前端·javascript·php
1024小神13 小时前
tauri2.0版本开发苹果ios和安卓android应用,环境搭建和最后编译为apk
android·ios·tauri
兰琛13 小时前
20241121 android中树结构列表(使用recyclerView实现)
android·gitee
Y多了个想法13 小时前
RK3568 android11 适配敦泰触摸屏 FocalTech-ft5526
android·rk3568·触摸屏·tp·敦泰·focaltech·ft5526
NotesChapter14 小时前
Android吸顶效果,并有着ViewPager左右切换
android
_祝你今天愉快16 小时前
分析android :The binary version of its metadata is 1.8.0, expected version is 1.5.
android
暮志未晚Webgl16 小时前
109. UE5 GAS RPG 实现检查点的存档功能
android·java·ue5
麦田里的守望者江16 小时前
KMP 中的 expect 和 actual 声明
android·ios·kotlin