AsyncTask小案例
△关于安卓里的异步任务
→安卓里的异步任务,可以当做java里面的多线程来看待了,在java语言里,想要启动多线程就只有两个方法:继承Thread类并重写run()方法,或者实现Runnable接口并且重写run()方法。
→安卓里面,想要启动多个线程可以通过:1,java原来有的两个方法(Thread类和Runnable接口);2,Handler类提供方法启动异步任务;3,AsyncTask类也能实现异步任务;4,IntentService类(Service的子类)也能实现异步任务,虽然这个和上述的相比有些勉强。
△为什么需要使用AsyncTask
Handler类提供很完整的异步任务机制,但是他的使用相对复杂,对于刚刚入手安卓的人来说,不好理解,AsyncTask的底层也是通过Handler来实现,通过封装,相对来说比较能让新手使用,简单方便。
△关于安卓的单线程模型
虽说AsyncTask封装的很好,能让你几乎在不了解安卓多线程工作的具体情况之下,照样使用异步任务,但是如果想理解好异步任务,还得说说安卓的某些线程的知识。理解这些知识以后,对于理解AsyncTask使用的注意事项,也更方便。
→主线程与UI线程:安卓应用在启动时,就是启动一个进程,这个进程可能会有若干线程,其中有个就是所谓“主线程”,他又叫做UI线程,首先记住:UI线程==主线程。
→安卓的单线程模型:详细情况你可以在网上查到,我在这里说说一定要记住的:
(1)不要阻塞UI线程:别在UI线程里面进行耗时操作(一般就是不要执行超过五秒钟的操作),否则将会出现ANR对话框(应用程序没有响应 )。
(2)只能够在主线程里更新UI:所有对UI的更新(比如修改TextView显示内容,修改ImageView的图片),都只能在主线程里进行,因为安卓对UI的操作是线程不安全的,如果你在非UI线程里更新UI,会抛异常。
△AsyncTask的简介
→他是个类,而且还是个抽象类,一个安卓封装好的用来简化异步任务的类,只有继承了并指定泛型参数,覆写指定的方法后才能使用,使用他,你能在几乎不了解安卓系统多线程的工作过程的前提下,使用安卓多线程的功能。
→类原型:public abstract class AsyncTask<Params, Progress,Result>,三个泛型参数的功能分别是:
(1)Params:启动后台任务时的输入参数,比如你要下载一些文件,那参数是这些文件的URI。
(2)Progress:任务执行过程中更新进度的参数,比如传给进度条的参数。
(3)Result:任务执行完成之后所返回的结果。
→主要方法:
(1)protected abstract Result doInBackground(Params... params);
这是必须要实现的方法,所有异步任务都在里面这些执行,同时,该方法的所有代码都运行在非UI线程里,根据刚才所说到的单线程的模型,这个方法里面绝对不能进行任何对UI的操作。
(2)protected void onPreExecute();
异步任务执行前的准备操作,该方法在异步任务正式启动之前被回调(doInBackground()方法执行之前回调),该方法的所有代码都执行在UI线程里面,这里可以进行UI操作,比如,将进度条的进度值设置为零。但是这里不能进行耗时操作,因为是在UI线程。
(3)protected void onProgressUpdate(Progress... values);
任务执行过程中更新UI的操作,当你在"doInBackground()"方法里面调用函数"publishProgress(Progress... values)",实际执行的代码是"onProgressUpdate(Progress... values)"方法里面所写代码。这个方法里的所有代码都运行在UI线程里,可以进行更新UI操作,但是不能进行耗时操作。
(4)protected void onPostExecute(Result result);
当异步任务正常结束后(doInBackground()方法运行结束之后),这个方法将被回调,该方法的所有代码都运行在UI线程,可以在这进行完成任务相关提示,这里不能进行耗时操作,注意:如果异步任务是被取消掉的,这个方法不会执行。
AsyncTask类方法小结
(1)异步任务各个方法执行顺序:onPreExecute()→doInBackground()→onProgressUpdate()(如果调用过publishProgress方法)→onPostExecute()。
(2)除了doInBackground()方法里的代码是运行在子线程里面的,其与方法的代码是跑在UI线程里面。
△AsyncTask使用注意
→一个AsycnTask实例只能被执行一次(只能够execute()一次),第二次会出现异常。
→刚才提到所重写的四个方法,都不能有开发者所调用,都不能有开发者所调用,都不能有开发者所调用,重要的事情说三遍,只能重写,他们都有系统调用。
△示例代码
下面来看看实例了,模拟一个文件下载效果,布局文件很简单的,先来看看布局文件代码:
<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:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_print_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="正下载第0个文件"
android:gravity="center_horizontal"
android:textSize="24sp" />
<Button
android:id="@+id/btn_star_asyncTask"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="启动一个异步任务"
android:onClick="asyncTask"/>
<Button
android:id="@+id/btn_pauseAsyncTask"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="取消当前异步任务"
android:onClick="asyncTask"/>
<ProgressBar
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/progressBar" />
</LinearLayout>
再来看看窗体代码:
package com.example.masynctask;
import android.app.Activity;
import android.os.AsyncTask;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
import java.io.File;
import java.io.InputStream;
public class MainActivity extends Activity {
//准备数组待会传递异步任务
private String[] strings = new String[]{"一号文件","二号文件","三号文件","四号文件"};
private TextView textView = null;//这个用来更新窗体
private ProgressBar progressBar = null;//进度
private MAsyncTask mAsyncTask = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView)findViewById(R.id.tv_print_content);
progressBar = (ProgressBar)findViewById(R.id.progressBar);
}
public void asyncTask(View view){
switch(view.getId()) {
case R.id.btn_star_asyncTask:
mAsyncTask = new MAsyncTask();
mAsyncTask.execute(strings);
break;
case R.id.btn_pauseAsyncTask:
mAsyncTask.cancel(true);
break;
}
}
// 第一参数:执行时传递进去的类型
// 第二参数:更新那时用的参数
// 第三参数:结束那是传递进去"onPostExecute"方法的参数
//AsyncTask里面的泛型,和上述所介绍哪些方法里的泛型,是对应的。
private class MAsyncTask extends AsyncTask<String,Integer,Integer>{
@Override
protected Integer doInBackground(String... params) {
Log.i("test", "doInBackGround");
int i = 0;
while(i<params.length) {
publishProgress(i,params.length);
try {
Thread.sleep(4000L);
} catch (Exception e) {
e.printStackTrace();
}
i++;
if (isCancelled()){
break;
}
}
Log.i("test","循环结束");
return params.length;
}
// 这个方法将在子线程开始前执行
@Override
protected void onPreExecute() {
super.onPreExecute();
Log.i("test","onPreExecute");
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
// Log.i("test","正在下载第"+values[0]+"个文件");
textView.setText("正下载第"+values[0]+"文件");
progressBar.setProgress( (int)((values[0]+1)/(float)values[1]*100) );
}
// 这个方法将执行在异步任务结束之后
@Override
protected void onPostExecute(Integer integer) {
super.onPostExecute(integer);
Log.i("test", "onPostExecute");
}
@Override
protected void onCancelled(Integer integer) {
super.onCancelled(integer);
Log.i("test", "onCancelled");
}
}