Android 多线程

简介

1. 线程分类

  • 主线程(UI线程) : 处理和界面相关的事情.
  • 子线程 : 处理耗时操作.

Android中规定, 只有在主线程中可以进行UI操作, 但是同时, 主线程中不能进行耗时操作,否则会产生ANR,因此耗时操作必须放到子线程中进行处理.

2. Android中多线程技术

  • Thread
  • AsyncTask : 底层用到了线程池.

    封装了线程池和Handler, 主要使用场景是在子线程中更新UI.

  • IntentService : 底层使用Thread
    内部采用HandlerThread来执行任务,当任务执行完成后IntentService就会自动退出. 从任务执行的角度看,IntentService很像是一个后台线程,其实他是一个Service,正是因为他是服务所以不容易被系统杀死从而保证任务的正常进行.
  • HandlerThread : 底层使用Thread
    具有消息循环的线程, 内部可以使用Handler.

注意一 : 从Android 3.0 开始如果在子线程中进行网络操作就会产生NetworkOnMainThreadException

AsyncTask

AsyncTask 是一个轻量级异步任务类, AsyncTask 封装了Thread和Handler, 通过AsyncTask可以更加方便地执行后台任务以及在主线程中访问UI, 但是AsyncTask并不适合进行特别耗时的后台任务,对于特别耗时的后台任务来说建议使用线程池.

1. AsyncTask 简单使用

  • 创建自定义任务类,继承自AsyncTask.
/**
 * 自定义 AsyncTask 类.
 * P1 : 输入参数类型
 * P2 : 进度类型
 * P3 : 返回值类型
 */
private class DownloadTask extends AsyncTask<Integer, Integer, Integer> {
    /**
     * 1. 主线程
     * 2. 执行任务之前执行, 常用来初始化任务,显示UI等.
     */
    @Override
    protected void onPreExecute() {
        Log.d(TAG, "onPreExecute: " + Thread.currentThread().getId());
    }
    /**
     * 1. 子线程
     * 2. onPreExecute执行后会立即执行这个方法.任务逻辑就是在这个方法中进行的.
     * 3. 可以通过 publicProgress(Progress...) 将任务进度传递到onProgressUpdate.
     * 4. 任务结果通过返回值返回, return.传递到onPostExecute.
     * @param params P2
     * @return P3
     */
    @Override
    protected Integer doInBackground(Integer... params) {
        Log.d(TAG, "doInBackground: " + Thread.currentThread().getId());
        for (int i = 0; i < params[0]; i++) {
            // 检查任务是否被取消
            if (isCancelled()) {
                return -1;
            }
            publishProgress(i);
        }
        return 0;
    }
    /**
     * 1. 主线程
     * 2. publicProgress被调用后会将进度传递到这个方法中.可以在这进行UI更新操作.
     * @param values 任务进度.
     */
    @Override
    protected void onProgressUpdate(Integer... values) {
        Log.d(TAG, "onProgressUpdate: " + Thread.currentThread().getId());
        Log.d(TAG, "onProgressUpdate: " + values[0]);
    }
    /**
     * 任务执行结束后,就会进入这个回调.
     * @param integer 任务结果
     */
    @Override
    protected void onPostExecute(Integer integer) {
        Log.d(TAG, "onPostExecute: " + Thread.currentThread().getId());
        Log.d(TAG, "onPostExecute: " + integer);
    }
    /**
     * 任务取消后调用
     * @param integer doInBackground 返回值.
     */
    @Override
    protected void onCancelled(Integer integer) {
        super.onCancelled(integer);
    }
}
  • 启动任务
// 创建并启动任务
new DownloadTask().execute(5);
  • 输出
//
D/MainActivity: onPreExecute: 1
//
D/MainActivity: doInBackground: 161
//
D/MainActivity: onProgressUpdate: 1 // 线程id
D/MainActivity: onProgressUpdate: 0 // 进度
D/MainActivity: onProgressUpdate: 1
D/MainActivity: onProgressUpdate: 1 // 进度
D/MainActivity: onProgressUpdate: 1
D/MainActivity: onProgressUpdate: 2 // 进度
D/MainActivity: onProgressUpdate: 1
D/MainActivity: onProgressUpdate: 3 // 进度
D/MainActivity: onProgressUpdate: 1
D/MainActivity: onProgressUpdate: 4 // 进度
//
D/MainActivity: onPostExecute: 1 // 线程id
D/MainActivity: onPostExecute: 0 // 结果

