Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android AsyncTask #10

Open
xxm-sz opened this issue Jun 28, 2019 · 0 comments
Open

Android AsyncTask #10

xxm-sz opened this issue Jun 28, 2019 · 0 comments

Comments

@xxm-sz
Copy link
Owner

xxm-sz commented Jun 28, 2019

前言

AsyncTaskHandlerThradIntentService都是对Android消息机制的封装和应用,解决在子线程耗时任务,主线程更新UI的问题。

AsyncTask的使用

AsyncTask是一个抽象类,通过子类继承重写doInBackground,该方法在子线程运行。

public class DownloadTask extends AsyncTask<String,Integer,String> {

    @Override
    protected void onPostExecute(String s) {
        super.onPostExecute(s);
    }

    @Override
    protected String doInBackground(String... strings) {
        publishProgress(1);
        return "AsyncTask";
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
    }
}

AsyncTask<Params, Progress, Result> 的泛型参数,可以根据业务需求,传不同的类型。第一个参数Params是指传到doInBackground的参数类型,例如瞎子啊进度的URL。第二个参数Progress是指在执行doInBackground方法时,通过publishProgress更新进度状态的类型,例如下载的进度。第三个参数ResultdoInBackground方法执行结束后传到onPostExecute的参数类型,例如下载结果。

相关方法

  • onPreExecute()

运行在主线程。可以开始任务之前,对UI或者数据进行初步准备。非必需方法。

  • doInBackground

在子线程(线程池)运行。一般进行耗时操作,例如下载。为AsyncTask的抽象方法,必须实现。

  • onProgressUpdate

运行在主线程,在doInBackground方法中调用publishProgress方法会回调该方法,显示当前任务状态。常用来更新下载进度。非必需方法。

  • onPostExecute

在主线程运行。在doInBackground方法中return值之后,将回调该方法。非必需方法。

  • onCancelled

在主线程运行,任务完成时回调该方法,表示任务结束。

开始任务

task的execute只能调用一次,不然报错。

    DownloadTask task=new DownloadTask();
    task.execute("Git");

源码分析

AsyncTask的构造函数

Android中代码注释是必须在UI线程中调用,因为UI线程默认拥有Looper,以及AsyncTask需要更新UI。如果不需要更新UI,在有Looper的子线程都可以创建。

    //构造方法1
    public AsyncTask() {
        this((Looper) null);
    }
    //构造方法2
    public AsyncTask(@Nullable Handler handler) {
        this(handler != null ? handler.getLooper() : null);
    }
    //构造方法3
    public AsyncTask(@Nullable Looper callbackLooper) {
        分析一:
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);
        分析二:
        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);
                }
            }
        };
    }

构造方法1、2,最终调用构造方法3。看看构造方法3做了什么?

分析一

给mHanlder赋值初始化。先判断是否传入Looper对象,如果没有传入,或者传入Looper对象并且和UI线程的Looper相同,则通过调用getMainHandler方法调用UI线程的Handler对象(这里可以简单理解,AsyncTask在UI线程创建的)。如果不相等,new一个 Handler对象。

    //AsyncTask
    private static Handler getMainHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler(Looper.getMainLooper());
            }
            return sHandler;
        }
    }
    //Looper
    public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }

AsyncTask的getMainHandler方法,通过调用Looper的getMainLooper方法来获得Looper对象(UI线程的Looper对象)并创建InternalHandler。这里可以看到,在没有传入Looper的情况,且不在UI线程创建AsyncTask,会获取不到Looper对象。sHandler变量是InternalHandler类型,继承Handler。

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

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

在方法处理中,可以看到是对MESSAGE_POST_RESULT消息和MESSAGE_POST_PROGRESS消息的处理,用来在任务结束和进度更新时,切换到主线程,回调相关方法。

分析二

静态抽象类WorkerRunnable继承Callable,只多添加了数组mParams,用于保存参数。在WorkerRunnable的call方法中,主要调用我们重写的doInBackground。最终对调用postResult方法。

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

在分析一中,最后看到InternalHandler对象对MESSAGE_POST_RESULT消息的处理,调用AsyncTask对象的finish方法。

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

根据当前AsyncTask的状态调用onCancelled或者onPostExecute方法。

在AsyncTask的构造方法中。通过构造Handler对象,和构造WorkRunnable对象,并将WorkRunnable对象用于创建FutureTask对象。FutureTask实现了RunnableFuture接口,而RunnableFuture接口继承了Runnable接口和Future接口。所以FutureTask既能做一个Runnable对象直接被Thread执行,也能作为Future对象用来得到Callable的计算结果。

AsyncTask的执行


        DownloadTask task=new DownloadTask();

        task.execute("Git");

我们在UI线程创建DownloadTask对象,并将调用DownloadTask对象的execute方法,Git方法是我们要传到doInBackground方法的值;

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

调用AsyncTask的execute方法会调用executeOnExecutor方法。

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

如果当前AsyncTask对象正在运行或者结束,会抛出异常,一个task只能运行一次。在这里调用了onPreExecute方法,并参数赋值给了前面讲到的WorkerRunable对象的omParams变量。通过线程池对象exex执行在构造方法创建的FutureTask对象,最终对调用WorkerRunable对象的call方法,从而执行我们重写的doInBackground方法。

这里我们看看线程池对象sDefaultExecutor。

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

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

通过ArrayDequ保存任务,并以加锁机制同步execute方法,串行执行任务。

THREAD_POOL_EXECUTO是什么东东?

  public static final Executor THREAD_POOL_EXECUTOR;

    static {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                sPoolWorkQueue, sThreadFactory);
        threadPoolExecutor.allowCoreThreadTimeOut(true);
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
    }
    
    private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    private static final int KEEP_ALIVE_SECONDS = 30;

毫无意外是一个线程池。

总结

通过AsyncTask的构造器的跟踪,我们了解AsyncTask的实现的整体流程。通过Looper对象创建Handler对象,用于在线程池中发送消息,切换到UI线程,进行相关操作。并创建WorkRunnable对象调用后台任务(我们重写的doInBackground方法),将WorkRunnable对象传给FutureTask对象,这样在线程池中执行时,对结果和过程可控。

在AsyncTask的execute方法追踪,我们知道后台任务采用双向队列保存,线程池通过锁同步串行运行。

另外需要注意的是,AsyncTask的生命周期和Activity的生命周期并不一致,若AsyncTask持有Activity,容易造成内存泄漏。类似下载耗时操作建议采用IntentServcie

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant