64

Android中的AsyncTask的工作原理

 5 years ago
source link: https://www.jakeprim.cn/2019/02/17/asyncTask/?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Android中的线程机制是非常重要的,在很多情况下为了使APP不卡顿,我们需要将很多事情放到子线程中执行,使主线程尽量没有耗时操作,否则会导致ANR.Android中的线程几乎完全采用了Java中的线程机制,那么创建、销毁、调度线程对线程的了解就很重要了.

Runnable/Callable

如何创建一个线程我相信只要做过Java的都会创建.这里就简单介绍一下.

new Thread(){
            @Override
            public void run() {
                super.run();
            }
        };
        
        new Thread(new Runnable() {
            @Override
            public void run() {
                
            }
        });

需要注意的是

Runnable 和 Callable,它们两个都是线程中执行的任务,主要区别是Callable可以在任务完成时能够返回一个值,Callable 可以返回装载有计算结果的 Future 对象.它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过 Future 对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果;Runnable是执行工作的独立任务,它不返回任何值.

Thread类只支持Runnable接口,只有线程池支持Callable

下面我们通过一个例子,看一下Callable

private static void callable() {
        ExecutorService executorService = Executors.newCachedThreadPool();
        List<Future<String>> futureList = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            //submit 会产生Future对象 它用Callable返回的结果的特定类型进行了参数化
            //可以用future.isDone来查询future是否已经完成 直接调用future.get可能会阻塞 知道结果准备就绪
            futureList.add(executorService.submit(new TaskWithResult(i)));
        }
        for (Future<String> future : futureList) {
            try {
                if (future.isDone()) {
                    System.out.println(future.get());
                }
            } catch (InterruptedException | ExecutionException e) {
                System.out.println(e);
            } finally {
                executorService.shutdown();
            }
        }
    }
    
public class TaskWithResult implements Callable<String> {

    private int id;

    public TaskWithResult(int id) {
        this.id = id;
    }

    @Override
    public String call() throws Exception {
        return "TaskWithResult is id:" + id;
    }
}

Thread 也可以实现这种方式,FutureTask

关于FutureTask

FutureTask 实现了 Runnable, Future<V> ,它既可以被Thread使用,也可以被Executor使用.

Future 弥补了线程设计的不足,他可以让你知道线程是否执行完毕,并且执行完毕后返回的结果.可以取消的异步的计算任务,get获取结果会阻塞等待直到结果准备就绪

public interface Future<V> {
    boolean cancel(boolean mayInterruptIfRunning);

    boolean isCancelled();

    boolean isDone();

    V get() throws InterruptedException, ExecutionException;

    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

我们继续通过一个小例子看一下FutureTask如何工作的.

FutureTask<String> futureTask = new FutureTask<String>(new Task()) {
           @Override
           protected void done() {
               try {
                   String s = get();
                   System.out.print("result:" + s);
               } catch (ExecutionException e) {
                   e.printStackTrace();
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
       };
       ExecutorService executorService = Executors.newCachedThreadPool();
       executorService.execute(futureTask);
       try {
           //阻塞 直到获得运行结果
           futureTask.get();
       } catch (Exception e) {
           e.printStackTrace();
       }
       
       static class Task implements Callable<String> {

       @Override
       public String call() throws Exception {
           //耗时操作
           Thread.sleep(3000);
           return "ok";
       }
   }

FutureTask 在Android中充当线程的角色还有 AsyncTaskHandlerThreadIntentService 等,下面通过AsyncTask来看看它是如何使用线程的

AsyncTask使用方法

什么是AsyncTask?这里我就不细说了,只要做过Android开发的都不陌生,AsyncTask它是一个轻量级的异步任务,内部封装了Thread和Handler,可以让我们在后台进行计算并且把计算的结果及时更新到UI上

AsyncTask使用的例子

private class Task extends AsyncTask<String, Integer, String> {

        @Override
        protected String doInBackground(String... strings) {
            return null;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
        }

        @Override
        protected void onPostExecute(String s) {
            super.onPostExecute(s);
        }

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }
    }
    
    new Task().execute("123");

使用AsyncTask需要注意的地方:

  • AsyncTask的类必须在UI线程加载(从4.1开始系统会帮我们自动完成)
  • AsyncTask对象必须在UI线程创建
  • execute方法必须在UI线程调用
  • 不要在你的程序中去直接调用onPreExecute(), onPostExecute, doInBackground, onProgressUpdate方法
  • 一个AsyncTask对象只能执行一次,即只能调用一次execute方法,否则会报运行时异常
  • AsyncTask不是被设计为处理耗时操作的,耗时上限为几秒钟,如果要做长耗时操作,强烈建议你使用Executor,ThreadPoolExecutor以及FutureTask
  • 在1.6之前,AsyncTask是串行执行任务的,1.6的时候AsyncTask开始采用线程池里处理并行任务,但是从3.0开始,为了避免AsyncTask所带来的并发错误,AsyncTask又采用一个线程来串行执行任务

AsyncTask源码解析(API 28)

分析AsyncTask工作原理,我们首先看AsyncTask的构造方法

public AsyncTask(@Nullable Looper callbackLooper) {
       //得到一个handler 这也是为什么AsyncTask 必须在主线程中调用,因为子线程中默认没有Looper无法创建
       //Handler
       mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
           ? getMainHandler()
           : new Handler(callbackLooper);
       //WorkerRunnable 实现了Callable 可以将任务的结果返回
       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
                   //调用doInBackground 进行耗时操作比如网络请求 如果需要进度则需要在doInBackground方法中
                   //计算进度 然后传递给
                   result = doInBackground(mParams);
                   Binder.flushPendingCommands();
               } catch (Throwable tr) {
                   //标记任务被取消
                   mCancelled.set(true);
                   throw tr;
               } finally {
                   //得到结果 返回给 onPostExecute
                   postResult(result);
               }
               //将得到的结果返回
               return result;
           }
       };
       //FutureTask 进一步判断,如果任务没有被标记,那么将得到结果返回给 onPostExecute
       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);
               }
           }
       };
   }

