主要有哪些类型:
(1)Thread。
(2)AsyncTask。(线程池,Handler)(更新UI)
(3)IntentService。(Thread线程,HandlerThread)(后台服务,不容易被杀死)
(4)HandlerThread。(Thread线程,Handler)(消息循环)
注意点:
(1)线程开太多的时候,系统会通过时间片轮转的方式调度每个线程。
(2)频繁创建和销毁线程是要消耗很大的资源的,正确的做法是采用线程池。
(3)用Executor来派生特定类型的线程池。
(4)主线程就是UI线程,只有一个。不能执行耗时(网络请求,I/O操作)的任务。
从Android3.0开始系统要求网络访问必须在子线程中进行,否则网络访将会失败并抛出NetworkOnMainThreadException这个异常。
一、AsyncTask
1、封装了Thread 和 Handler:
它是轻量级的异步任务类,它可以在线程池中执行后台任务,然后把执行的最终结果传递给主线程并在主线程中更新UI。
2、并不适合进行特别耗时的后台任务:
对于特别耗时的后台任务来说,建议使用线程池。
3、注意点介绍:
(1)源码位置:
sources\android\os\AsyncTask.java
(2)AsyncTask是一个抽象的泛型类,提供了 Params(参数的类型)、Progress(后台任务执行速度的类型)、Result(后台任务返回结果的类型)这三个泛型参数。不需要的时候可以用Void来代替。
public abstract class AsyncTask<Params, Progress, Result>
(3)AsyncTask包括四个核心方法:
onPreExecute():主线程,异步任务执行之前调用,做准备工作。
doInBackground(Params...params):线程池,用于执行异步任务,参数params表示异步任务的输入参数,需要返回计算结果给onPostExecute方法,doInBackground方法中可以通过调用publishProgress方法来更新任务的进度,而publishProgress中又会调用onProgressUpdate方法。
onProgressUpdate(Progress...values):主线程,当后台任务执行进度发生变化时在publishProgress方法中调用。
onPostExecute(Result result):主线程,当异步任务执行后被调用,其中result参数是doInBackground方法的返回值。
onCncelled():主线程,当异步任务被取消时调用,这个时候onPostExecute方法就不会被调用啦。
这里一直提各种主线程,线程池什么的,意思是这样的:
只有doInBackground方法是在线程池中进行的,意味着在其他的方法中,都是可以直接进行UI操作的!!!!!
下面是几个AsyncTask使用的注意点:
(4)AsyncTask的类必须在主线程中加载,这就意味着第一次访问AsyncTask必须发生在主线程,
当然这个过程在Android4.1及以上版本中已经被系统自动完成,
在Android5.0的源码中,可以查看ActivityThread的main方法,它会调用AsyncTask的init方法,这就满足了AsyncTask的类必须在主线程中加载的条件。
(5)AsyncTask的对象必须在主线程中创建。
(6)execute方法必须在UI线程(主线程)调用。
(7)不要在程序中直接调用 onPreExecute()、onPostExecute()、doInBackground和onProgressUpdate方法。
(8)一个AsyncTask对象只能执行一次,即只能调用execute方法一次,否则运行时会出现异常。
(9)在Android1.6以前,AsyncTask是串行执行任务的,Android1.6的时候AsyncTask开始采用线程池(Executor)处理并行任务,但从Android3.0开始,为了避免AsyncTask所带来的并发错误,AsyncTask又采用一个线程串行执行任务。
尽管如此,在Android3.0以及后续版本中,我们仍然可以通过AsyncTask的executeOnExecutor方法来并行的执行任务。
举个例子吧:模拟后台下载的小例子。
(在Java中 ... 表示参数的数量不定,它是一种数组型参数)
package com.ryg.my;
import java.net.URL;
import android.os.AsyncTask;
public class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
/*
* 在线程池中执行。
* */
@Override
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
// 这里Downloader是假的
totalSize += Downloader.downloadFile(urls[i]);
// 更新进度条:它里面会调用onProgressUpdate方法。
publishProgress((int) ((i / (float)count) * 100));
// 是否取消后台异步操作?
// 这个isCancelled是AsyncTask类中的一个方法。
if(isCancelled()){
break;
}
}
return totalSize;
}
/*
* 在doInBackground方法中的publishProgress方法中调用:
* */
@Override
protected void onProgressUpdate(Integer... progress) {
// 这个方法是假的:
setProgressPercent(progress[0]);
}
/*
* 异步操作doInBackground后调用:
* */
@Override
protected void onPostExecute(Long result) {
// 这也是个假的:
showDialog("Download " + result + " bytes.");
}
}
那具体的调用呢这样写:
new DownloadFilesTase().execute(url1, url2, url3);
或者可以这样写:
DownloadFilesTase task = new DownloadFilesTase();
task.execute(url1, url2, url3);
注意啦注意啦:传入的参数是传递给execute的,不是给DownloadFiles的!!!
4、源码分析
(1)源码位置:
sources\android\os\AsyncTask.java
(2)AsyncTask执行时要调用它的 execute方法:
/**
* Executes the task with the specified parameters. The task returns
* itself (this) so that the caller can keep a reference to it.
*
* <p>Note: this function schedules the task on a queue for a single background
* thread or pool of threads depending on the platform version. When first
* introduced, AsyncTasks were executed serially on a single background thread.
* Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed
* to a pool of threads allowing multiple tasks to operate in parallel. Starting
* {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are back to being
* executed on a single thread to avoid common application errors caused
* by parallel execution. If you truly want parallel execution, you can use
* the {@link #executeOnExecutor} version of this method
* with {@link #THREAD_POOL_EXECUTOR}; however, see commentary there for warnings
* on its use.
*
* <p>This method must be invoked on the UI thread.
*
* @param params The parameters of the task.
*
* @return This instance of AsyncTask.
*
* @throws IllegalStateException If {@link #getStatus()} returns either
* {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
*
* @see #executeOnExecutor(java.util.concurrent.Executor, Object[])
* @see #execute(Runnable)
*/
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
注意它的参数params是指输入参数,在实际调用的时候,可以传入多个Params类型的参数。
注意它的返回值是AsyncTask<Params, Progress, Result>,神奇。
可以看到啦,它里面调用的是 executeOnExecutor方法,就是我们刚刚提到的它具体是用串行还是并行的那个方法,
在executeOnExecutor 方法中,第一个参数
sDefaultExecutor是一个线程池Executor对象,这里的sDefaultExecutor代表的是一个串行的线程池,
一个进程中所有的AsyncTask全部在这个串行的线程池中排队执行,这个排队执行的过程后面再进行分析。
那我们去看看executeOnExecutor这个方法吧:
(3)executeOnExecutor(Executor exec, Params... params)方法:
AsyncTask执行状态的声明:初始化mStatus为PENDING未执行状态。
private volatile Status mStatus = Status.PENDING;
/**
* Indicates the current status of the task. Each status will be set only once
* during the lifetime of a task.
*/
public enum Status {
/**
* Indicates that the task has not been executed yet.
*/
PENDING,
/**
* Indicates that the task is running.
*/
RUNNING,
/**
* Indicates that {@link AsyncTask#onPostExecute} has finished.
*/
FINISHED,
}
/**
* Executes the task with the specified parameters. The task returns
* itself (this) so that the caller can keep a reference to it.
*
* <p>This method is typically used with {@link #THREAD_POOL_EXECUTOR} to
* allow multiple tasks to run in parallel on a pool of threads managed by
* AsyncTask, however you can also use your own {@link Executor} for custom
* behavior.
*
* <p><em>Warning:</em> Allowing multiple tasks to run in parallel from
* a thread pool is generally <em>not</em> what one wants, because the order
* of their operation is not defined. For example, if these tasks are used
* to modify any state in common (such as writing a file due to a button click),
* there are no guarantees on the order of the modifications.
* Without careful work it is possible in rare cases for the newer version
* of the data to be over-written by an older one, leading to obscure data
* loss and stability issues. Such changes are best
* executed in serial; to guarantee such work is serialized regardless of
* platform version you can use this function with {@link #SERIAL_EXECUTOR}.
*
* <p>This method must be invoked on the UI thread.
*
* @param exec The executor to use. {@link #THREAD_POOL_EXECUTOR} is available as a
* convenient process-wide thread pool for tasks that are loosely coupled.
* @param params The parameters of the task.
*
* @return This instance of AsyncTask.
*
* @throws IllegalStateException If {@link #getStatus()} returns either
* {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
*
* @see #execute(Object[])
*/
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
/*
* 一个异步任务AsyncTask只能执行一次,
* 所以启动一个异步任务时,
* 如果它正在执行,抛出异常
* 如果它已经执行完毕,抛出异常
* */
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();
/*
* 然后线程池exec开始执行:
* */
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
(4)下面我们看看线程池的执行过程:
这里所说的线程池就是刚刚在executeOnExecutor 方法中传递的第一个参数 sDefaultExecutor ,它是一个串行的线程池。
先去看看sDefaultExecutor 是怎么来的:
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
private static class SerialExecutor implements Executor {
/*
* 首先系统会把AsyncTask的Params参数也就是输入参数封装为 FutureTask 对象,
* 这个FutureTask 是一个并发类,在这里它充当了Runnable 的作用。
* */
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
/*
* 接着这个FutureTask会交给 SerialExecutor 的execute方法去处理,
* */
public synchronized void execute(final Runnable r) {
/*
* SerialExecutor的execute方法首先会把FutureTask对象插入到任务队了mTasks中,
* */
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
/*
* 同时当一个AsyncTask任务执行完以后,
* AsyncTask会继续执行其他任务直到所有的任务都被执行为止。
* */
scheduleNext();
}
}
});
/*
* 如果这个时候没有正在活动的AsyncTask任务,
* 那么就会调用SerialExecutor的scheduleNext方法来执行下一个AsyncTask任务。
* */
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);//这个在下面介绍
}
}
}
从SerialExecutor的实现我们来分析一下AsyncTask的排队执行过程。
首先系统会把AsyncTask的Params参数也就是输入参数封装为 FutureTask 对象,这个FutureTask 是一个并发类,在这里它充当了Runnable 的作用。
接着这个FutureTask会交给SerialExecutor 的execute方法去处理,
SerialExecutor的execute方法首先会把FutureTask对象插入到任务队了mTasks中,
如果这个时候没有正在活动的AsyncTask任务,那么就会调用SerialExecutor的scheduleNext方法来执行下一个AsyncTask任务。
同时当一个AsyncTask任务执行完以后,AsyncTask会继续执行其他任务直到所有的任务都被执行为止。
所以从上面的分析我们可以看出,AsyncTask是串行执行的。
(5)这时候我们就会发现在SerialExecutor线程池中,真正的异步执行过程是在scheduleNext方法中进行的,
这里面有一个THREAD_POOL_EXECUTOR 这样一个线程池。
其实,在AsyncTask中是有两个线程池的(分别是THREAD_POOL_EXECUTOR 和SerialExecutor)和一个Handler(InternalHandler)的。
其中,
SerialExecutor 线程池用于任务的排队,
THREAD_POOL_EXECUTOR 线程池用于真正地执行任务,
InternalHandler 用于将执行环境从线程池切换到主线程,其本质仍然是线程的调用过程。
我们来看看AsyncTask的构造方法吧:在这里就可以看到刚刚说的FutureTask了。
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*/
public AsyncTask() {
/*
* 刚刚讲了在SerialExecutor中的scheduleNext中执行真正的异步操作,
* 而在SerialExecutor中有这两条语句:
* final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
* Runnable mActive;
* 而我又在上面讲过系统会把Params参数封装成FutureTask对象来充当Runnable的作用,
* 反正说了这么多我就是想表达,Params和FutureTask和mTasks和mActive和mWorker直接是有联系的
*/
/*
* 由于FutureTask的run方法会调用mWorker的call方法,
* 因此mWorker的call方法最终会在线程池中执行。
* */
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
// 在mWorker的call方法中,首先将mTaskInvoked设为true,
// 表示当前任务已经被调用过了,
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
// 然后执行AsyncTask的doInBackground方法,
// 接着将其返回值传递给postResult
return postResult(doInBackground(mParams));
}
};
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 occured while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
(6)随后我们再去看看postResult方法:
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
这个方法中会通过 sHandler发送一个MESSAGE_POST_RESULT的消息。
其中sHandler的定义如下所示:
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) {
/*
* sHandler在接收到MESSAGE_POST_RESULT后会调用
* AsyncTask的finish方法:
* */
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;
}
}
}
我们可以看到sHandler是一个static的静态Handler对象,为了能够将执行环境切换到主线程,这就要求sHandler这个对象必须在主线程中创建,
由于静态成员会在加载类的时候进行初始化,因此这就变相要求AsyncTask的类必须在主线程中加载(这样会捎带把它内部的静态成员也初始化了),
否则同一个进程中的AsyncTask都将无法正常工作。
到此为止,AsyncTask的整个工作过程就分析完毕了!!!
sHandler在接收到MESSAGE_POST_RESULT后会调用 AsyncTask的finish方法:
private void finish(Result result) {
/*
* 如果AsyncTask被取消执行了,
* 那就取消执行。
* */
if (isCancelled()) {
onCancelled(result);
} else {
// 否则调用这里喽:
onPostExecute(result);
}
// 状态也跟着变喽:
mStatus = Status.FINISHED;
}