zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    理解maven命令package、install、deploy的联系与区别
    Mybatis中传参包There is no getter for property named 'XXX' in 'class java.lang.String'
    Spring boot 外部资源配置
    配置spring boot 内置tomcat的accessLog日志
    spring使用@Value标签读取.properties文件的中文乱码问题的解决
    使用Lombok简化你的代码
    统计mysql数据库中数据表/字段等数量的sql
    Kettle
    Python内置函数(39)——locals
    Python内置函数(38)——list
  • 原文地址:https://www.cnblogs.com/mKaoree/p/4461837.html
Copyright © 2011-2022 走看看