多线程(四)、Android多线程使用及AsyncTask源码分析

1,594 阅读23分钟

本篇是多线程系列的第四篇,如果对前三篇感兴趣的也可以去看看。

多线程(一)、基础概念及notify()和wait()的使用

多线程(二)、内置锁 synchronized

多线程(三)、线程池 ThreadPoolExecutor 知识点总结

除了前面的线程池的使用外,在Android中,我们除了通过Thread创建线程外,还可以通过 AsyncTaskIntentServiceHandleThread 来创建,线程池前面一篇已经详细介绍了,下面对其他几个方法简单的介绍。

1.1、HandleThread

1.1.1、源码
public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }

    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }

    protected void onLooperPrepared() {
    }

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }

        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }

    public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }

    public int getThreadId() {
        return mTid;
    }
}

可以看到 HandlerThread 继承的是 Thread ,其在内部自己实现了 Looper,可以单独发送和接收消息,可以实现ui线程到子线程通信和子线程到子线程的通信。

1.1.2、使用

1、创建HandlerThread 并启动

        // 创建 HandlerThread 实例对象,参数为自定义线程名字,作为标记
        handlerThread = new HandlerThread("HandlerThreadTest");
        // 启动 HandlerThread
        handlerThread.start();

2、创建工作线程,并复写 handleMessage 方法

        // 创建工作线程,复写 handleMessage 方法
        workHandler = new Handler(handlerThread.getLooper()) {
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
                boolean isMain = Looper.myLooper() == Looper.getMainLooper();
                System.out.println("收到消息:what=" + msg.what + ",message=" + (String) msg.obj);
                System.out.println("IsMainThread=" + isMain + ",currentThread:" + Thread.currentThread());
            }
        };

3、发送消息 (这里模拟在子线程中发送消息)

    new Thread(new Runnable() {
                    @Override
                    public void run() {
                        // 使用工作线程向工作线程发送数据。
                        Message msg = Message.obtain();
                        msg.what = 1;
                        msg.obj = "测试数据";
                        workHandler.sendMessage(msg);
                        boolean isMain = Looper.myLooper() == Looper.getMainLooper();
                        System.out.println("IsMainThread=" + isMain + ",currentThread:" + Thread.currentThread());

                    }
                }).start();

4、释放

 // 释放
 mHandlerThread.quitSafely();

运行结果:

可以看到上面的示例实现了在两个子线程之间数据传送。

如果我们将发送消息放在ui线程,即实现了ui线程到子线程的数据通信。

1.2、IntentService

1.2.1、源码

同样的,intentService源码也不多,这里直接贴出来。

public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;

    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);
        }
    }

    public IntentService(String name) {
        super();
        mName = name;
    }

    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }

    @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 thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    @Override
    public void onStart(Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        mServiceLooper.quit();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    @WorkerThread
    protected abstract void onHandleIntent(Intent intent);
}

通过源码,可以看到 IntentService 是一个抽象类,还有一个抽象方法 onHandleIntent,继承至 Service

1.2.2、使用

新建一个MyIntentService继承至 IntentService

public class MyIntentService extends IntentService {


    private static final String TAG = "MyIntentService";

    public MyIntentService() {
        super("IntentService");
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG, "onCreate: ");
    }

    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {

        Log.i(TAG, "onStartCommand: ");
        return super.onStartCommand(intent, flags, startId);
    }


    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        boolean isMainThread = Looper.getMainLooper() == Looper.myLooper();

        Log.i(TAG, "onHandleIntent: isMainThread=" + isMainThread);
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        Log.i(TAG, "onHandleIntent: 任务结束");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i(TAG, "onDestroy");
    }
}

2、跟Service一样,需要在 AndroidManifest.xml 中进行注册

 <service android:name=".MyIntentService">

3、启动服务

startService(new Intent(this, MyIntentService.class));

运行结果:

可以看到在MyIntentServiceonHandleIntent 默认给我们开启了一个子线程来执行耗时操作,且当任务执行结束后自动停止服务,和Service的区别是 使用Service 可以同时执行多个请求,而使用IntentService 只能同时执行一个请求。

1.3、AsyncTask

AsyncTask 也是我们在Android中使用较为频繁的异步通信方法,我们可以通过 AsyncTask轻松的实现子线程到ui线程的通信,保证了线程安全,减少了我们通过 Thread + Handler 这种复杂的组合方法来实现,而且AsyncTask实现原理也是通过线程池,所以也具有上面线程池的好处。

1.3.1、使用
public abstract class AsyncTask<ParamsProgressResult>{
    // ......
}

首先可以看到 AsyncTask是一个抽象类,所以,我们在使用的时候需要创建自己的类来实现它,还要实现里面唯一的一个抽象方法。

protected abstract Result doInBackground(Params... params);

可以看到抽象方法有三个泛型参数,分别是.

Params : 开始异步任务执行时传入的参数类型,与 doInBackground()参数和执行时调用 excute() 参数类型一致。

Progress : 异步任务执行过程中,返回下载进度值的类型,与 onProgressUpdate() 参数类型一致。

Result : 异步任务执行完成后,返回的结果类型,与doInBackground()的返回值类型还有 onPostExecute() 参数类型一致

使用 AsyncTask 时,除了必须实现的抽象方法外,一般还有几个方法比较重要,可以根据我们的需求进行重写

  • onPreExecute:主线程,执行线程任务前自动调用,通过在这个方法里面进行相应的初始化操作。
  • doInBackground:子线程,必须实现的抽象方法,方法里面为工作线程,可以进行耗时操作。
  • onProgressUpdate:主线程,任务执行时候,获取进度,通过在 doInBackground 方法中调用 publishProgress 来触发
  • onPostExecute :主线程,当任务执行结束后自动调用,可进行UI更新。

简单示例:

// 新建 MyAsyncTask 实现 抽象类 AsyncTask 
// Params:Integer
// Progress:String
// Result:Double
public  class MyAsyncTask extends android.os.AsyncTask<IntegerStringDouble{
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            System.out.println(TAG + "--onPreExecute");
        }

        // 这里的参数类型为我们自己设置的Params 参数类型
        @Override
        protected Double doInBackground(Integer... integers) {
            // 模拟耗时操作
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(TAG + "--doInBackground  length=" + integers.length);

            for (Integer i : integers) {
                System.out.println(TAG + "--doInBackground  data=" + i);
                // 在 doInBackground 调用 publishProgress 触发 onProgressUpdate
                publishProgress(i + "—更新后的值");
            }
            // 这里模拟返回结果,参数类型为我们设置的 Double
            return 2.2;
        }

        // 这里的参数类型为我们自己设置的String类型 (Progress)
        @Override
        protected void onProgressUpdate(String... values) {
            super.onProgressUpdate(values);
            for (String data : values) {
                System.out.println(TAG + "--onProgressUpdate  date=" + data);
            }
        }

        // 任务执行结束后自动触发,参数类型为我们自己设置的Result 参数类型
        @Override
        protected void onPostExecute(Double aDouble) {
            super.onPostExecute(aDouble);
            System.out.println(TAG + "--onPostExecute  aDouble=" + aDouble);
        }
    }

调用 MyAsyncTask :

通过 execute 方法调用,需要在主线程里面执行,传入参数需要和 doInBackground 参数一致,及 Params 设置的类型。

只能调用一次,如果调用两次 execute 方法,会抛异常

Caused by: java.lang.IllegalStateException: Cannot execute task: the task is already running.

 MyAsyncTask myAsyncTask = new MyAsyncTask();
 myAsyncTask.execute(100,101,102);

结果:

1.3.2、源码分析

我们深入 AsyncTask 大致看看它是怎么实现的,首先从 构造方法入手

    public AsyncTask(@Nullable Looper callbackLooper) {
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);
        // 通过Callable创建一个任务
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    // 从 doInBackground 中获取
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    // 任务执行结束,将结果 发送到
                    postResult(result);
                }
                // 返回结果
                return result;
            }
        }; 
        // 将任务传给 FutureTask ,复写其 done 方法
        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                try {
                    postResultIfNotInvoked(get());
                } catch (InterruptedException e) {
                    android.util.Log.w(LOG_TAG, e);
                } catch (ExecutionException e) {
                    throw new RuntimeException("An error occurred while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
        };
    }


