AsyncTask学习

       记录下AsyncTask的学习心得:

      AsyncTask​类是用来处理异步请求的,当我们在UI线程(主线程)中执行耗时操作时(从网络获取数据,从数据库中获取数据,或者读取文件)会阻塞主线程的运行,导致界面不流畅,卡。这种情况下,我们需要将这些耗时的操作另起一个线程来执行,尽量在主线程中执行UI操作。

    ​  一般做法,我们用Thread+handler去处理耗时操作,和处理善后工作。AsyncTask对Thread和Handler进行了一定的封装,简化代码的编写,方便我们使用。

      AsyncTask的架构是创建一个Executor来执行ArrayDeque里的Runnable对象,这个Executor可以是自定义的,也可以是AsyncTask默认实现的SerialExecutor

   

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

  上面代码 SerialExecutor实现了Executor中的execute方法,execute中将一个Runnable对象(在本类中是一个FutureTask)加入队列中,然后判断当前是否有活动的Runnable,如果没有的话,就从Queue中take一个,然后将它设为当前活动Runnable, 并且执行这个Runnable。

      现在来看一下则个Runnable做了什么,首先他执行参数Runnable的run方法,finally去执行sheduleNext()函数,进入这个函数,他拉去队列的首元素出来当成当前active  Runnable,如果不为空,就执行他,这样Executor就顺序执行了Queue里的Runnable对象。当然还可以自定义自己的Executor,比如给队列赋予权重,权重大的runnable先得到执行

PriorityBlockingQueue<PriorityRunnable> mTasks=new PriorityBlockingQueue<PriorityRunnable>(); 
 private static class PriorityRunnable implements Comparable<PriorityRunnable>{
        	private Runnable mRunnable;
            private  Priority mPriority;
            private Integer mSequence; 
            private AtomicInteger mSequenceGenerator = new AtomicInteger();
			public  PriorityRunnable(final Runnable r){
            	mRunnable=new Runnable() {
	                public void run() {
                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            };
                this.setmSequence(getSequenceNumber());
               // this.setmPriority();
            }
			 public int getSequenceNumber() {
			        return mSequenceGenerator.incrementAndGet();
			 }

			public Runnable getRunnable(){
				return mRunnable;
			}
			public void setmRunnable(Runnable mRunnable) {
				this.mRunnable = mRunnable;
			}
			public Priority getmPriority() {
				return mPriority;
			}
			public void setmPriority(Priority mPriority) {
				this.mPriority = mPriority;
			}
			public Integer getmSequence() {
				return mSequence;
			}
			public void setmSequence(Integer mSequence) {
				this.mSequence = mSequence;
			}
			@Override
			public int compareTo(PriorityRunnable another) {
				// TODO Auto-generated method stub
				 Priority left = this.getmPriority();
			      Priority right = another.getmPriority();
			      return left == right ?
			                this.mSequence - another.mSequence :
			                right.ordinal() - left.ordinal();
			}
			
        }

  上面的Runnable是自定义的,实现了compareable接口,因为我们用PriorityBlockingQueue来容纳Runnable。

      上面说到execute参数里的Runnable是个FutureTask,FutrueTask是用来执行异步计算的,它可以包装一个Callable或者Runnable,并且获得Callable的计算结果和运行状态。在AsyncTask中,每一次耗时请求都new一个全新的FutureTask。FutureTask的初始化工作在 new AsyncTask()中。

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

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                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);
                }
            }
        };
    }

 在AsyncTask初始化中,首先先new 一个WorkRunnable,WorkRunnable实现了Callable<Result>的抽象类,在call方法体内,首先mTaskInvoked.set(true),这个mTaskInvoked是个AutomicBoolean类型的变量,用来追踪Task的状态,当当前Task被执行是,将状态置为true,用来判断Task是否被取消执行。设置完mTaskInvoked后,又为该Task设置了线程优先级为Process.THREAD_PRIORITY_BACKGROUND级别,最后返回postResult函数值,进入postResult():

    

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

  postResult函数参数为doInBackground()函数的执行结果,这里的doInBackground()函数为AsyncTask的抽象方法,我们需要在自定义的AsyncTask类中去实现这个函数,我们的异步请求的主要逻辑就在这里执行。doInBackground()函数的返回值类型是Result,Result是泛型类型,postResult()函数用来创建一个Message,然后将Result包装为AsyncTaskResult类型,然后由Message携带传给相应线程的Handler去处理。看一下AsyncTaskResult的数据结构:

 private static class AsyncTaskResult<Data> {
        final AsyncTask mTask;
        final Data[] mData;

        AsyncTaskResult(AsyncTask task, Data... data) {
            mTask = task;
            mData = data;
        }
    }

  它有两个字段,mTask和mDate, mTask用来保存自己的引用,mData用来保存mFuture.get()的值,这个类的用法下面会进一步分析。

