Android AsyncTask使用以及源码解析
综述
在android中,我们需要进行一些耗时的操作,会将这个操作放在子线程中进行。在子线程操作完成以后我们可以通过handler进行发送消息,通知ui进行一些更新操作(具体使用及其原理可以查看android的消息机制——handler的工作过程这篇文章)。当然为了简化我们的操作,在android1.5以后为我们提供了asynctask类,它能够将子线程处理完成后的结果返回到ui线程中,之后我们便可以根据这些结果进行一列的ui操作了。
asynctask的使用方法
实际上asynctask内部也就是对handler和线程池进行了一次封装。它是一个轻量级的异步任务类,它的后台任务在线程池中进行。之后我们可以将任务执行的结果传递给主线程,这时候我们就可以在主线程中操作ui了。
asynctask他是一个抽象的泛型类,所以我们创建一个子类,来实现asynctask中的抽象方法。asynctask中提供了三个泛型参数,下面我们就来看一下这三个泛型参数.
1. params:在执行asynctask时所传递的参数,该参数在后台线程中使用。
2. progress:后台任务执行进度的类型
3. result:后台任务执行完成后返回的结果类型。
对于以上三个泛型参数我们不需要使用的时候,可以使用void来代替。与activity生命周期类似,在asynctask中也为我们提供了一些方法,我们通过重写这几个方法来完成整个异步任务。我们主要使用的方法有一下四个:
1. onpreexecute():该方法在异步任务工作之前执行,主要用于一些参数或者ui的初始化操作。
2. doinbackground(params… params):该方法在线程池中执行,params参数表示异步任务时输入的参数。在这个方法中我们通过publishprogress来通知任务进度。
3. onprogressupdate(progress… values):当后台任务的进度发生改变的时候会调用该方法,我们可以再这个方法中进行ui的进度展示。values参数表示任务进度。
4. postresult(result result):在异步任务完成之后执行,result参数为异步任务执行完以后所返回的结果。
在上面四个方法中只有doinbackground在子线程中运行,其余都三个方法都是在主线程中运行的。其中的…表示参数的数量不定,是一种数组类型的参数。
下面我们就来写一个例子来看一下asynctask的用法,在这里我们就一个下载的功能,从网络上下载两个文件。我们先来看一下效果演示。
效果演示
代码分析
由于我们做的下载任务,首先我们就得添加访问网络权限以及一些sd卡相关的权限。
<!-- 在sd卡中创建与删除文件权限 --> <uses-permission android:name="android.permission.mount_unmount_filesystems"/> <!-- 向sd卡写入数据权限 --> <uses-permission android:name="android.permission.write_external_storage"/> <!-- 授权访问网络 --> <uses-permission android:name="android.permission.internet"/>
下面我们来看一下activity代码。
package com.example.ljd.asynctask; import android.app.progressdialog; import android.os.asynctask; import android.os.environment; import android.support.v7.app.appcompatactivity; import android.os.bundle; import android.view.view; import android.widget.button; import android.widget.toast; import java.io.bufferedinputstream; import java.io.file; import java.io.fileoutputstream; import java.io.ioexception; import java.io.inputstream; import java.net.httpurlconnection; import java.net.url; public class mainactivity extends appcompatactivity implements view.onclicklistener{ private downloadasynctask mdownloadasynctask; private button mbutton; private string[] path = { "http://msoftdl.360.cn/mobilesafe/shouji360/360safesis/360mobilesafe_6.2.3.1060.apk", "http://dlsw.baidu.com/sw-search-sp/soft/7b/33461/freeime.1406862029.exe", }; @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); mbutton = (button)findviewbyid(r.id.button); mbutton.setonclicklistener(this); } @override protected void ondestroy() { if (mdownloadasynctask != null){ mdownloadasynctask.cancel(true); } super.ondestroy(); } @override public void onclick(view v) { mdownloadasynctask = new downloadasynctask(); mdownloadasynctask.execute(path); } class downloadasynctask extends asynctask<string,integer,boolean>{ private progressdialog mpbar; private int filesize; //下载的文件大小 @override protected void onpreexecute() { super.onpreexecute(); mpbar = new progressdialog(mainactivity.this); mpbar.setprogressnumberformat("%1d kb/%2d kb"); mpbar.settitle("下载"); mpbar.setmessage("正在下载,请稍后..."); mpbar.setprogressstyle(progressdialog.style_horizontal); mpbar.setcancelable(false); mpbar.show(); } @override protected boolean doinbackground(string... params) { //下载图片 for (int i=0;i<params.length;i++){ try{ if(environment.getexternalstoragestate().equals(environment.media_mounted)){ url url = new url(params[i]); httpurlconnection conn = (httpurlconnection) url.openconnection(); //设置超时时间 conn.setconnecttimeout(5000); //获取下载文件的大小 filesize = conn.getcontentlength(); inputstream is = conn.getinputstream(); //获取文件名称 string filename = path[i].substring(path[i].lastindexof("/") + 1); file file = new file(environment.getexternalstoragedirectory(), filename); fileoutputstream fos = new fileoutputstream(file); bufferedinputstream bis = new bufferedinputstream(is); byte[] buffer = new byte[1024]; int len ; int total = 0; while((len =bis.read(buffer))!=-1){ fos.write(buffer, 0, len); total += len; publishprogress(total); fos.flush(); } fos.close(); bis.close(); is.close(); } else{ return false; } }catch (ioexception e){ e.printstacktrace(); return false; } } return true; } @override protected void onpostexecute(boolean aboolean) { super.onpostexecute(aboolean); mpbar.dismiss(); if (aboolean){ toast.maketext(mainactivity.this,"下载完成",toast.length_short).show(); } else { toast.maketext(mainactivity.this,"下载失败",toast.length_short).show(); } } @override protected void onprogressupdate(integer... values) { super.onprogressupdate(values); mpbar.setmax(filesize / 1024); mpbar.setprogress(values[0]/1024); } } }
在以上代码中有几点我们需要注意一下。
1. asynctask中的execute方法必须在主线程中执行。
2. 每个asynctask对象只能执行一次execute方法。
3. 当我们的activity销毁的时候需要进行取消操作,其中boolean型的参数mayinterruptifrunning表示是否中断后台任务。
最后的布局代码就很简单了,只有一个button而已。
<?xml version="1.0" encoding="utf-8"?> <linearlayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="@dimen/activity_vertical_margin" android:orientation="vertical" tools:context="com.example.ljd.asynctask.mainactivity"> <button android:id="@+id/button" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="download"/> </linearlayout>
这里还有一点需要说明一下,由于在不同的android版本中对asynctask进行了多次修改,所以当我们通过多个asynctask对象执行多次execute方法的时候,它们执行顺序是串行还是并行根据系统不同的版本而出现差异,这里就不再具体分析。
asynctask源码分析
在这里我们采用android6.0中的asynctask源码进行分析,对于不同系统的asynctask代码会有一定的差异。当创建一个asynctask对象以后我们便可以通过execute方法来执行整个任务。那就在这里首先看一下execute方法中的代码。
@mainthread public final asynctask<params, progress, result> execute(params... params) { return executeonexecutor(sdefaultexecutor, params); }
可以看到这个execute方法中的代码是如此的简单,只是执行了executeonexecutor方法并返回asynctask对象。下面我们就来看一下executeonexecutor这个方法。
@mainthread 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所执行的状态进行判断。如果asynctask正在执行任务或者任务已经知己完成就会给我们抛出异常,也就解释上面所说的每个asynctask对象只能执行一次execute方法。紧接着将当前任务状态修改为正在运行以后便开始执行onpreexecute方法。这也说明了在上面我们重写的四个方法中onpreexecute方法最先指向的。下面我们在看一下mworker和mfuture这两个全局变量。
mfuture是futuretask对象,而mworker是workerrunnable对象,workerrunnable是asynctask中的一个实现callable接口的抽象内部类,在workerrunnable中只定义了一个params[]。
private final workerrunnable<params, result> mworker; private final futuretask<result> mfuture; ...... private static abstract class workerrunnable<params, result> implements callable<result> { params[] mparams; }
mfuture和mworker是在asynctask的构造方法中初始化的。我们看一下asynctask的构造方法。
public asynctask() { mworker = new workerrunnable<params, result>() { public result call() throws exception { mtaskinvoked.set(true); process.setthreadpriority(process.thread_priority_background); //noinspection unchecked result result = doinbackground(mparams); binder.flushpendingcommands(); return postresult(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); } } }; }
在这里首先创建一个workerrunnable对象mworker,并且实现了callable接口的call方法,对于这个call方面里面的内容在后面会进行详细说明。然后我们再通过mworker,创建一个futuretask对象mfuture,并且重写里面的done方法,当我们调用asynctask里面的cancel方法时,在futuretask中会调用这个done方法。在这里介绍完mworker和mfuture后我们再回过头来看executeonexecutor方法。在这里通过我们传入的params参数初始化了mworker中的mparams。而下面的exec则是在execute里面传入的sdefaultexecutor,并且执行sdefaultexecutor的execute方法。下面我们再来看一下这个sdefaultexecutor对象。
private static final int cpu_count = runtime.getruntime().availableprocessors(); private static final int core_pool_size = cpu_count + 1; private static final int maximum_pool_size = cpu_count * 2 + 1; private static final int keep_alive = 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>(128); public static final executor thread_pool_executor = new threadpoolexecutor(core_pool_size, maximum_pool_size, keep_alive, timeunit.seconds, spoolworkqueue, sthreadfactory); public static final executor serial_executor = new serialexecutor(); ...... private static volatile executor sdefaultexecutor = serial_executor; ...... 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和thread_pool_executor。serialexecutor线程池用于对任务的排队,而thread_pool_executor则是用来执行任务。而sdefaultexecutor就是serialexecutor线程池。
我们进入serialexecutor代码中看一下里面的内容。serialexecutor中的execute方法内的参数runnable就是我们传入的mfuture。在execute方法中创建了一个runnable对象,并将该队列插入对象尾部。这个时候如果是第一次执行任务。mactive必然为null,这时候便调用schedulenext方法从队列中取出runnable对象,并且通过thread_pool_executor线程池执行任务。我们可以看到在队列中的runnable的run方法中首先执行mfuture中的run方法,执行完之后又会调用schedulenext方法,从队列中取出runnable执行,直到所有的runnable执行完为止。下面就来看一下在线城池中是如何执行这个runnable的。从上面代码可以看出,在线城池中执行runnable其实最核心的部分还是执行mfuture的run方法。那就来看一下这个mfuture中的run方法。
public void run() { if (state != new || !u.compareandswapobject(this, runner, null, thread.currentthread())) return; try { callable<v> c = callable; if (c != null && state == new) { v result; boolean ran; try { result = c.call(); ran = true; } catch (throwable ex) { result = null; ran = false; setexception(ex); } if (ran) 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); } }
在这个方法中的callable对象就是我们asynctask中的mworker对象。在这里面也正是执行mworker中的call方法来完成一些耗时任务,于是我们就能想到我重写的doinbackground应该就在这个call方法中执行了。现在再回到asynctask的构造方法中看一下这个mworker中的call方法。
public result call() throws exception { mtaskinvoked.set(true); process.setthreadpriority(process.thread_priority_background); //noinspection unchecked result result = doinbackground(mparams); binder.flushpendingcommands(); return postresult(result); }
这里我们很清楚的看到它执行我们重写的doinbackground方法,并且将返回的结果传到postresult方法中。下面就在看一下这个postresult方法。
private result postresult(result result) { @suppresswarnings("unchecked") message message = gethandler().obtainmessage(message_post_result, new asynctaskresult<result>(this, result)); message.sendtotarget(); return result; }
对与这个postresult方法里面也就是在我们子线程的任务处理完成之后通过一个handler对象将message发送到主线程中,并且交由主线程处理。asynctaskresult对象中存放的是子线程返回的结果以及当前asynctask对象。下面再看一下这和handler中所处理了哪些事情。
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; } } }
可以看到在这个handler的handlemessage中会接受到两种message。在message_post_progress这个消息中主要是通过publishprogress方法将子线程执行的进度发送到主线程中并且通过onprogressupdate方法来更新进度条的显示。在message_post_result这个消息中通过当前的asynctask对象调用了finish方法,那么就来看一下这个finish方法。
private void finish(result result) { if (iscancelled()) { oncancelled(result); } else { onpostexecute(result); } mstatus = status.finished; }
这时候就可以看出若是我们取消了asynctask的话就不在执行onpostexecute方法,而是执行oncancelled方法,所以我们可以通过重写oncancelled方法来执行取消时我们需要处理的一些操作。当然若是asynctask没有被取消,这时候就回执行onpostexecute方法。到这里整个asynctask任务也就完成了。
总结
在上面的serialexecutor线程池中可以看出,当有多个异步任务同时执行的时候,它们执行的顺序是串行的,会按照任务创建的先后顺序进行一次执行。如果我们希望多个任务并发执行则可以通过asynctask中的setdefaultexecutor方法将线程池设为thread_pool_executor即可。
对于asynctask的在不同版本之间的差异不得不提一下。在android1.6,asynctask采用的是串行执行任务,在android1.6的时候采用线程池处理并行任务,而在3.0以后才通过serialexecutor线程池串行处理任务。在android4.1之前asynctask类必须在主线程中,但是在之后的版本中就被系统自动完成。而在android5.0的版本中会在activitythread的main方法中执行asynctask的init方法,而在android6.0中又将init方法删除。所以在使用这个asynctask的时候若是适配更多的系统的版本的话,使用的时候就要注意了。
源码下载:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
推荐阅读
-
Android AsyncTask使用以及源码解析
-
Android AsyncTask使用以及源码解析
-
Android3.0 ActionBar导航标题栏使用解析
-
详解Android 中AsyncTask 的使用
-
JAVA数据结构-1.普通队列和循环队列的实现以及JAVA的Queue的使用解析
-
Android MeasureSpec的理解和源码的解析
-
Android使用OKHTTP解析JSON数据的实例代码
-
String之PropertyPlaceholderConfigurery源码解析 博客分类: spring PropertyPlaceholderConfigurery源码详解使用
-
解析android中include标签的使用
-
Spring源码深度解析(第2版)- 自定义标签的使用