zoukankan      html  css  js  c++  java
  • 第十一章 Android的线程和线程池

    AsyncTask

    1. 三个参数(都可为Void):
      Params:参数
      Progress:执行进度
      Result:返回值
    2. 四个方法 :
      onPreExecute() 主线程执行,异步方法执行前调用。
      doInBackground(Params...params) 线程池中执行,用于执行异步任务;在方法内部用publishProgress 来更新任务进度。
      onProgressUpdate(Progress...value)主线程执行,后台任务进度状态改变时被调用。
      onPostExecute(Result result) 主线程执行,异步任务执行之后被调用
      执行顺序: onPreExecute->doInBackground->onPostExecute 如果取消了异步任务,会回调onCancelled(),onPostExecute则不会被调用

    Tips: AsyncTask的类必须在主线程加载,Android4.1及以上已经被系统自动完成了;AsyncTask对象必须在主线程创建;execute方法需要在UI线程调用;一个AsyncTask对象只能调用一次;Android1.6之前串行执行,Android1.6采用线程池并行处理任务,Android3.0开始,采用一个线程执行任务,但也可以通过executeOnExecutor方法来并行执行任务




    AsyncTask的工作原理

  • AsyncTask中有两个线程池(SerialExecutor和THREAD_POOL_EXECUTOR)和一个InternalHandler,其中线程池SerialExecutor用于任务排队,THREAD_POOL_EXECUTOR用于真正执行任务,InternalHandler用于将执行环境切换到主线程

  • AsyncTask的排队过程:系统首先会把AsyncTask的Params参数封装成FutureTask对象,它充当Runnable的作用,接下来这个FutureTask会交给SerialExecutor的execute方法处理,execute方法首先会把FutereTask对象插入到任务队列mTasks中去;如果没有正在活动的AsyncTask任务,就会执行下一个AsyncTask任务;同时当一个AsyncTask任务执行完成后,AsyncTask会继续执行其他任务直到所有任务都执行为止,可以看出默认情况,AsyncTask是串行执行的(Android3.0后)

先看一段示例:

    

  1. package com.example.zhy_asynctask_demo01;
  2. import android.app.Activity;
  3. import android.app.ProgressDialog;
  4. import android.os.AsyncTask;
  5. import android.os.Bundle;
  6. import android.util.Log;
  7. import android.widget.TextView;
  8. public class MainActivity extends Activity
  9. {
  10. private static final String TAG = "MainActivity";
  11. private ProgressDialog mDialog;
  12. private TextView mTextView;
  13. @Override
  14. protected void onCreate(Bundle savedInstanceState)
  15. {
  16. super.onCreate(savedInstanceState);
  17. setContentView(R.layout.activity_main);
  18. mTextView = (TextView) findViewById(R.id.id_tv);
  19. mDialog = new ProgressDialog(this);
  20. mDialog.setMax(100);
  21. mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
  22. mDialog.setCancelable(false);
  23. new MyAsyncTask().execute();
  24. }
  25. private class MyAsyncTask extends AsyncTask<Void, Integer, Void>
  26. {
  27. @Override
  28. protected void onPreExecute()
  29. {
  30. mDialog.show();
  31. Log.e(TAG, Thread.currentThread().getName() + " onPreExecute ");
  32. }
  33. @Override
  34. protected Void doInBackground(Void... params)
  35. {
  36. // 模拟数据的加载,耗时的任务
  37. for (int i = 0; i < 100; i++)
  38. {
  39. try
  40. {
  41. Thread.sleep(80);
  42. } catch (InterruptedException e)
  43. {
  44. e.printStackTrace();
  45. }
  46. publishProgress(i);
  47. }
  48. Log.e(TAG, Thread.currentThread().getName() + " doInBackground ");
  49. return null;
  50. }
  51. @Override
  52. protected void onProgressUpdate(Integer... values)
  53. {
  54. mDialog.setProgress(values[0]);
  55. Log.e(TAG, Thread.currentThread().getName() + " onProgressUpdate ");
  56. }
  57. @Override
  58. protected void onPostExecute(Void result)
  59. {
  60. // 进行数据加载完成后的UI操作
  61. mDialog.dismiss();
  62. mTextView.setText("LOAD DATA SUCCESS ");
  63. Log.e(TAG, Thread.currentThread().getName() + " onPostExecute ");
  64. }
  65. }
  66. }