new WorkRunnable后接着new FutureTask,并且mWorker作为参数传入FutureTask作为其获取数据的来源。FutureTask可以控制线程的执行和得到线程返回的数据,当FutureTask中的Callable执行完后,FutureTask的done函数得以触发。如果线程在执行前就被Cancle掉,那么直接执行FutureTask的done函数。FutureTask的状态是isDone,postResultIfNotInvoked()函数主要是用来处理task被cancel的情况,当Task被cancel时,WorkRunnable的call函数不会执行,此时mTaskInvoked的值为false,然后执行postResult函数,这样保证了Task不管是被执行还是被cancel都能得到处理。

  private void postResultIfNotInvoked(Result result) {
        final boolean wasTaskInvoked = mTaskInvoked.get();
        if (!wasTaskInvoked) {
            postResult(result);
        }
    }

 现在来看InternalHandler,这是个Handler的子类,他接受Message传来的数据,根据msg.what进行判断以进行不同的逻辑处理。在这里msg.what有两种类型,一种是MESSAGE_POST_RESULT,表示处理mWorkRunnable执行完毕发送的message,执行AsyncTask的finish函数。另一种是MESSAGE_POST_PROGRESS,它表示处理mWorkRunnable在更新时发送的message,执行AsyncTask的onProgressUpdate函数.

  finish函数:

private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }

  函数体先判断当前task是否被cancelled,如果isCancelled,执行onCacelled(result)函数。 

onCancelled函数:

 protected void onCancelled(Result result) {
        onCancelled();
    }
  protected void onCancelled() {
    }

  我们可以override这个函数,执行自己的操作。

      如果当前task没有被cancelled掉,执行onPostExecute(result)函数,同样我们可以在AsyncTask的子类中override这个函数,这个函数是在UI线程中执行的,可以对UI元素进行操作。

     在这里出现了一个变量mStatus,看它的定义:

   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,
    }

  它有三种状态,一种是Pendding,注释明明白白的说道,这是为了标识还未执行的tasks的状态,Running用来标识正在运行的task的状态,Finished用来标识已经执行完任务的task的状态。

     现在再看看到底AsyncTask是怎么执行的,我们平常用的时候是这样子的  new MyAsyncTask().executef();

     看看execute函数的庐山真面目吧:

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

  首先,函数体先判断当前task的状态,如果状态不是未运行,则抛出异常,如果执行execute()时,当前task处于Running状态,则抛出“cannot execute task:the task is already running"异常,

     至于为什么要这么设计,我的猜想是:

Future 仅在计算完成时才能检索结果;如果计算尚未完成,则阻塞 get 方法。一旦计算完成,就不能再重新开始或取消计算。

  只要是重新new AsyncTask(),就会获得一个全新的task。

      回到代码,如果当前task的状态是pending,那么更新它的状态为Running。然后执行onPreExecute(),函数,它和onPostExecute还有onBackground()函数一样,都可以在客户端override,定义自己的业务逻辑。执行完毕后,将从客户端传来的可变参数params赋值给mWorkRunnable的mParams成员变量,用以保存。最后开始调用线程池的execute()函数,之前说过execute()函数将当前task队列加入queue队列中,如果没有正在执行的task,则执行刚创建的task,如果有正在执行任务的task,则先缓存在队列中,等待他执行的时机。

     最后看一下线程池:

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

 private static final BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(10);

 public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

  ThreadFactory类是用来创建线程的,简单的重写newThread方法即可定义自己的ThreadFactory。

      ThreadPoolExecutor类有几个重要的参数,core_pool_size:池中所保存的线程数,包括空闲线程。,maximum_pool_size:池中允许的最大线程数,keep_alive:当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。unit - keepAliveTime 参数的时间单位。workQueue - 执行前用于保持任务的队列。此队列仅保持由 execute 方法提交的 Runnable 任务。threadFactory - 执行程序创建新线程时使用的工厂。

    对于AsycnTask的大体分析就到此为止了。谈一下自己对AsycnTask的几点感想:

      1.AsycnTask在SerialExecutor设计时用到了设计模式中的责任链模式。

      2.频繁的获取网络数据或者进行耗时操作时,new AsycnTask()可能造成大量的临时对象,影响性能,导致界面”打嗝“性卡顿。

      3.AsycnTask不适合上传大文件,一般上传大文件用socket上传,或者断点上传,利用RandomAceccsFile来实现。 

      4AsycnTask利用线程池来管理Thread,在性能上比每次单独创建一个Thread更省内存。

      以上有不对的地方,敬请堪正。

   关于ThreadPoolExecutor,点这http://blog.sina.com.cn/s/blog_70bcd7c10101a1w7.html

原文地址:https://www.cnblogs.com/mKaoree/p/4461837.html