美文网首页
4.AsyncTask使用,原理

4.AsyncTask使用,原理

作者: Varmin | 来源:发表于2017-06-03 22:32 被阅读0次

资料

概述:

Android中,实现异步有两种方式,Handler和AsyncTask。
使用Handler+Thread时,每次执行耗时操作都需要创建新的线程进行处理,代码比较臃肿,性能开销也会较大。
使用AsyncTask,内部是封装好的Handler+Thread,并且使用了线程池,有效的优化了性能。

"AsyncTask并不适合进行特别耗时的后台任务,对特别耗时的任务,建议使用线程池。--开发艺术探索上所写,但是还并未理解"
可能是因为:多个任务时特别耗时的任务会影响后面的任务迟迟不能执行。用线程池并发的话,就没这样的问题了。

原理:

轻量级的异步任务类,在线程池中串行执行任务,通过Handler把进度和执行结果发送到主线程,更新UI。

简单使用:

 MyAsyncTask mAsyncTask = new MyAsyncTask();
mAsyncTask.execute();//下面后台任务参数为Void类型,所以不填参数。
 
/**
*AsyncTask<Void, Integer, Boolean>:泛型,可以为任意类型。
*分别为:后台任务执行的参数,更新进度的参数,任务执行完成的结果
*/
public class MyAsyncTask extends AsyncTask<String, Integer, Boolean>{
        private ProgressDialog mProgress;
        private int currentProgress;
        //任务开始前的准备操作,可以做些UI的准备或初始化。
        //多个任务时,无论是否等待,execute()的时候就在主线程调用
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            mProgress.setMax(100);
            mProgress.setMessage("下载进度");
            mProgress.show();
        }

        //线程池中执行任务
        @Override
        protected Boolean doInBackground(Void... params) {
            while (currentProgress <= 100) {
                //不能跟新UI
                //mProgress.setProgress(currentProgress);
                //想更新进度的话,主动调用该方法,会通过handler给onProgressUpdate方法。
                publishProgress(currentProgress);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                   return false;
                }
                currentProgress++;
            }
            return true;
        }

        //publishProgress通过handler调用,主线程
        @Override
        protected void onProgressUpdate(Integer... values) {
            mProgress.setMessage("下载进度:"+values[0]);
        }

        //执行完成后的结果。
        @Override
        protected void onPostExecute(Boolean aBoolean) {
            super.onPostExecute(aBoolean);
            if (aBoolean) {
                mProgress.setMessage("下载完成");
            }else {
                mProgress.setMessage("下载出错");
            }

                mProgress.cancel();
        }
        
        //中断任务时,调用该方法。但并不一定会马上中断。
        @Override
        protected void onCancelled() {
            super.onCancelled();
        }
    }

原理,源码分析:

按照调用方法,一步一步的分析。

  • 构造函数入手:
public AsyncTask() {
        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
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    postResult(result);
                }
                return result;
            }
        };

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

好像并不能看出来什么,就两个实例对象初始化。这里记得就行,从内部逻辑上调用的方法名来看,一个mWorker跟后台任务有关;一个mFuture跟结果通知有关。这里看不懂正常,也不要每步都要求详细清楚,往下看。

  • 调用mAsyncTask.execute()方法:
方法1:    
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }

方法2:    
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        //如果状态为正在运行或者已经结束,报错。说明,只能调用一次mAsyncTask.execute()方法。
        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;//改变状态

        //看好了哈,在此处调用。此时还是在主线程中哦,所以可以操作UI。
        onPreExecute();

        //构造方法中的一个实例,把执行后台任务的参数给了它。
        mWorker.mParams = params;
        //该exec是方法1中的sDefaultExecutor,看下面代码.
        exec.execute(mFuture);

        return this;
    }
    

