Android中的线程
Posted 川峰
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android中的线程相关的知识,希望对你有一定的参考价值。
本文主要是对android当中的线程相关的知识进行复习和总结。
文章目录
new Thread
缺乏统一管理,无限制创建,可能占用过多系统资源导致死机或oom,不推荐。
AsyncTask
场景:需要知晓任务执行的进度,多个任务串行执行
缺点:生命周期和宿主的生命周期不同步,有可能发生内存泄漏,默认情况所有任务串行执行
class MyAsyncTask extends AsyncTask<String, String, String> {
private static final String TAG = "MyAsyncTask";
@Override
protected String doInBackground(String... params) {
for (int i = 0; i < 100; i++) {
publishProgress(params[0] + " === "+ (i * 10));
}
return params[0];
}
@Override
protected void onPostExecute(String result) {
Log.e(TAG, "result: " + result);
}
@Override
protected void onProgressUpdate(String... values) {
Log.e(TAG, "onProgressUpdate: " + values[0]);
}
}
测试代码:
private void testAsyncTask() {
int count = 100;
for (int i = 0; i < count; i++) {
//串行
new MyAsyncTask().execute("MyAsyncTask[ " +i+ " ]");
//并行
new MyAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "MyAsyncTask[ " +i+ " ]");
final String name = "MyAsyncTask[ " +i+ " ]";
//串行
AsyncTask.execute(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
Log.e("MyAsyncTask", "onProgressUpdate: " + name + " === "+ (i * 10) );
}
}
});
//并行
AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 100; j++) {
Log.e("MyAsyncTask", "onProgressUpdate: " + name + " === "+ (j * 10));
}
}
});
}
}
AsyncTask源码解析:
AsyncTask的三个泛型参数:
public abstract class AsyncTask<Params, Progress, Result>
- Params: doInBackground方法的接受参数类型
- Progress:onProgressUpdate方法的接受参数类型
- Result:onPostExecute方法的接受参数类型,以及doInBackground方法的返回参数类型
AsyncTask的五个运行方法:
- onPreExecute(): 在主线程中执行,在异步任务执行之前被调用,一般用于一些准备工作
- doInBackground(Params… params) :在子线程运行,用来处理后台任务,
params
就是execute(Params… params)
方法传递的参数 - onProgressUpdate(Progress… values):在主线程运,在
doInBackground
中可以调用publishProgress
来更新进度 - onPostExecute(Result result):在主线程运行,后台任务处理完毕调用,并返回
doInBackground
的结果 - publishProgress(Progress… values): 在doInBackground中调用,来开启进度更新
注意:
- AsyncTask的实例必须在UI线程中创建
- execute必须在UI线程中调用
- 不能在doInBackGround中更改UI组件信息
- 一个AsyncTask的实例只能执行一次任务,执行第二次会抛出异常
AsyncTask构造函数:
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*
* @hide
*/
public AsyncTask(@Nullable Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return result;
}
};
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);
}
}
};
}
首先会尝试初始化一个主线程的mHandler
对象,使用 Looper.getMainLooper()
.然后创建了两个对象:WorkerRunnable
和 FutureTask
,分别赋值为mWorker
、mFuture
。这两个对象初始化都实现了两个回调方法,当用户执行了execute方法的时候在特定情况下会触发这两个对象的回调方法。
这里能够看到:doInBackground
是在WorkerRunnable
的call
方法中被回调执行的
WorkerRunnable:
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
它是一个实现了 Callable
接口的抽象类
Callable接口:
public interface Callable<V> {
V call() throws Exception;
}
与 Runnable
是一对兄弟,主要用在线程调用中,区别是 Runnable
的 run()
方法没有返回值,而 Callable
的 call()
方法有返回值。Callable
需要和ExcutorService
结合使用,其中ExecutorService
也是一个线程池对象继承自Executor
接口,ExecutorService
提供的方法:
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
- submit(Callable task),传递一个实现Callable接口的任务,并且返回封装了异步计算结果的Future。
- submit(Runnable task, T result),传递一个实现Runnable接口的任务,并且指定了在调用Future的get方法时返回的result对象。
- submit(Runnable task),传递一个实现Runnable接口的任务,并且返回封装了异步计算结果的Future。
FutureTask:
public class FutureTask<V> implements RunnableFuture<V> {
public FutureTask(Callable<V> callable) {
if (callable == null) throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
public boolean isCancelled() {
return state >= CANCELLED;
}
public boolean isDone() {
return state != NEW;
}
public void run() {
...
}
public boolean cancel(boolean mayInterruptIfRunning) {...}
protected void done() { }
public V get() {...}
public void set(V v) {...}
}
它一个实现 RunnableFuture
接口的类,构造函数可接受 Callable
参数类型,也就是 AsyncTask构造函数中的 WorkerRunnable
类型的 mWorker
对象,同时它具备run
方法以及cancel
、done
和状态判断等方法。
至此,我们能看到: WorkerRunnable
的 call()
方法是在 FutureTask
的 run()
方法中调用的:
RunnableFuture:
继承 Runnable
、Future
两个接口,run
方法是来自Runnable
接口
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
public interface Future<V> {
//取消任务
boolean cancel(boolean mayInterruptIfRunning);
//如果任务完成前被取消,则返回true。
boolean isCancelled();
//如果任务执行结束,无论是正常结束或是中途取消还是发生异常,都返回true。
boolean isDone();
//获取异步执行的结果,如果没有结果可用,此方法会阻塞直到异步计算完成。
V get() throws InterruptedException, ExecutionException;
// 获取异步执行结果,如果没有结果可用,此方法会阻塞,但是会有时间限制,
//如果阻塞时间超过设定的timeout时间,该方法将返回null。
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}
Future
接口能够中断执行中的任务, 判断任务是否完成,获取任务执行完成后的结果(get),说白了就是对具体的Runnable或者Callable对象任务执行的结果进行获取(get()),取消(cancel()),判断是否完成等操作。
因为 RunnableFuture
同时继承了 Runnable
、Future
两个接口,所以 FutureTask
既可以被当做Runnable
使用也可以被当做Future
来使用。而 FutureTask
是 RunnableFuture
的实现类,那么接下来关键就是FutureTask
的run
方法在何时被执行?
AsyncTask的execute()方法:
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();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
这里先调用了 onPreExecute()
方法,然后执行了exec.execute(mFuture)
,也就是为啥onPreExecute()
是在主线程执行的,而 FutureTask
对象是被放入线程池中执行。
并且这里判断状态不等于 PENDING
时,只要状态等于 RUNNING
或者 FINISHED
就会抛异常!这也是为啥 一个AsyncTask的实例只能执行一次,第二次就会报错的原因!
SerialExecutor:
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static class SerialExecutor implements Executor {
// 双端队列 既可以当成队列使用 也可以当成栈使用
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive; // 当前正在执行的那个 mFutureTask
public synchronized void execute(final Runnable r) {
// offer 向队尾插入元素,成功返回 true
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
//判断如果没有Runnable在执行,就调用scheduleNext
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
// poll 获取并删除队首元素,失败返回null
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive); // 由线程池真正的执行任务
}
}
}
sDefaultExecutor
是一个 SerialExecutor
类型的 全局静态的常量 对象 SERIAL_EXECUTOR
,SerialExecutor
是 Executor
的实现类,在 execute()
方法中调用了 Runnable
对象的 run
方法,因此我们的 mFuture
对象最终是在这里被调用的。(前面提过 FutureTask
既是Runnable
的实现也是Future
的实现)。
这里 SerialExecutor
实际维护了一个队列,每当 execute()
执行并传入一个 FutureTask
对象,这个对象就会被加入到队尾,然后判断当前如果没有正在执行的FutureTask
对象,就从队首获取一个FutureTask
放入线程池中执行。
而加入队列中的 FutureTask
对象也不是直接添加的,而是又包了一层 Runnable
对象,这个包一层又有什么用呢?看它这里使用了 try-finally
保证了scheduleNext()
方法必被调用,也就是说当前这个 FutureTask
的run
方法被执行完以后,会自动取下一个FutureTask
放入线程池中执行。
也就是说当我们new
一个AsyncTask
任务开始execute
时, 并不一定立马执行,要看它里面的任务队列情况,如果它是第一个就会立马执行,如果它前面还有,那么就会排队等待。也就是一个串行的任务队列。
至此,我们能得到一个大体的执行流程:
new AsyncTask()
--> new FutureTask()
--> AsyncTask#execute()
—> SerialExecutor#execute()
--> mTasks.offer()
--> mTasks.poll()
--> scheduleNext()
--> THREAD_POOL_EXECUTOR.execute()
--> FutureTask#run()
-->WorkerRunable#call()
–>doInBackground()
postResult方法:
再回来看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;
}
....
private Handler getHandler() {
return mHandler;
}
....
private static Handler getMainHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler(Looper.getMainLooper());
}
return sHandler;
}
}
这里没什么神秘的了,就是通过Handler
发消息,而这里的Handler
对象就是构造函数第一行代码初始化的运行在主线程的Handler
对象(使用了 Looper.getMainLooper()
)。
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:
// mTask就是当前的AsyncTask对象
result.mTask.以上是关于Android中的线程的主要内容,如果未能解决你的问题,请参考以下文章