代码中已经有了详细的注释,不进行过多解释,通过Log也证实了.这里说下取消任务

AsyncTask 提供了 cancel(boolean) 取消任务.
1. 参数含义:是否立即中断线程执行,true表示立即终止线程,false表示允许任务完成.
2. 调用了cancel()onCancelled() 回调会被执行(UI线程), onPostExecute() 不会被执行了.
3. 调用后 isCancelled 会返回 true.
4. 建议onInBackground() 中检查 isCancelled 以便尽快结束任务.

2. AsyncTask使用注意

  • AsyncTask这个类必须在主线程中进行加载. 在Android 4.1 以后系统自动完成这一过程.Android 6.0 以后可以在子线程中加载
  • AsyncTask必须在主线程中创建.
  • execute() 必须在UI线程中进行调用.
  • 不要在直接调用 onPreExecute(), onPostExecute(), onInBackground() ,onProgressUpdate() 等方法.
  • 一个AsyncTask 对象 只能调用一次execute方法,如果执行多个任务就创建多个任务.
  • Android3.0 以后 ,AsyncTask用一个线程串行执行任务.

3. AsyncTask的工作原理

  • 首先分析execute() 方法.由于它是直接调用了executeOnExecutor()因此主要分析一下后者
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    // 调用 : executeOnExecutor 
    // sDefaultExecutor 是一个串行的线程池Executor.
    return executeOnExecutor(sDefaultExecutor, params);
}
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
        Params... params) {
    // 检查状态, 此处可用证明为什么一个AsyncTask对象只可以调用一次execute方法.
    if (mStatus != Status.PENDING) {
        switch (mStatus) {
            case RUNNING:  // 如果任务已经执行,再次执行就会抛出异常.
                throw new IllegalStateException("Cannot execute task:"
                        + " the task is already running.");
            case FINISHED: // 如果任务已经执行完了,再次执行就会抛出异常.
                throw new IllegalStateException("Cannot execute task:"
                        + " the task has already been executed "
                        + "(a task can be executed only once)");
        }
    }

    // 设置为运行状态.
    mStatus = Status.RUNNING;
    // 首先调用 onPreExecute, 证实了onPreExecute他回调最先执行.
    // onPreExecute在主线程中执行, 这也是AsyncTask的execute()必须在主线程调用的一个的原因.
    onPreExecute();
    // 保存参数
    mWorker.mParams = params;
    // 执行线程
    // mFuture 是一个Runnable.
    exec.execute(mFuture);

    return this;
}

代码中已经有了详解的注释这里就不过多的解释了.
1. @MainThread 注解说明这两个方法都必须在主线程中执行,因此execute()方法必须在主线程中调用.
2. if (mStatus != Status.PENDING){...}中的判断说明了,一个AsyncTask对象只可以调用一次execute() 方法.
3. sDefaultExecutor是一个串行的线程池主要用于任务排队,并不负责任务的实际执行,源码分析如下