进入某个Activity,Activity中需要的数据来自于网络或者其它耗时操作,可以在AsyncTask中onPreExecute完成一些准备操作,比如上例中显示进度对话框;然后在doInBackground完成耗时操作,在进行耗时操作时还能不时的通过publishProgress给onProgressUpdate中传递参数,然后在onProgressUpdate中可以进行UI操作,比如上例更新进度条的进度;当耗时任务执行完成后,最后在onPostExecute进行设置控件数据更新UI等操作,例如隐藏进度对话框。



源码解析

注:本篇源码分析基于Andorid-17,因为和3.0之前版本变动较大,有必要标出。

那么大家一定好奇,AsyncTask在Android中是如何实现的,下面进行源码分析:从我们的执行异步任务的起点开始,进入execute方法:

  1. public final AsyncTask<Params, Progress, Result> execute(Params... params) {
  2. return executeOnExecutor(sDefaultExecutor, params);
  3. }
  4. public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
  5. Params... params) {
  6. if (mStatus != Status.PENDING) {
  7. switch (mStatus) {
  8. case RUNNING:
  9. throw new IllegalStateException("Cannot execute task:"
  10. + " the task is already running.");
  11. case FINISHED:
  12. throw new IllegalStateException("Cannot execute task:"
  13. + " the task has already been executed "
  14. + "(a task can be executed only once)");
  15. }
  16. }
  17. mStatus = Status.RUNNING;
  18. onPreExecute();
  19. mWorker.mParams = params;
  20. exec.execute(mFuture);
  21. return this;
  22. }

18行:设置当前AsyncTask的状态为RUNNING,上面的switch也可以看出,每个异步任务在完成前只能执行一次。
20行:执行了onPreExecute(),当前依然在UI线程,所以我们可以在其中做一些准备工作。
22行:将我们传入的参数赋值给了mWorker.mParams
23行:exec.execute(mFuture)

相信大家对22行出现的mWorker,以及23行出现的mFuture都会有些困惑。
mWorker找到这个类:

  1. private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
  2. Params[] mParams;
  3. }
可以看到是Callable的子类,且包含一个mParams用于保存我们传入的参数,下面看初始化mWorker的代码:
  1. public AsyncTask() {
  2. mWorker = new WorkerRunnable<Params, Result>() {
  3. public Result call() throws Exception {
  4. mTaskInvoked.set(true);
  5. Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
  6. //noinspection unchecked
  7. return postResult(doInBackground(mParams));
  8. }
  9. };
  10. //….
  11. }
可以看到mWorker在构造方法中完成了初始化,并且因为是一个抽象类,在这里new了一个实现类,实现了call方法,call方法中设置mTaskInvoked=true,且最终调用doInBackground(mParams)方法,并返回Result值作为参数给postResult方法.可以看到我们的doInBackground出现了,下面继续看:
  1. private Result postResult(Result result) {
  2. @SuppressWarnings("unchecked")
  3. Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
  4. new AsyncTaskResult<Result>(this, result));
  5. message.sendToTarget();
  6. return result;
  7. }
可以看到postResult中出现了我们熟悉的异步消息机制,传递了一个消息message, message.what为MESSAGE_POST_RESULT;
message.object= new AsyncTaskResult(this,result);
  1. private static class AsyncTaskResult<Data> {
  2. final AsyncTask mTask;
  3. final Data[] mData;
  4. AsyncTaskResult(AsyncTask task, Data... data) {
  5. mTask = task;
  6. mData = data;
  7. }
  8. }
