zoukankan      html  css  js  c++  java
  • Android中AsyncTask分析--你所不注意的坑

    AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,提供接口反馈当前异步执行的程度(可以通过接口实现UI进度更新),最后反馈执行的结果给UI主线程.

    本文不分析AsyncTask的使用,它的使用教程网上一搜一大堆,本文主要分析它的内部逻辑和实现,它是怎么实现异步的,它是怎么处理多个任务的,是并发么??

    一、线程任务的调度

    在AsyncTask内部会创建一个类相关的线程池来管理要运行的任务,也就就是说当你调用了AsyncTask的execute()后,AsyncTask会把任务交给线程池,由线程池来管理创建Thread和运行Therad。

    在Android4.0版本中它内部是有两个线程池:SerialExecutor和ThreadPoolExecutor,SerialExecutor是串行的,ThreadPoolExecutor是并发的,而默认的就是SerialExecutor的,所以你一个程序中如果用了好几个AsyncTask你就得注意了:不要忘了换成并发的线程池执行。下面演示一下,穿行的调度

    1.一个简单的例子:可以看出他是一个个执行的
     
    代码如下:
    public class AsyncTaskDemoActivity extends Activity {  
        private static int ID = 0;  
        private static final int TASK_COUNT = 9;  
        private static ExecutorService SINGLE_TASK_EXECUTOR;  
        private static ExecutorService LIMITED_TASK_EXECUTOR;  
        private static ExecutorService FULL_TASK_EXECUTOR;  
          
        static {  
            SINGLE_TASK_EXECUTOR = (ExecutorService) Executors.newSingleThreadExecutor();  
            LIMITED_TASK_EXECUTOR = (ExecutorService) Executors.newFixedThreadPool(7);  
            FULL_TASK_EXECUTOR = (ExecutorService) Executors.newCachedThreadPool();  
        };  
          
        @Override  
        public void onCreate(Bundle icicle) {  
            super.onCreate(icicle);  
            setContentView(R.layout.asynctask_demo_activity);  
            String title = "AsyncTask of API " + VERSION.SDK_INT;  
            setTitle(title);  
            final ListView taskList = (ListView) findViewById(R.id.task_list);  
            taskList.setAdapter(new AsyncTaskAdapter(getApplication(), TASK_COUNT));  
        }  
          
        private class AsyncTaskAdapter extends BaseAdapter {  
            private Context mContext;  
            private LayoutInflater mFactory;  
            private int mTaskCount;  
            List<SimpleAsyncTask> mTaskList;  
              
            public AsyncTaskAdapter(Context context, int taskCount) {  
                mContext = context;  
                mFactory = LayoutInflater.from(mContext);  
                mTaskCount = taskCount;  
                mTaskList = new ArrayList<SimpleAsyncTask>(taskCount);  
            }  
              
            @Override  
            public int getCount() {  
                return mTaskCount;  
            }  
      
            @Override  
            public Object getItem(int position) {  
                return mTaskList.get(position);  
            }  
      
            @Override  
            public long getItemId(int position) {  
                return position;  
            }  
      
            @Override  
            public View getView(int position, View convertView, ViewGroup parent) {  
                if (convertView == null) {  
                    convertView = mFactory.inflate(R.layout.asynctask_demo_item, null);  
                    SimpleAsyncTask task = new SimpleAsyncTask((TaskItem) convertView);  
                    /* 
                     * It only supports five tasks at most. More tasks will be scheduled only after 
                     * first five finish. In all, the pool size of AsyncTask is 5, at any time it only 
                     * has 5 threads running. 
                     */  
                    task.execute();
                    // use AsyncTask#SERIAL_EXECUTOR is the same to #execute();  
    //                task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);  
                    // use AsyncTask#THREAD_POOL_EXECUTOR is the same to older version #execute() (less than API 11)  
                    // but different from newer version of #execute()  
    //                task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);  
                    // one by one, same to newer version of #execute()  
    //                task.executeOnExecutor(SINGLE_TASK_EXECUTOR);  
                    // execute tasks at some limit which can be customized  
    //                task.executeOnExecutor(LIMITED_TASK_EXECUTOR);  
                    // no limit to thread pool size, all tasks run simultaneously  
                    //task.executeOnExecutor(FULL_TASK_EXECUTOR);  
                      
                    mTaskList.add(task);  
                }  
                return convertView;  
            }  
        }  
          
        private class SimpleAsyncTask extends AsyncTask<Void, Integer, Void> {  
            private TaskItem mTaskItem;  
            private String mName;  
              
            public SimpleAsyncTask(TaskItem item) {  
                mTaskItem = item;  
                mName = "Task #" + String.valueOf(++ID);  
            }  
              
            @Override  
            protected Void doInBackground(Void... params) {  
                int prog = 1;  
                while (prog < 101) {  
                    SystemClock.sleep(100);  
                    publishProgress(prog);  
                    prog++;  
                }  
                return null;  
            }  
              
            @Override  
            protected void onPostExecute(Void result) {  
            }  
              
            @Override  
            protected void onPreExecute() {  
                mTaskItem.setTitle(mName);  
            }  
              
            @Override  
            protected void onProgressUpdate(Integer... values) {  
                mTaskItem.setProgress(values[0]);  
            }  
        }  
    }  
      
    class TaskItem extends LinearLayout {  
        private TextView mTitle;  
        private ProgressBar mProgress;  
          
        public TaskItem(Context context, AttributeSet attrs) {  
            super(context, attrs);  
        }  
      
        public TaskItem(Context context) {  
            super(context);  
        }  
          
        public void setTitle(String title) {  
            if (mTitle == null) {  
                mTitle = (TextView) findViewById(R.id.task_name);  
            }  
            mTitle.setText(title);  
        }  
          
        public void setProgress(int prog) {  
            if (mProgress == null) {  
                mProgress = (ProgressBar) findViewById(R.id.task_progress);  
            }  
            mProgress.setProgress(prog);  
        }  
    }  
    

      2.你想要的并发执行

    上面的情况肯定不是你想要的,你想要的应该是这种情况:
    只需要修改一行代码:
     task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);  
    

      当然也可以换成你自己的线程池。

    二、源码分析

      1.成员变量:

    定义了需要用到的成员,可以根据名字就能知道干什么的

    //生产线程的工厂
    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);
    
        /**
         * 可以平行的执行任务!就是并发的
         * 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);
    
        /**
         * 线性执行的执行器
         * An {@link Executor} that executes tasks one at a time in serial
         * order.  This serialization is global to a particular process.
         */
        public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
        //内部交互的handler
        private static final InternalHandler sHandler = new InternalHandler();
        //默认的Executor
        private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
    

    定义了需要用到的成员,可以根据名字就能知道干什么的,另外注意都是static修饰的:

      第二行的sThreadFactory是创建线程的;

      第十行的sPoolWorkQueue阻塞队列,存放任务的;

     第十七行是 THREAD_POOL_EXECUTOR是线程池,这个是并发执行的线程池;

     第26行是线性调度的线程池,SERIAL_EXECUTOR,执行完一个才会执行下一个;

     第28行是一个内部封装的Handler:InternalHandler

     第30行可以看出他默认的是线性调度的线程池, Executor sDefaultExecutor = SERIAL_EXECUTOR,看到这里你应该注意一个问题,如果程序里有好多个AsyncTask,它们就是线性调度的,这肯定可你预想的不一样,所以你别忘了换成并发的执行器。

    2.内部Handler的使用:

    2.1 自定义的InternalHandler(内部handler)

    private static class InternalHandler extends Handler {
            @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
            @Override
            //接受message的处理,可以看到根据状态选择是完成了,还是更新着
            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;
                }
            }
        }
    

      在上边handleMessage中,根据msg进行判断,是完成了还是在更新;

         任务完成调用finish方法,在其中执行你定义的onPostExecute方法,

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

      3.构造方法

    public AsyncTask() {
            mWorker = new WorkerRunnable<Params, Result>() {
                public Result call() throws Exception {
                    mTaskInvoked.set(true);
    
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    return postResult(doInBackground(mParams));
                }
            };
    
            mFuture = new FutureTask<Result>(mWorker) {
                @Override
                protected void done() {
                    try {
                        final Result result = get();
    
                        postResultIfNotInvoked(result);
                    } 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);
                    } catch (Throwable t) {
                        throw new RuntimeException("An error occured while executing "
                                + "doInBackground()", t);
                    }
                }
            };
        }
    

    构造方法中其实隐藏的信息很多,WorkerRunnable和FutureTask;

    其中WorkerRunnable继承了Callable接口,应该是用于在未来某个线程的回调接口,在其中执行了postResult(doInBackground(mParams));调用doInBackground,并用postResult方法把result发送到主线程。

    FutureTask你看类的介绍是说控制任务的,控制任务的开始、取消等等,在这不细究,跟本文关系不大,而且我也没看明白。

    第17行有一个方法:postResultIfNotInvoked(result);根据名字可以看出来是如果没有调用就把结果post出去,所以他应该是处理取消的任务的。

    看下postResult方法:代码很少也很简单,就是把msg发送给handler:

    //用shandler把设置message,并发送。
    private Result postResult(Result result) {
            Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
                    new AsyncTaskResult<Result>(this, result));
            message.sendToTarget();
            return result;
        }

    构造方法就分析到这,下一步就是execute():

    3.1 按照执行过程流程,实例化完,就可以调用execute():

    //Params... 就相当于一个数组,是传给doInBackground的参数
    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;
        } 
    

    代码逻辑很清晰,没有几行:

    20行:修改了状态;

    21行:准备工作;

    24行:设置参数;

    25行:线程池调用执行,注意参数是mFuture。 

    3.2 execute的执行逻辑

    就以它定义SerialExecutor为例:

    /*An {@link Executor} that executes tasks one at a time in serial
         * order.  This serialization is global to a particular process.*/
    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);
                }
            }
        }
    

     可以看到它的方法都是用synchronized关键字修饰的,且在他的介绍里也说了在同一时间只有一个任务在执行。

    •  在execute方法中把Future 的 run方法封装到Runnable里,放到Task队列中,如果mActive为空则调用scheduleNext方法执行任务;在这里还有一个细节就是执行完r.run(),还有个finally模块调用scheduleNext 方法,所以它才会一个接一个的执行任务。
    •  而在scheduleNext 中使用THREAD_POOL_EXECUTOR 执行任务。

    三、AsyncTask中异步的处理逻辑

    没有忘了前边构造方法中的postResult(doInBackground(mParams))和postResultIfNotInvoked(result);方法吧,如果忘了翻前边去看。这两个方法把执行成功的和失败的任务都包含了。

    所以我们可以设想一下它是怎么执行的:

    1.在executeOnExecutor方法中给变量赋值

    2.用执行器Executor另起线程执行任务

    3.在Executor中一些复杂的逻辑,用FutureTask进行判断任务是否被取消,如果没有就调用回调接口call()方法,

    4.在call()方法中调用了postResult(doInBackground(mParams));

    5.postResult发送给主线程的Handler进行后续处理。

    看的时候可以画下图,会很清晰,基本逻辑就这样,好AsyncTask就分析到这,欢迎纠错。。。

     转发请注明出处,原文地址:http://www.cnblogs.com/jycboy/p/asynctask_1.html

  • 相关阅读:
    Vue Cli 3.x项目如何部署到IIS子站点下
    IntelliJ IDEA 2018.3.2无法正常输入字符问题解决方案
    解决macOS git clone Azure DevOps提示身份认证失败问题
    JDBC driver for MySQL连接提示"The connection property 'zeroDateTimeBehavior' acceptable values are: 'CONVERT_TO_NULL', 'EXCEPTION' or 'ROUND'. The value 'convertToNull' is not acceptable."解决方案
    dbeaver导出MySQL的架构提示"IO Error: Utility 'mysqldump.exe' not found in client home 'MySQL Connector/Net"解决方案
    Git 常用命令和 Git Flow 梳理
    gitflow工作流程基本命令使用
    RocketMQ学习分享
    解决docker中使用nginx做负载均衡时并发过高时的一些问题
    Tomcat中session共享问题的简单解决办法
  • 原文地址:https://www.cnblogs.com/jycboy/p/asynctask_1.html
Copyright © 2011-2022 走看看