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

相关推荐
还鮟2 小时前
CTF Web的数组巧用
android
小蜜蜂嗡嗡3 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi003 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体
zhangphil5 小时前
Android理解onTrimMemory中ComponentCallbacks2的内存警戒水位线值
android
你过来啊你5 小时前
Android View的绘制原理详解
android
移动开发者1号8 小时前
使用 Android App Bundle 极致压缩应用体积
android·kotlin
移动开发者1号8 小时前
构建高可用线上性能监控体系:从原理到实战
android·kotlin
ii_best13 小时前
按键精灵支持安卓14、15系统,兼容64位环境开发辅助工具
android
美狐美颜sdk13 小时前
跨平台直播美颜SDK集成实录:Android/iOS如何适配贴纸功能
android·人工智能·ios·架构·音视频·美颜sdk·第三方美颜sdk
恋猫de小郭18 小时前
Meta 宣布加入 Kotlin 基金会,将为 Kotlin 和 Android 生态提供全新支持
android·开发语言·ios·kotlin