Android开发实用技巧mqttAndroid知识

架构之异步任务

2016-09-09  本文已影响208人  shone

AsyncTask是啥?
AsyncTask是一个围绕Handler和Thread而设计的辅助类,封装了在工作线程中与UI交互的细节,只需要对应重写几个回调方法即可,并使得代码更加简洁,优雅。但要注意的是AsyncTask并不构成一个通用的线程框架 ,这在Android官方介绍中有提到:

AsyncTask is designed to be a helper class around {@link Thread} and 
{@link Handler}* and does not constitute a generic threading framework.

AsyncTask的适用于短耗时操作,如果是长时间多线程执行,推荐使用java.util.concurrent包下的API,如Executor,ThreadPoolExecutor,FutureTask...

特性:

1.任务运行在后台线程,结果返回到UI线程
2.三个通用参数:Params,Progress,Result对应参数,进度和结果集
3.操作仅4步曲

onPreExecute
doInBackground
onProgressUpdate
onPostExecute

官方示例

  private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
      protected Long doInBackground(URL... urls) {
          int count = urls.length;
          long totalSize = 0;
          for (int i = 0; i < count; i++) {
              totalSize += Downloader.downloadFile(urls[i]);
              publishProgress((int) ((i / (float) count) * 100));
              // Escape early if cancel() is called
              if (isCancelled()) break;
          }
          return totalSize;
      }
 
      protected void onProgressUpdate(Integer... progress) {
          setProgressPercent(progress[0]);
      }
 
      protected void onPostExecute(Long result) {
          showDialog("Downloaded " + result + " bytes");
      }
  }

new DownloadFilesTask().execute(url1, url2, url3);

历史

September 2009: Android 1.6.之前,AsyncTask被执行在单线程上!
Android 1.6.以后:AsyncTask改变成,设计为线程池方式,允许多任务并发执行!
February 2011: Android 3.0.以后,AsyncTask的多任务只在单线程上被执行,为了避免不同版本因为并发执行带来的异常问题,但是仍然可以配置成多线程执行,只需调用executeOnExecutor(java.util.concurrent.Executor,Object[])方法,传入THREAD_POOL_EXECUTOR或者自定义Executor!

分析

1.最重要的2个成员

private final WorkerRunnable<Params, Result> mWorker;//核心工作人员
private final FutureTask<Result> mFuture;//对mWorker的一层包装,可以统计工作完成情况

2.构造方法,实例化工作人员

public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                Result result = doInBackground(mParams);//后台任务开始执行
                Binder.flushPendingCommands();
                return postResult(result);//把结果交给Handler发出去,UI更新
            }
        };

        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);
                }
            }
        };
    }

3.单线程模型(默认)

private static class SerialExecutor implements Executor {
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        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);
            }
        }
    }

默认的指挥官是SERIAL_EXECUTOR,字面意思是顺序执行器,内部维护着一个双端队列ArrayDeque:

ArrayDeque缺点是线程非安全,优点是效率高,用作堆栈时快于Stack,用作队列时快于LinkedList。

每接收一个任务,都会先把任务扔到队列里,然后检测当前是否有正在被执行的任务,没有的话,就从队列里取出head,交给另一个并发执行器去执行,如果当前有任务正在执行的话,就不做调度,默默的等任务执行完,如果执行完了,再调度下一个将要被执行的任务对象!

3.多线程模型(可选)

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;
private static final BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(128);

private static final ThreadFactory sThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);

        public Thread newThread(Runnable r) {
            return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
        }
};

/**
  * An {@link Executor} that can be used to execute tasks in parallel.
 */
public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

唯一的线程池指挥官ThreadPoolExecutor,权利大大的,拥有一个128容量的阻塞任务队列,集团化作战随时待命,空闲线程1s的存活时间,机动性能不是盖的:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory)

必杀技:

1.当线程池小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程。
2.当线程池达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行
3.当workQueue已满,且maximumPoolSize>corePoolSize时,新提交任务会创建新线程执行任务
4.当提交任务数超过maximumPoolSize时,新提交任务由RejectedExecutionHandler处理
5.当线程池中超过corePoolSize线程,空闲时间达到keepAliveTime时,关闭空闲线程
6.当设置allowCoreThreadTimeOut(true)时,线程池中corePoolSize线程空闲时间达到keepAliveTime也将关闭

4.最平凡的工作人员:Callable
平常用的较多的创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口。

这2种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果。
如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦。
"造物主"为了解决这个问题,就提供了Callable和Future这对搭档,通过它们可以在任务执行完毕之后得到任务执行结果。

private static abstract class WorkerRunnable<Params, Result> 
                       implements Callable<Result> {    
         Params[] mParams;
}

在本异步任务中当然不能缺席Callable,这里是用内部类去实现了,并且自带加特技Params,主要是方便把参数传递给doInBackground(mParams)~

4.最可敬的工作人员:FutureTask
FutureTask是RunnableFuture接口的实现类,RunnableFuture既包含Runnable特性,又兼具Future特点
上面说了Callable要配合Future使用,达到效果,而线程池执行者需要执行Runnable对象,所以用FutureTask最好不过了!

Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。

在异步任务构造器里分别完成了WorkerRunnable和FutureTask的实例化!

5.开始作战
第1步:下达命令

@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    return executeOnExecutor(sDefaultExecutor, params);
}

MainThread注解表示该方法只能在主线程调用~
第2步:装填弹药

@MainThread
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();//UI前戏,预处理

        mWorker.mParams = params;//绑定参数,传参
        exec.execute(mFuture);//执行器执行任务

        return this;
}

第3步:开火

public Result call() throws Exception {
      mTaskInvoked.set(true);
      Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);//线程优先级,后台线程
       //noinspection unchecked
       Result result = doInBackground(mParams);//回调到doInBackground,后台执行
       Binder.flushPendingCommands();
       return postResult(result);
}

第4步:打扫战场

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);
     }
}

第5步:向总部报告作战成果

private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();//发消息
        return result;
}
public void handleMessage(Message msg) {
       AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
       switch (msg.what) {
            case MESSAGE_POST_RESULT://报告结果-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);//回调UI,任务取消了 
    } else {
        onPostExecute(result);//回调UI,任务执行完毕,结果是result
    }
    mStatus = Status.FINISHED;//更新状态
}

到此,异步作战就算完毕了~
此次作战学到了:
1.单兵作战神器SerialExecutor
2.群战(并发)神器,ThreadPoolExecutor
3.Callable与Future协同作战,威力大
4.群战(并发)法宝AtomicBoolean,原子操作
5.离不开的通信使者Handler
6.ArrayDeque双端队列,高效管理任务

上一篇下一篇

猜你喜欢

热点阅读