// sDefaultExecutor 定义
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
// SERIAL_EXECUTOR : 用于任务排队.
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
// SerialExecutor 串行线程池
private static class SerialExecutor implements Executor {
    final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
    Runnable mActive;
    // 执行Runnable
    public synchronized void execute(final Runnable r) {
        mTasks.offer(new Runnable() {
            public void run() {
                try {
                    r.run();
                } finally {
                    // 调度下一个.
                    scheduleNext();
                }
            }
        });
        if (mActive == null) {
            scheduleNext();
        }
    }
    // 启动下一个Runnable
    protected synchronized void scheduleNext() {
        if ((mActive = mTasks.poll()) != null) {
            // 执行任务.
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}
// mFuture是一个Runnable
private final FutureTask<Result> mFuture;
  1. THREAD_POOL_EXECUTOR 也是一个线程池,负责任务的执行.
// 定义
public static final Executor THREAD_POOL_EXECUTOR;
// 静态加载线程池.
static {
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
            CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
            sPoolWorkQueue, sThreadFactory);
    threadPoolExecutor.allowCoreThreadTimeOut(true);
    THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
  1. sHandler 负责线程环境的切换.
// 7.0 代码
// 负责线程切换
private static InternalHandler sHandler;
// 获取Handler
private static Handler getHandler() {
    synchronized (AsyncTask.class) {
        if (sHandler == null) {
            sHandler = new InternalHandler();
        }
        return sHandler;
    }
}
// 该类负责实现线程的切换
private static class InternalHandler extends Handler {
    // 7.0 
    public InternalHandler() {
        super(Looper.getMainLooper());
    }
    @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
    @Override
    public void handleMessage(Message msg) {
        AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
        switch (msg.what) {
            case MESSAGE_POST_RESULT: // 任务执行结束
                // There is only one result
                result.mTask.finish(result.mData[0]);
                break;
            case MESSAGE_POST_PROGRESS: // 更新进度.
                result.mTask.onProgressUpdate(result.mData);
                break;
        }
    }
}
// 任务结束函数
private void finish(Result result) {
    if (isCancelled()) {
        // 任务被取消
        onCancelled(result);
    } else {
        // 任务正常完成
        onPostExecute(result);
    }
    mStatus = Status.FINISHED;
}

Android 5.0 代码

// 静态加载.
private static final InternalHandler sHandler = new InternalHandler();
private static class InternalHandler extends Handler {
    @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
    @Override
    public void handleMessage(Message msg) {
        AsyncTaskResult result = (AsyncTaskResult) msg.obj;
        switch (msg.what) {
            case MESSAGE_POST_RESULT:
                // There is only one result
                result.mTask.finish(result.mData[0]);
                break;
            case MESSAGE_POST_PROGRESS:
                result.mTask.onProgressUpdate(result.mData);
                break;
        }
    }
}
  1. sHandler 是一个静态的Handler对象,Android 6.0 以前,为了能够将执行环境切换到主线程,这就要求sHandler这个对象必须在主线程中进行创建.
  2. Android 6.0 以后 sHandler 使用了懒加载,因此,在子线程中也可以进行加载AsyncTask.同时在ActivityThread的main() 方法中也去掉了AsyncTask.init(); 也证实了这一点.

5 总结

  • AsyncTask 本质上就是封装了线程池和Handler
  • Android 6.0 以后可以在子线程中加载AsyncTask.

HandlerThread

HandlerThread 继承了Thread ,添加加了Looper, 实现方式如下:
- 通过Looper.prepare() 来创建消息队列.
- 通过Looper.loop() 来开启消息循环.

@Override
public void run() {
    mTid = Process.myTid();
    // 创建消息队列.
    Looper.prepare();
    synchronized (this) {
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    // 子类实现.
    onLooperPrepared();
    // 开启运行循环.
    Looper.loop();
    mTid = -1;
}
  • 普通的Thread主要用于进行耗时操作.HandlerThread内部常见了消息队列,外界需要通过Handler的消息方式通知HandlerThread来执行一个任务.由于HandlerThread的run() 是一个无限循环(Looper.loop()),因此当明确不需要时需要主动调用,quit() 或者 quitSafely() 来终止线程的执行.

关于HandlerThread的使用方式参考IntentService源码

IntentService

1. 简介

  1. IntentService是一种特殊的服务, 他继承自Service并且他是一个抽象类.
  2. IntentService用于后台耗时任务, 任务执行结束后自动停止.
  3. 由于它是一个服务因此有着比普通Thread更高的优先级,不容易被系统杀死.因此他适合执行一些优先级较高的后台任务.

2. 工作原理

  • IntentService 其实就是封装了 HandlerHandlerThread从下面的源码分析中可以看出来.
// 1. Handler
private volatile ServiceHandler mServiceHandler;
// 定义Handler
private final class ServiceHandler extends Handler {
    public ServiceHandler(Looper looper) {
        super(looper);
    }
    @Override
    public void handleMessage(Message msg) {
        // onHandleIntent 这个方法是子类来实现的.
        onHandleIntent((Intent)msg.obj);
        // 停止当前服务.
        stopSelf(msg.arg1);
    }
}
// IntentService 的onCreate方法
@Override
public void onCreate() {
    // TODO: It would be nice to have an option to hold a partial wakelock
    // during processing, and to have a static startService(Context, Intent)
    // method that would launch the service & hand off a wakelock.
    super.onCreate();
    // 创建并启动了HandlerThread.
    HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
    thread.start();
    // 保存Looper
    mServiceLooper = thread.getLooper();
    // 创建Handler , 并且将创建的Handler 和 HandlerThread 的Looper绑定在一起.
    mServiceHandler = new ServiceHandler(mServiceLooper);
}
  1. mServiceHandler就是一个Handler , 并且他的handleMessage处理逻辑也十分简单,首先调用onHandleIntent将结果返给子类处理. 然后调用stopSelf() 来停止服务.
  2. 在onCreate() 方法中创建了一个HandlerThread 并且将Handler和他绑定.

IntentService工作原理如下:

  1. 在onCreate() 中创建Handler和HandlerThread.并绑定.
  2. onStart() 中通过Handler 发送消息.
  3. 在 Handler中处理调用onHandleIntent()进行耗时操作.
  4. 数据通过Intent进行传输.
  5. 任务执行结束后,后停止服务.
  6. onDestory() 中退出HandlerThread.

工作原理

3. 使用

// 定义
public class LocalIntentService extends IntentService{
    /**
     * 构造方法
     */
    public LocalIntentService() {
        super("123");
    }
    @Override
    protected void onHandleIntent(Intent intent) {
        String action = intent.getStringExtra("task_action");
        Log.d(TAG, "onHandleIntent: " + action);
        // 延时
        SystemClock.sleep(500);
    }
    @Override
    public void onDestroy() {
        Log.d(TAG, "onDestroy: 服务结束");
        super.onDestroy();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand: ");
        return super.onStartCommand(intent, flags, startId);
    }
}
Intent intent = new Intent(this, LocalIntentService.class);
intent.putExtra("task_action", "任务 1");
startService(intent);
// 任务二
intent.putExtra("task_action", "任务 2");
startService(intent);
SystemClock.sleep(2000);
// 任务三
intent.putExtra("task_action", "任务 3");
startService(intent);

运行代码发现上述三个任务串行执行,最后会停止服务.

Android中的线程池

Android线程池的概念源自于Java的Executor, Executor 是一个接口,ThreadPoolExecutor 是他的实现类.ThreadPoolExecutor提过了一系列的参数来配置线程池.
Android中的线程池主要分为四类.后面会详细介绍.

1. 线程池的好处

  • 重用线程池中的线程, 避免线程的创建和销毁带来的性能开销.
  • 能有效的控制线程池的最大并发数, 避免大量线程之间因为抢夺子系统资源而导致的阻塞现象.
  • 能够对象成进行简单的管理, 并提供定时执行以及定时间隔循环执行的功能.

2. ThreadPoolExecutor

  • 构造方法
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory);
  1. corePoolSize : 线程池的核心线程数,默认情况下核心线程会在线程池中一直存活.如果ThreadPoolExecutor 的 allowCoreThreadTimeOut 属性设置为true, 那么闲置的核心线程会有超时策略,超时时间由keepAliveTime指定.
  2. maximumPoolSize : 线程池最大的线程数.达到最大值后新任务会阻塞.
  3. keepAliveTime : 超时时间.
  4. unit : 超时单位,MILLISECONDS(毫秒),SECONDS(秒), MINUTES(分钟)
  5. workQueue: 线程池中的任务队列,通过线程池的execute方法提交的Runnable对象会存储在这里.
  6. threadFactory : 线程工厂, 为线程池提供创建新线程的功能.

除了上面的主要参数以外, ThreadPoolExecuteor还有一个不常用的参数RejectedExecutionHandler handler. 当线程池无法执行新的任务时会调用handler 的rejectedExeception方法来通知调用者, 默认情况下 rejectedExecution 方法会抛出一个rejectedExecutionException异常,具体的可以查看API文档.

  • ThreadPoolExecutor 执行时遵循以下规则:

    • 如果线程池中的线程为达到核心线程的数量, 那么会直接启动一个核心线程来执行任务.
    • 如果线程池中的线程数量已经达到或者超过核心线程数量, 那么任务会插入任务队列中进行排队.
    • 如果过步骤2中无法将任务插入到任务队列,一般是队列已满,这个时候如果线程数量没有达到线程池的最大数量限制,那么会立即启动一个非核心线程来执行任务.
    • 如果步骤3中线程数量已经到了线程池规定的最大值, 那么就拒绝执行此任务,ThreadPoolExecutor会调用RejectedExecutionHandler的rejectedExecution() 方法来通知调用者.
  • AsyncTask中的线程池配置

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
// We want at least 2 threads and at most 4 threads in the core pool,
// preferring to have 1 less than the CPU count to avoid saturating
// the CPU with background work
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;
static {
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
            CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
            sPoolWorkQueue, sThreadFactory);
    threadPoolExecutor.allowCoreThreadTimeOut(true);
    THREAD_POOL_EXECUTOR = threadPoolExecutor;
}

3. Android中的四种线程池.

Android中常用有四种线程池,他们都是直接或者间接配置ThreadPoolExecutor 来实现自己的功能的.

3.1 FixedThreadPool
  • 通过Executors 的newFixedThreadPool 方法来创建.
  • 线程数量固定.
  • 线程处于空闲状态时, 线程也不会被回收,除非线程池关闭了.正是这个原因所以它能够更加快速第响应外界请求.
  • 当所有的线程都处于活动状态时, 新任务处于等待状态, 直到有线程空闲.
  • 没有超时机制
  • 没有任务熟练限制.

newFixedThreadPool方法源码

public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
}
3.2 CachedThreadPool
  • 通过Executors的newCachedThreadPool 来创建, 它是一种线程数量不定的线程池,他只有非核心线程并且最大线程数为integer.MAX_VALUE.
  • 当线程池中的活动都处于活动状态时,会直接创建新的线程来处理任务.否则就利用空闲线程来处理任务.
  • 超时时间是 60s ,线程闲置60s就会被回收.
  • 这种线程适合大量的耗时较小的任务.

newCachedThreadPool源码

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
}
3.3 ScheduledThreadPool
  • 通过Executors的newScheduledThreadPool方法来创建.
  • 核心线程数是固定的,非核心线程数没有限制.当非核心线程一旦闲置就会被立即回收.
  • ScheduledThreadPool主要用来执行定时任务和具有固定周期的重复任务.

newScheduledThreadPool源码

public static ScheduledExecutorService newScheduledThreadPool(
        int corePoolSize, ThreadFactory threadFactory) {
    return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
3.4 SingleThreadPool
  • 通过Executors的newSingleThreadExecutor方法创建.
  • 只有一个核心线程,他可以确保所有的任务都在同一个线程中顺序执行.
  • 他可以统一所有的外界任务到一个线程中,这样可以避免线程同步问题.

newSingleThreadExecutor源码

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

4. 四种常用线程池的用法

private void threadPoolTest() {
    // 创建任务
    Runnable command = new Runnable() {
        @Override
        public void run() {
            Log.d(TAG, "run: " + Thread.currentThread().getId());
            SystemClock.sleep(2000);
        }
    };
    // FixedThreadPool
    ExecutorService fixed = Executors.newFixedThreadPool(4);
    fixed.execute(command);
    // Cached
    ExecutorService cached = Executors.newCachedThreadPool();
    cached.execute(command);
    // Scheduled
    ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(4);
    // 2000 ms 后执行
    scheduled.schedule(command, 2000, TimeUnit.MILLISECONDS);
    // 延时 10 后每隔 1000ms 执行一次
    scheduled.scheduleAtFixedRate(command, 10, 1000, TimeUnit.MICROSECONDS);
    // Single
    ExecutorService single = Executors.newSingleThreadExecutor();
    single.execute(command);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值