AsyncTaskResult就是一个简单的携带参数的对象。

看到这,我相信大家肯定会想到,在某处肯定存在一个sHandler,且复写了其handleMessage方法等待消息的传入,以及消息的处理。


  1. private static final InternalHandler sHandler = new InternalHandler();
  2. private static class InternalHandler extends Handler {
  3. @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
  4. @Override
  5. public void handleMessage(Message msg) {
  6. AsyncTaskResult result = (AsyncTaskResult) msg.obj;
  7. switch (msg.what) {
  8. case MESSAGE_POST_RESULT:
  9. // There is only one result
  10. result.mTask.finish(result.mData[0]);
  11. break;
  12. case MESSAGE_POST_PROGRESS:
  13. result.mTask.onProgressUpdate(result.mData);
  14. break;
  15. }
  16. }
  17. }
哈哈,出现了我们的handleMessage,可以看到,在接收到MESSAGE_POST_RESULT消息时,执行了result.mTask.finish(result.mData[0]);其实就是我们的AsyncTask.this.finish(result),于是看finish方法
  1. private void finish(Result result) {
  2. if (isCancelled()) {
  3. onCancelled(result);
  4. } else {
  5. onPostExecute(result);
  6. }
  7. mStatus = Status.FINISHED;
  8. }
可以看到,如果我们调用了cancel()则执行onCancelled回调;正常执行的情况下调用我们的onPostExecute(result);主要这里的调用是在handler的handleMessage中,所以是在UI线程中。

最后将状态置为FINISHED。

mWoker看完了,应该到我们的mFuture了,依然实在构造方法中完成mFuture的初始化,将mWorker作为参数,复写了其done方法。

  1. public AsyncTask() {
  2. ...
  3. mFuture = new FutureTask<Result>(mWorker) {
  4. @Override
  5. protected void done() {
  6. try {
  7. postResultIfNotInvoked(get());
  8. } catch (InterruptedException e) {
  9. android.util.Log.w(LOG_TAG, e);
  10. } catch (ExecutionException e) {
  11. throw new RuntimeException("An error occured while executing doInBackground()",
  12. e.getCause());
  13. } catch (CancellationException e) {
  14. postResultIfNotInvoked(null);
  15. }
  16. }
  17. };
  18. }
任务执行结束会调用:postResultIfNotInvoked(get());get()表示获取mWorker的call的返回值,即Result.然后看postResultIfNotInvoked方法

  1. private void postResultIfNotInvoked(Result result) {
  2. final boolean wasTaskInvoked = mTaskInvoked.get();
  3. if (!wasTaskInvoked) {
  4. postResult(result);
  5. }
  6. }
如果mTaskInvoked不为true,则执行postResult;但是在mWorker初始化时就已经将mTaskInvoked为true,所以一般这个postResult执行不到。
好了,到了这里,已经介绍完了execute方法中出现了mWorker和mFurture,不过这里一直是初始化这两个对象的代码,并没有真正的执行。下面我们看真正调用执行的地方。
execute方法中的:
还记得上面的execute中的23行:exec.execute(mFuture)

exec为executeOnExecutor(sDefaultExecutor, params)中的sDefaultExecutor

下面看这个sDefaultExecutor
  1. private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
  2. public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
  3. private static class SerialExecutor implements Executor {
  4. final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
  5. Runnable mActive;
  6. public synchronized void execute(final Runnable r) {
  7. mTasks.offer(new Runnable() {
  8. public void run() {
  9. try {
  10. r.run();
  11. } finally {
  12. scheduleNext();
  13. }
  14. }
  15. });
  16. if (mActive == null) {
  17. scheduleNext();
  18. }
  19. }
  20. protected synchronized void scheduleNext() {
  21. if ((mActive = mTasks.poll()) != null) {
  22. THREAD_POOL_EXECUTOR.execute(mActive);
  23. }
  24. }
  25. }