上述代码,在创建AsyncTask的时候,首先创建一个Handler,通过Handler将子线程切换到主线程中运行,这也就是为什么说创建AsyncTask必须在主线冲中创建.

WorkerRunnable 实现了Callable,同时我们还看到了 FutureTask ,我们着重看一下WorkerRunnable做了些什么?内部调用了 doInBackground(mParams); 执行任务,并将任务的结果返回 postResult .

postResult()方法如果将结果返回的?

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

在上面的代码中,给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;
            }
        }
    }
    
      private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }

在上面的代码中,调用了 onPostExecute(result) 方法,将结果返回,交给开发人员处理.通过上述代码,我们知道了AsyncTask是如何执行Runnable并且将结果返回的.

下面分析线程池是如何执行Runnable的,先从 execute 方法看起,如下所示:

@MainThread
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }
    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;
    }

在上述的代码中,可以看出 sDefaultExecutor 是一个线程池,并通过sDefaultExecutor执行mFuture.

sDefaultExecutor是如何实现的,如下所示

//串行任务执行器,其作用就是将多个任务一个一个的执行(execute())
   public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
     //默认的任务执行器,默认为串行任务执行
   private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
   
   //串行执行器的实现,AsyncTask.execute(param...) 方法最终是走到这里
   //其实现就是将任务加入到队列中 一个个排队 去实现任务
   private static class SerialExecutor implements Executor {
       //线性双向队列 用来存储所有的AsyncTask任务
       final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
       //正在执行的任务
       Runnable mActive;

       public synchronized void execute(final Runnable r) {
           //串行执行多个任务核心代码
           //将新的AsyncTask任务加入到双向队列中
           mTasks.offer(new Runnable() {
               public void run() {
                   try {
                       //执行任务
                       r.run();
                   } finally {
                       //当AsyncTask任务执行完毕后,执行下一个任务
                       scheduleNext();
                   }
               }
           });
           //如果没有正在执行的任务 从任务队列中直接获取任务 执行
           if (mActive == null) {
               scheduleNext();
           }
       }

       protected synchronized void scheduleNext() {
           //取出队列头部任务 交给线程池执行
           if ((mActive = mTasks.poll()) != null) {
               //线程池执行该任务
               THREAD_POOL_EXECUTOR.execute(mActive);
           }
       }
   }

从上述代码中,分析AsyncTask执行的任务是串行执行的过程,首先AsyncTask将FutureTask交给 SerialExecutor 的execute方法去处理, SerialExecutor 的execute方法会将FutureTask插入到mTasks(双向队列)中,如果没有正在执行的任务 mActive==null 从任务队列中直接获取任务 执行 scheduleNext 知道所有的任务执行完为止.

如何实现并行任务呢?

通过 executeOnExecutor 可自定义AsyncTask的执行方式,串行or并行,execute是串行执行,或者自定义线程池 asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, Params... params); 可实现并行执行任务

关于任务进度

//需要在doInBackground 方法中获取实际的进度 然后调用publishProgress
    @WorkerThread
    protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {
            getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
        }
    }

生产/消费模式

定义生产者

public class Producer extends Thread {

    private final Object mutex;

    public Producer(Object mutex) {
        this.mutex = mutex;
    }

    @Override
    public void run() {
        //生产产品
        while (true) {
            synchronized (mutex) {
                //如果生产的产品没有被消费掉 等待消费者消费
                if (null != Product.value) {
                    try {
                        mutex.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //使用
                if (null == Product.value) {
                    Product.value = "NO." + System.currentTimeMillis();
                    System.out.println("生产产品:" + Product.value);
                }
                //通知消费者可以消费了
                mutex.notify();
            }
        }
    }
}

定义消费者

public class Consumer extends Thread {
    private final Object mutex;

    public Consumer(Object mutex) {
        this.mutex = mutex;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (mutex){
                //如果没有产品生产就等待
                if (null == Product.value){
                    try {
                        mutex.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //有产品生产了
                if (null != Product.value) {
                    System.out.println("消费产品:" + Product.value);
                    Product.value = null;
                }
                //通知生产者产品已经消费
                mutex.notify();
            }

        }
    }
}

定义一个产品

public class Product {
    //线程机制为了提高运行效率,当一个线程不断当访问 一个变量
    //线程会使用一个私有空间 存储这个变量
    //volatile 易变变量 专门修饰被不同线程访问和修改的变量
    //让线程访问这个变量
    public volatile static String value;
}

实现方法

//        Object mutex = new Object();
//        new Producer(mutex).start();
//        new Consumer(mutex).start();

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK