最近因为在做项目的过程中经常需要进行网络传输,所以打算把几个常用的网络通信框架和GitHub上面的开源框架梳理一遍,本文简单介绍了AsyncTask工作原理以及一个十分简单的应用demo。
当然,了解一个组件,最好是先从Android API文档入手。
那么首先我们来看一下AsyncTask的继承结构:
可以看到,AsyncTask跟Handler一样,是直接从Object类继承的,属于安卓系统包里的基本组件。
再来看看文档中对AsyncTask给出的描述:
从中我们可以得到3个比较重要的信息点:
1、AsyncTask与handler一样,都是为了防止线程阻塞而用于执行简单的异步处理的类。
2、比起Handler实现异步的过程:需要使用到Handler, Looper, Message,Thread四个对象,并需要通过主线程启动Thread(子线程)运行并生成Message-àLooper获取Message并传递给HandleràHandler逐个获取Looper中的Message,并进行UI变更。AsyncTask的实现过程更为简单,只有4个步骤,分别为onpreexecute,doInBackground,onProgressUpdate和onpostexecute。
3、AsyncTask是被设计成处理异步操作的一个辅助类而不是一个通用的线程框架,应该被用于一些简单的通信异步处理的情况中(最多几秒钟),如果要保持长时间的通信线程运行(比如文件传输之类的),最好不要使用AsyncTask!
AsyncTask的实现步骤:
这里参考了:http://www.cnblogs.com/devinzhang/archive/2012/02/13/2350070.html的博文,归纳得十分透彻精辟,值得牢记。
1、首先我们可以通过实现一个类继承AsyncTask来实现异步加载数据,如下所示:
public class TestAsyncTask extends AsyncTask<String, Integer, String>
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> 其中,AsyncTask定义了三种泛型类型:Params,Progress和Result。Params :启动任务执行的输入参数,比如HTTP请求的URL。Progress :后台任务执行的百分比。Result 后台执行任务最终返回的结果,这里用到的是String。</span>
2、实现这个异步加载类必须要有以下两个方法:
doInBackground(Params…) :在后台执行,比较耗时的操作都可以放在这里。但是不能直接操作UI。此方法在后台线程执行,完成任务的主要工作,通常需要较长的时间。在执行过程中可以调用publicProgress(Progress…)来更新任务的进度。
onPostExecute(Result): 相当于Handler 处理UI的方式,在这里面可以使用在doInBackground 得到的结果处理操作UI。 此方法在主线程执行,任务执行的结果作为此方法的参数返回。
3、有必要的话你还得重写以下这三个方法,但不是必须的:
onProgressUpdate(Progress…) : 可以使用进度条增加用户体验度。 此方法在主线程执行,用于显示任务执行的进度。
onPreExecute() : 这里是最终用户调用Excute时的接口,当任务执行之前开始调用此方法,可以在这里显示进度对话框。
onCancelled() : 用户调用取消时,要做的操作
4、使用AsyncTask类,以下是几条必须遵守的准则:
Task的实例必须在UI thread中创建;
execute方法必须在UI thread中调用;
不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)这几个方法;
该task只能被执行一次,否则多次调用时将会出现异常;
AsyncTask实现执行过程源码分析:
TestAsyncTask testAsyncTask = (TestAsyncTask) new TestAsyncTask(TestAsyAty.this,textView,progressBar).execute("");
首先,例如我们在Activity中调用自定义的AsyncTask实现异步操作,先要执行execute()方法执行这个异步任务。
@MainThread public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); }
可以看到,在execute方法的源码内部,会执行一个executeOnExecutor()方法用以return一个AsyncTask的实例:
@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的实例,还可以看到关键的一点是执行了onPreExecute()方法,于是跳转到onPreExecute()方法:
/** * Runs on the UI thread before {@link #doInBackground}. * * @see #onPostExecute * @see #doInBackground */ @MainThread protected void onPreExecute() { }
注释已经很明确了,在doInBackground()方法之前执行,那么跳转到doInBackground():
/** * Override this method to perform a computation on a background thread. The * specified parameters are the parameters passed to {@link #execute} * by the caller of this task. * * This method can call {@link #publishProgress} to publish updates * on the UI thread. * * @param params The parameters of the task. * * @return A result, defined by the subclass of this task. * * @see #onPreExecute() * @see #onPostExecute * @see #publishProgress */ @WorkerThread protected abstract Result doInBackground(Params... params);
可以通过重写该方法执行异步任务操作(执行网络请求什么的可以放doInBackground()里面)。在该方法中可以调用publishProgress()方法实现对进度条的更新,增加用户体验度。
在方法之后,执行的自然是onProgressUpdate()方法进行对UI的更新操作:
/** * Runs on the UI thread after {@link #publishProgress} is invoked. * The specified values are the values passed to {@link #publishProgress}. * * @param values The values indicating progress. * * @see #publishProgress * @see #doInBackground */ @SuppressWarnings({"UnusedDeclaration"}) @MainThread protected void onProgressUpdate(Progress... values) { }
现在回到doInBackground()方法,虽然注释中并没有给出下一个要执行的方法,不过通过阅读上文,冰雪聪明の你应该已经想到了:在执行完异步操作之后,下一步自然是要更新UI,执行onPostExecute(Result)方法了:
/** * <p>Runs on the UI thread after {@link #doInBackground}. The * specified result is the value returned by {@link #doInBackground}.</p> * * <p>This method won‘t be invoked if the task was cancelled.</p> * * @param result The result of the operation computed by {@link #doInBackground}. * * @see #onPreExecute * @see #doInBackground * @see #onCancelled(Object) */ @SuppressWarnings({"UnusedDeclaration"}) @MainThread protected void onPostExecute(Result result) { }
可以看到该方法确实是在doInbackground()方法之后调用的,接受到的result也是doInbackground()的返回值,还有一个要注意的点:如果异步任务呗取消(用户调用onCancelled()方法),那么onPostExecute()方法是不会被执行的!
整体流程结构图如下所示(画的比较简陋,轻喷。。):
AsyncTask简单Demo实现:
下面给出一个比较简单的Demo实现,通过异步操作调用百度的身份证查询API接口查询个人资料数据(result是json数据,这里就不解析了= =)。
注释比较详细这里就不赘述了大家看源码吧(づ ̄ 3 ̄)づ:
TestAsyAty.java
public class TestAsyAty extends Activity { TestAsyncTask testAsyncTask; ProgressBar progressBar; TextView showtv; public static final String API_KEY = "f31209d4d0c59c0fb4dcca8b9282f2f9"; public String path = "http://apis.baidu.com/apistore/idservice/id"; String httpArg = "id=420984198704207896"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.testasy); progressBar = (ProgressBar) findViewById(R.id.progressBar); progressBar.setMax(100); showtv = (TextView) findViewById(R.id.textView); progressBar.setMax(100); testAsyncTask = new TestAsyncTask(showtv,progressBar,httpArg); testAsyncTask.execute(); } }
TestAsyncTask.java
public class TestAsyncTask extends AsyncTask<String, Integer, String> { String httpUrl = "http://apis.baidu.com/apistore/idservice/id"; String httpArg ; TextView tv; ProgressBar bar; public TestAsyncTask(TextView tv,ProgressBar bar,String httpArg) { super(); this.tv = tv; this.bar = bar; this.httpArg = httpArg; } /** * 这里的String参数对应AsyncTask中的第一个参数 * 这里的String返回值对应AsyncTask的第三个参数 * 该方法并不运行在UI线程当中,主要用于异步操作,所有在该方法中不能对UI当中的空间进行设置和修改 * 但是可以调用publishProgress方法触发onProgressUpdate对UI进行操作 * @param params * @return */ @Override protected String doInBackground(String... params) { BufferedReader reader = null; String result = null; StringBuffer sbf = new StringBuffer(); httpUrl = httpUrl + "?" + httpArg; try { URL url = new URL(httpUrl); HttpURLConnection connection = (HttpURLConnection) url .openConnection(); connection.setRequestMethod("GET"); connection.setRequestProperty("apikey", "f31209d4d0c59c0fb4dcca8b9282f2f9"); connection.connect(); InputStream is = connection.getInputStream(); reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); String strRead = null; while ((strRead = reader.readLine()) != null) { sbf.append(strRead); sbf.append("\r\n"); } reader.close(); result = sbf.toString(); if (result != null) { publishProgress(100); } } catch (Exception e) { e.printStackTrace(); } return result; } /** * 这里的String参数对应AsyncTask中的第三个参数(也就是接收doInBackground的返回值) * 在doInBackground方法执行结束之后在运行 * 并且运行在UI线程当中 可以对UI空间进行设置 */ @Override protected void onPostExecute(String s) { tv.setText(s); } //该方法运行在UI线程当中,并且运行在UI线程当中 可以对UI空间进行设置 @Override protected void onPreExecute() { tv.setText("开始执行异步线程"); } /** * 这里的Intege参数对应AsyncTask中的第二个参数 * 在doInBackground方法当中,,每次调用publishProgress方法都会触发onProgressUpdate执行 * onProgressUpdate是在UI线程中执行,所有可以对UI空间进行操作 */ @Override protected void onProgressUpdate(Integer... values) { int v = values[0]; bar.setProgress(v); } }
运行效果:
解析什么的就自己做辣~\(≧▽≦)/~~
下篇文章: AsyncTask解析(下)——实现自定义AsyncTask网络传输工具类封装
实现了自定义AsyncTask的封装以及实现细节,可以点进去看看哟QAQ
继续努力~~~好好学习~~~!!!求关注!!!求互粉!!!!!!