可以看到sDefaultExecutor其实为SerialExecutor的一个实例,其内部维持一个任务队列;直接看其execute(Runnable runnable)方法,将runnable放入mTasks队尾;
16-17行:判断当前mActive是否为空,为空则调用scheduleNext方法
20行:scheduleNext,则直接取出任务队列中的队首任务,如果不为null则传入THREAD_POOL_EXECUTOR进行执行。
下面看THREAD_POOL_EXECUTOR为何方神圣:
  1. public static final Executor THREAD_POOL_EXECUTOR
  2. =new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
  3. TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
可以看到就是一个自己设置参数的线程池,参数为:
  1. //现在已经根据CPU内核数目来设置线程池的大小了.

  1. private static final int CORE_POOL_SIZE = 5;
  2. private static final int MAXIMUM_POOL_SIZE = 128;
  3. private static final int KEEP_ALIVE = 1;
  4. private static final ThreadFactory sThreadFactory = new ThreadFactory() {
  5. private final AtomicInteger mCount = new AtomicInteger(1);
  6. public Thread newThread(Runnable r) {
  7. return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
  8. }
  9. };
  10. private static final BlockingQueue<Runnable> sPoolWorkQueue =
  11. new LinkedBlockingQueue<Runnable>(10);
可以看到,如果此时有10个任务同时调用execute(s synchronized)方法,第一个任务入队,然后在mActive = mTasks.poll()) != null被取出,并且赋值给mActivte,然后交给线程池去执行。然后第二个任务入队,但是此时mActive并不为null,并不会执行scheduleNext();所以如果第一个任务比较慢,10个任务都会进入队列等待;真正执行下一个任务的时机是,线程池执行完成第一个任务以后,调用Runnable中的finally代码块中的scheduleNext,所以虽然内部有一个线程池,其实调用的过程还是线性的。一个接着一个的执行,相当于单线程。



 HandlerThread

  • HandlerThread继承了Thread,是一种可以使用Handler的Thread;在run方法中通过looper.prepare()来开启消息循环,这样就可以在HandlerThread中创建Handler了;外界可以通过一个Handler的消息方式来通知HandlerThread来执行具体任务;确定不使用之后,可以通过quit或quitSafely方法来终止线程执行;具体使用场景是IntentService。

2.4 IntentService

  1. IntentSercie是一种特殊的Service,继承了Service并且是抽象类,任务执行完成后会自动停止,优先级远高于普通线程,适合执行一些高优先级的后台任务;
  2. IntentService封装了HandlerThread和Handler,onCreate方法自动创建一个HandlerThread,然后用它的Looper构造了一个Handler对象mServiceHandler,这样通过mServiceHandler发送的消息都会在HandlerThread执行;
    IntentServiced的onHandlerIntent方法是一个抽象方法,需要在子类实现,onHandlerIntent方法执行后,stopSelt(int startId)就会停止服务,如果存在多个后台任务,执行完最后一个stopSelf(int startId)才会停止服务















来自为知笔记(Wiz)


查看全文
  • 相关阅读:
    [BZOJ 1552] 排序机械臂
    [BZOJ 1124][POI 2008] 枪战 Maf
    [BZOJ 1647][USACO 2007 Open] Fliptile 翻格子游戏
    [BZOJ 1592] Making The Grade路面修整
    [BZOJ 3829][POI2014] FarmCraft
    [技术] 如何正确食用cnblogs的CSS定制
    [BZOJ 1458] 士兵占领
    今天写了一个Imageloader,,AndroidStudio报了Error:Execution failed for task ':app:mergeDebugResources'. > Error: Java.util.concurrent.ExecutionException: com.Android.ide.common.process.ProcessException: 这个错误
    Http响应码代表的含义
    获取WIFI列表,在旧手机上运行就没有问题,在新手机上就怎么也获取不到WIFI列表,长度一直为0,还不报异常,很疑惑。
  • 原文地址:https://www.cnblogs.com/You0/p/5984482.html
  • Copyright © 2011-2022 走看看