扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
这篇文章主要介绍了AsyncTask怎么用,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。
成都创新互联公司是专业的东台网站建设公司,东台接单;提供做网站、成都网站建设,网页设计,网站设计,建网站,PHP网站建设等专业做网站服务;采用PHP框架,可快速的进行东台网站开发网页制作和功能扩展;专业做搜索引擎喜爱的网站,专业的做网站团队,希望更多企业前来合作!
AsyncTask
,相信大家已经很熟悉了。它的内部封装了Thread
和Handler
,这让我们可以将一些耗时操作放到AsyncTask
,并且能将结果及时更新到UI上。AsyncTask
主要用于短时间耗时操作,长时间耗时操作不建议使用AsyncTask
。
private class DownloadFilesTask extends AsyncTask{ protected void onPreExecute() { showProgress(); } protected Long doInBackground(URL... urls) { int count = urls.length; long totalSize = 0; for (int i = 0; i < count; i++) { totalSize += Downloader.downloadFile(urls[i]); publishProgress((int) ((i / (float) count) * 100)); // Escape early if cancel() is called if (isCancelled()) break; } return totalSize; } protected void onProgressUpdate(Integer... progress) { setProgressPercent(progress[0]); } protected void onPostExecute(Long result) { showDialog("Downloaded " + result + " bytes"); } } 复制代码
AsyncTask
是一个抽象类,我们要使用时必须自定义一个类继承于它。AsyncTask
的原型为:
public abstract class AsyncTask{} 复制代码
它接收三个泛型参数,分别表示参数类型、进度类型、结果类型。
上述的例子中DownloadFilesTask
接收参数类型为URL
类型,使用Integer
类型表示任务进度,最终的任务结果是一个Long
类型。
注意:上面三个泛型类型不一定都得用一个明确的类型,对于没有使用的类型,可以使用
Void
类型代替。
继承AsyncTask
至少需要重写doInBackground
方法,同时AsyncTask
也提供了另外三个方法供我们重写,分别是onPreExecute
、onProgressUpdate
、onPostExecute
。
onPreExecute方法。在任务开始执行之前执行,它运行在UI线程中。通常我们可以在这里展示一个等待进度条。
doInBackground方法。贯穿整个耗时任务,它运行在子线程中。在这里执行耗时操作。
onProgressUpdate方法。贯穿整个耗时任务,在publishProgress
方法被调用后执行,它运行在UI线程中。通常用于展示整个任务的一个进度。
onProgressUpdate方法。在任务接收后调用,doInBackground
的返回结果会透传给onPostExecute
的参数值,它运行在主线程中。通常我们从这里获取任务执行完成后的结果数据。
AsyncTask
类必须在UI线程加载。(在4.1系统版本以上会自动完成)
AsyncTask
对象必须在UI线程创建,也就是说AsyncTask
的构造方法必须在UI线程中调用。(经过测试AsyncTask
对象可以在子线程创建,只要保证execute
方法在UI线程执行就OK的。但是没有人会这样做,因为多此一举!!!)
execute
方法必须在UI线程中调用。这样做是保证onPreExecute
方法运行在UI线程。
不要主动调用onPreExecute
、doInBackground
、onProgressUpdate
、onProgressUpdate
方法。
单线程下,AsyncTask对象的任务只能执行一次,否则会报运行时错误。
在AsyncTask
诞生之初,任务是在一个后台线程中顺序执行的。从Android 1.6开始,就变成了可以在后台线程中并行执行任务。然后,到了Android 3.0版本,又改成了单线程顺序执行,以此避免并发任务产生的错误行为。
为了验证上述结论,下面看一个Demo例子。
public class MainActivity extends Activity { public static final String TAG = "MyApplication"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new MyTask("task1").execute(); new MyTask("task2").execute(); new MyTask("task3").execute(); new MyTask("task4").execute(); new MyTask("task5").execute(); new MyTask("task6").execute(); } private class MyTask extends AsyncTask{ private String taskName; MyTask(String taskName) { this.taskName = taskName; } @Override protected Void doInBackground(Void... integers) { try { Thread.sleep(6000); } catch (InterruptedException e) { e.printStackTrace(); } return null; } @Override protected void onPostExecute(Void aVoid) { SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Log.e(TAG, taskName + " finish at: " + df.format(new Date())); } } } 复制代码
这个例子比较简单,就是在MainActivity
启动时,执行了六次MyTask
,并将任务执行后的时间节点打印出来。
image.png
手机的系统版本是Android 8.0,从上面的Log信息可以看出,AsyncTask
的确是串行执行的。由于现有测试机最低系统版本都是Android 4.1,已经很难找到Android 3.0以下的老古董机子了????,所以我们只能通过源码去验证Android 1.6到Android 3.0期间,AsyncTask
是否是并行执行的。
AsyncTask
是否串行或者并行执行,取决于它的execute
方法。
public final AsyncTaskexecute(Params... params) { ...省略 mWorker.mParams = params; sExecutor.execute(mFuture); return this; } 复制代码
而execute
方法中通过sExecutor
,实际为ThreadPoolExecutor
对象,它的初始化如下所示。
private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory); 复制代码
ThreadPoolExecutor
是一个多线程容器,其中可以创建多个线程来执行多个任务。由此验证了Android 1.6版本到Android 3.0版本直接,AsyncTask
执行任务的机制的确也现在的机制不一样,它可以让任务并行执行。
我们对比一下Android 8.0版本的execute
方法。
public final AsyncTaskexecute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); }public final AsyncTask executeOnExecutor(Executor exec, Params... params) { ...省略 mWorker.mParams = params; exec.execute(mFuture); return this; } 复制代码
execute
方法中调用了executeOnExecutor
方法,并将sDefaultExecutor
作为Executor
对象传递进去,sDefaultExecutor
的初始化如下所示。
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;public static final Executor SERIAL_EXECUTOR = new SerialExecutor();private static class SerialExecutor implements Executor { final ArrayDequemTasks = new ArrayDeque (); Runnable mActive; public synchronized void execute(final Runnable r) { mTasks.offer(new Runnable() { public void run() { try { r.run(); } finally { scheduleNext(); //任务执行完毕后继续执行scheduleNext方法 } } }); if (mActive == null) { //第一个任务会执行该方法 scheduleNext(); } } protected synchronized void scheduleNext() { if ((mActive = mTasks.poll()) != null) { //判断mTask队列中是否有下一个任务,有则取出来执行 THREAD_POOL_EXECUTOR.execute(mActive); } } } 复制代码
可以看到,在Android 8.0版本中,创建了一个ArrayDeque
队列,每次只从队列中获取一个任务执行,执行完毕后会继续判断队列中是否有任务,如果有则取出来执行,直到所有任务执行完毕为止。由此可见,Android 8.0版本执行任务是串行执行的。
如果我们想改变AsyncTask
这种默认行为呢,可以修改么?答案是肯定的。
我们可以直接调用AsyncTask
的executeOnExecutor
方法,并将一个Executor
对象传递过去,就能变成并行的执行方法了。
对于上面的例子,可以这样改动。
public class MainActivity extends Activity { public static final String TAG = "MyApplication"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new MyTask("task1").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); new MyTask("task2").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); new MyTask("task3").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); new MyTask("task4").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); new MyTask("task5").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); new MyTask("task6").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } private class MyTask extends AsyncTask{ private String taskName; MyTask(String taskName) { this.taskName = taskName; } @Override protected Void doInBackground(Void... integers) { try { Thread.sleep(6000); } catch (InterruptedException e) { e.printStackTrace(); } return null; } @Override protected void onPostExecute(Void aVoid) { SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Log.e(TAG, taskName + " finish at: " + df.format(new Date())); } } } 复制代码
执行后,打印出来的Log信息如下图所示。
image.png
注意:这里前五个Task是同时执行的,因为AsyncTask.THREAD_POOL_EXECUTOR创建了五个核心线程,第六个任务需要等待空闲线程才能继续执行。所以会出现第六个任务和前五个任务执行时间不一致的现象,特此说明。
感谢你能够认真阅读完这篇文章,希望小编分享的“AsyncTask怎么用”这篇文章对大家有帮助,同时也希望大家多多支持创新互联,关注创新互联行业资讯频道,更多相关知识等着你来学习!
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流