// WorkerRunnable
private static abstract class WorkerRunnable<ParamsResultimplements Callable<Result{
        Params[] mParams;
}

我们看到构造方法中,先创建了一个Callable的任务,在 多线程(一)、基础概念及notify()和wait()的使用 我们介绍了通过 Callable 来创建线程,可以在线程结束后获取结果,我们也看到在 任务里面通过 result = doInBackground(mParams) 获取到结果。任务创建后,将其放入到 FutureTask 中,这是 Callable 线程创建的标准用法。

看完构造方法,我们再来看调用 execute 方法执行的时候,它又做了啥。

    @MainThread
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }

这里没啥信息,直接调用了executeOnExecutor 方法,传入了 sDefaultExecutor 和我们设置的参数,再看 executeOnExecutor 方法:

public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,Params... params) {
        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();

        mWorker.mParams = params;
        // 执行了我们在构造方法里面创建的mFuture
        exec.execute(mFuture);

        return this;
    }

这里面我们还是看到很有用的信息,首先调用了 onPreExecute 方法,这也是我们在执行 AsyncTask 方法的时候,调用的第一个方法,用来进行一些初始化操作,我们的任务通过 exec.execute(mFuture) 进行执行,看看我们传入的 sDefaultExecutor 是个啥。

最终创建了 SerialExecutor

    private static class SerialExecutor implements Executor {
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;

        public synchronized void execute(final Runnable r) {
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            });
            if (mActive == null) {
                scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

内部声明了一个双端队列ArrayDeque类型的mTasks(双端队列中offer方法表示从队列尾插入,poll()表示从队列头获取元素)。

这里通过 mTasks.offer 将我们的任务不断的放入阻塞队列中,再通过 mTasks.poll() 拿出任务,最终通过 THREAD_POOL_EXECUTOR 来执行。

再看 THREAD_POOL_EXECUTOR 又是啥?

static {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                new SynchronousQueue<Runnable>(), sThreadFactory);
        threadPoolExecutor.setRejectedExecutionHandler(sRunOnSerialPolicy);
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
 }

我们前面说了,AsyncTask 内部是通过线程池来执行的,看这里应该就清楚了,任务的执行,实质是通过创建的 threadPoolExecutor线程池,来执行任务。按照前面的老规矩,看看具体参数。

参数说明:

    private static final int CORE_POOL_SIZE = 1;
    private static final int MAXIMUM_POOL_SIZE = 20;
    private static final int KEEP_ALIVE_SECONDS = 3;

int corePoolSize: 1

int maximumPoolSize: 20

long keepAliveTime:3

TimeUnit unit:TimeUnit.SECONDS

BlockingQueue workQueue:new SynchronousQueue()

1个核心线程,非核心线程数目为20,使用 SynchronousQueue 阻塞队列。

这里源码是查看的API 29 ,不同的版本有区别。

看到了任务的执行,那么AsyncTask是怎么实现子线程到主线程的数据通信的呢?

postResult:

    private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }

前面构造中,我们看到通过 doInBackground 返回的结果,通过 postResult 方法传递出去了,而在 postResult 里面就是通过Handler,来将数据从子线程传到了主线程。

    private static class InternalHandler extends Handler {
        public InternalHandler(Looper looper) {
            super(looper);
        }

        @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;
            }
        }
    }

我们再看看publishProgress

protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {
            getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
        }
    }

其本质,也是通过 Handler 来实现子线程到主线程的数据通信。

总结:所以,AsyncTask 本质是通过线程池来执行任务,并且封装了 Handler 来实现子线程到ui线程的数据传输。

优点:AsyncTask 是一个轻量级的异步任务处理类,轻量级体现在,使用方便、代码简洁上,而且整个异步任务的过程可以通过cancel()进行控制。

缺点:不适用于处理长时间的异步任务,一般这个异步任务的过程最好控制在几秒以内,如果是长时间的异步任务就需要考虑多线程的控制问题;当处理多个异步任务时,UI更新变得困难。

二、总结

多线程部分一共四篇,这篇收了个尾,后面也会继续对Android其他知识点进行总结,Android进阶系列也会一直写下去,如果文中有错误的地方,欢迎大佬们批评指点。