1.只能执行一次mAsyncTask.execute()方法。
2.主线程调用onPreExecute()
3.更改状态,赋值。
4.调用线程池有关方法执行。sDefaultExecutor.execute(mFuture)
继续往下看,调用的方法:

    private static volatile Executor sDefaultExecutor = new SerialExecutor();
    //Executor?线程池类?
    private static class SerialExecutor implements Executor {
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;
        //mFuture参数,也即是构造方法中的实例
        public synchronized void execute(final Runnable r) {
            //封装成一个Runnable,放到队列的最后。
语句1:        mTasks.offer(new Runnable() {
                public void run() {
                    try {
                    //mFuture的run方法,只是调用方法,run并没有开新线程
语句5:              r.run();
                    } finally {
语句6:              scheduleNext();
                    }
                }
            });
            //第一次执行时
            if (mActive == null) {
语句2:             scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
            //取出队列中第一个封装好的Runnable
语句3:     if ((mActive = mTasks.poll()) != null) {
                //THREAD_POOL_EXECUTOR是线程池
语句4:        THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

执行步骤,按语句顺序:将mFuture的run方法封装成Runnable对象放入队列最后--->第一次执行时,取出Runnable并到线程池中执行--->执行完以后,再取,再去执行。以此类推...
需要注意:封装好的Runnable在线程池中会开新的线程。mFuture的run只是一个方法,也就是说此时run中的代码已经在子线程了。
上面整个过程都是在放入/取出Runnable,最主要的是执行的mFuture的run方法。

//FutureTask类,mFuture的run方法
public void run() {
        if (state != NEW || !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
            return;
        try {
语句1:    Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
语句2:             result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
语句3:             set(result);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

语句1中的callable是构造函数中初始化mFuture的用到的第一个实例mWorker啊。
语句2执行了mWorker的call方法。
语句3最后执行了mFuture的done方法。
语句2,3执行的正好是构造时两个实例复写的方法:先执行call,再执行done。
再回过头来看AsyncTask的构造方法:

    public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
语句0           mTaskInvoked.set(true);
                Result result = null;
                try {
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //noinspection unchecked
语句1:             result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
语句2:            postResult(result);
                }
                return result;
            }
        };

        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                try {
语句3:           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);
                }
            }
        };
    }

语句1就是生成AsyncTask实例时复写的后台执行任务的方法:doInBackground() 在线程池中执行的。

    @WorkerThread
    protected abstract Result doInBackground(Params... params);

语句2拿到后台任务执行后的result,通过mainHandler调用onPostExecute(result);

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


private static class InternalHandler extends Handler {
        public InternalHandler() {
            super(Looper.getMainLooper());
        }

        @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;
    }
    
    protected void onPostExecute(Result result) {
    }

另外,语句3的 postResultIfNotInvoked(get()),如果满足条件,也会调用postResult通过handler....
但是,正是因为语句0的设置,使其并不满足条件,所以不执行。(不太清楚wasTaskInvoked的作用)

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

至此,关于调用mAsyncTask.execute()方法,上面的源码分析完了,先把任务放入队列最后,然后取出队列第一个,在线程池中调用了doInBackground,后通过主线程的handler调用onPostExecute(Result result);

  • publishProgress:该方法可以再doInbackGroud中给onProgressUpdate更新进度。上面分析的过程中,程序并不会自动调用该方法,需要用到的时候,我们自己调用就行了。
    protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {
            getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
        }
    }
    
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;
            }
        }
        
protected void onProgressUpdate(Progress... values) {
    }

至此,整个AsyncTask执行过程就分析完了。跨度太大,总结一下:
1.onPreExecute()在mAsyncTask.execute(...params)执行的时候,马上执行,主线程。
2.doInBackgroun在线程池中调用。子线程。
3.得到结果后,通过主线程的handler发送消息,调用onPostExecute。主线程。
4.手动调用publishProgress会通过主线程的handler调用onProgressUpdate。 主线程。

相关文章

网友评论

      本文标题:4.AsyncTask使用,原理

      本文链接:https://www.haomeiwen.com/subject/fawmfxtx.html