最近因为在做项目的过程中经常需要进行网络传输,所以打算把几个常用的网络通信框架和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
继续努力~~~好好学习~~~!!!求关注!!!求互粉!!!!!!