本篇是多线程系列的第四篇,如果对前三篇感兴趣的也可以去看看。
多线程(一)、基础概念及notify()和wait()的使用
多线程(二)、内置锁 synchronized
多线程(三)、线程池 ThreadPoolExecutor 知识点总结
除了前面的线程池的使用外,在Android中,我们除了通过Thread
创建线程外,还可以通过 AsyncTask
、IntentService
、HandleThread
来创建,线程池前面一篇已经详细介绍了,下面对其他几个方法简单的介绍。
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
中进行注册
3、启动服务
startService(new Intent(this, MyIntentService.class));
运行结果:
可以看到在MyIntentService
中 onHandleIntent
默认给我们开启了一个子线程来执行耗时操作,且当任务执行结束后自动停止服务,和Service的区别是 使用Service 可以同时执行多个请求,而使用IntentService 只能同时执行一个请求。
1.3、AsyncTask
AsyncTask
也是我们在Android中使用较为频繁的异步通信方法,我们可以通过 AsyncTask
轻松的实现子线程到ui线程的通信,保证了线程安全,减少了我们通过 Thread
+ Handler
这种复杂的组合方法来实现,而且AsyncTask
实现原理也是通过线程池,所以也具有上面线程池的好处。
1.3.1、使用
public abstract class AsyncTask{
// ......
}
首先可以看到 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 {
@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() {
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(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 implements Callable {
Params[] mParams;
}
我们看到构造方法中,先创建了一个Callable
的任务,在 多线程(一)、基础概念及notify()和wait()的使用 我们介绍了通过 Callable
来创建线程,可以在线程结束后获取结果,我们也看到在 任务里面通过 result = doInBackground(mParams)
获取到结果。任务创建后,将其放入到 FutureTask
中,这是 Callable
线程创建的标准用法。
看完构造方法,我们再来看调用 execute
方法执行的时候,它又做了啥。
@MainThread
public final AsyncTask execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
这里没啥信息,直接调用了executeOnExecutor
方法,传入了 sDefaultExecutor
和我们设置的参数,再看 executeOnExecutor
方法:
public final AsyncTask 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 mTasks = new ArrayDeque();
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(), 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(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
其本质,也是通过 Handler
来实现子线程到主线程的数据通信。
总结:所以,AsyncTask 本质是通过线程池来执行任务,并且封装了 Handler
来实现子线程到ui线程的数据传输。
优点:AsyncTask 是一个轻量级的异步任务处理类,轻量级体现在,使用方便、代码简洁上,而且整个异步任务的过程可以通过cancel()进行控制。
缺点:不适用于处理长时间的异步任务,一般这个异步任务的过程最好控制在几秒以内,如果是长时间的异步任务就需要考虑多线程的控制问题;当处理多个异步任务时,UI更新变得困难。
二、总结
多线程部分一共四篇,这篇收了个尾,后面也会继续对Android其他知识点进行总结,Android进阶系列也会一直写下去,如果文中有错误的地方,欢迎大佬们批评指点。