首先Android有Handler+Thread和AsyncTask,都是为了不阻塞主线程,即UI线程,而UI线程的更新只能在主线程完成,因此异步加载是不可避免的。
AsyncTask是对Thread+Handler的一个轻量级封装,使需要与用户界面长时间运行的任务变得更简单,不需要借助Handler+Thread即可实现。
AsyncTask有何优点:
- 通过conCurrent框架来管理匿名线程,conCurrent是一个很成熟的并发框架 ,能很好管理匿名线程
- 线程的开销较大,如果每个任务都需要创建一个线程,那么应用程序效率要低很多,而AsyncTask会开辟一个线程池,最大值128 ,可以在线程池中取线程,当需要开辟多个线程时能更好的减少开销。
- 对于子线程与主线程通信 相对Handler更加友好,比如进度更新用AsyncTask会相对简单。
模板代码,其中DoInBackground是必须实现的方法。注意Object… params这种参数表示可增长型Object数组
public class MainActivity extends Activity implements OnClickListener{
AsyncTask<Object, Object, Object> asyncTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/**
* 第一个参数:可为任意Object或Void,在excute(Object)里传入,可传入多个object,也是doInBackground的参数,根据下标可以取出
* 第二个参数:可为任意Object或Void,当doInBackground里调用publishProgress时传入,可传入多个object,在publishProgress根据下标取出
* 第三个参数:可为任意Object或Void,为doInBackground和onCancelled的返回值
*/
asyncTask=new AsyncTask<Object, Object, Object>() {
/**
* 在doInBackground前完成一些操作,如进度条的初始化
* 执行线程:主线程
*/
@Override
protected void onPreExecute() {
super.onPreExecute();
}
/**
* 把所有耗时代码都放到这里
*/
@Override
protected Object doInBackground(Object... params) {
if(isCancelled()){//isCancelled为true时表示当前任务被打断
return new Object();//接下来会调用onCancelled并把返回对象传过去
}else{//isCancelled为false表示当前任务没被打断
publishProgress(new Object());//当调用publishProgress(new Object())会调用onProgressUpdate(Object... values)
}
return new Object();//doInBackground任务没被打断正常结束后,会调用onPostExecute
}
/**
* doInBackground完成时调用
*/
@Override
protected void onPostExecute(Object result) {
super.onPostExecute(result);
//任务结束了
}
/**
* 当doInBackground里调用publishProgress时会调用
*/
@Override
protected void onProgressUpdate(Object... values) {
super.onProgressUpdate(values);
//更新了
}
@Override
/**
* 当程序被打断返回后滴用
*/
protected void onCancelled(Object result) {
super.onCancelled(result);
}
};
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
//线程执行asyncTask.execute(传进的参数是doInBackground里的参数)
case R.id.excute:
asyncTask.execute(new Object())
break;
case R.id.cancel:
//当调用cancel(true)时,会吧isCancelled的值设置为false
asyncTask.cancel(true);
break;
default:
break;
}
}
}
注意事项
- AsyncTask的实例必须在UI线程中创建
- execute方法必须在UI线程中调用
- 不要手动调用onPreExecute(),onPostExecute(),doInBackground(),onCancelled,onProgressUpdate()这几个方法
- AsyncTask的实例只能被执行一次
案例:短信备份
<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/status"
android:layout_width="match_parent"
android:layout_height="40dp"
android:gravity="center"
android:text="上次备份时间:2015年1月12日 "
android:textSize="20sp" />
<!-- 进度条 -->
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="10dp"
android:visibility="gone"/>
<!-- 按钮 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/start"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="1"
android:gravity="center"
android:onClick="backup"
android:text="备份 "/>
<Button
android:id="@+id/stop"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="1"
android:gravity="center"
android:text="取消 "
android:onClick="cancel"
android:clickable="false"/>
</LinearLayout>
</LinearLayout>
public class MainActivity extends Activity {
private TextView textView;
private ProgressBar progressBar;
private Button backup;
private Button cancel;
//记录当前备份到了第几条短信
private int currentPosition = 0;
//记录备份时间
private String backupTime;
//这里三个参数,第一个表示启动任务无输入参数,第二个是任务执行过程中根据传入的正在备份第几条短信更新进度条
//第三个表示,程序备份完成后,返回备份时间
private AsyncTask<Void, Integer, Void> asyncTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_backup);
initWeight();
// 读取sd卡的sms.xml获取上次更新时间,如果文件不存在则显示还没备份过
File file = new File(Environment.getExternalStorageDirectory()
.getAbsolutePath(), "sms.xml");
if (file.exists()) {
try {
FileInputStream is = new FileInputStream(file);
textView.setText("上次备份时间:" + getBackupTime(is));
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
textView.setText("还没备份过,赶紧备份吧");
}
}
/**
* 初始化控件
*/
private void initWeight() {
// TODO Auto-generated method stub
textView = (TextView) findViewById(R.id.status);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
backup=(Button) findViewById(R.id.start);
cancel=(Button) findViewById(R.id.stop);
}
/**
* 备份按钮点击事件
* @param view
*/
public void backup(View view) {
asyncTask = new AsyncTask<Void, Integer, Void>() {
@Override
protected Void doInBackground(Void... params) {
// content://sms 查询系统所有短信的uri
Uri uri = Uri.parse("content://sms/");
// 获取ContentResolver对象
ContentResolver resolver = getContentResolver();
Cursor cursor = resolver.query(uri, new String[] { "address",
"date", "type", "body" }, null, null, null);
//获取系统短信的条数,用publishProgress传入条数的负数,来与当前是第几天区分
publishProgress(-cursor.getCount());
//下面是Xml序列化,把cursor对象转化成xml
try {
// 创建一个序列化器
XmlSerializer serializer = Xml.newSerializer();
//确认sd卡是否挂载
if (Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)) {
File file = new File(
Environment.getExternalStorageDirectory(),
"sms.xml");
FileOutputStream fos = new FileOutputStream(file);
serializer.setOutput(fos, "utf-8");
serializer.startDocument("utf-8", true);
serializer.startTag(null, "smss");
serializer.startTag(null, "backupTime");
backupTime = geTime();
serializer.text(backupTime + "");
serializer.endTag(null, "backupTime");
while (cursor.moveToNext()) {
if (isCancelled()) {
return null;
} else {
serializer.startTag(null, "sms");
serializer.startTag(null, "address");
serializer.text(cursor.getString(0) + "");
serializer.endTag(null, "address");
serializer.startTag(null, "date");
serializer.text(cursor.getString(1) + "");
serializer.endTag(null, "date");
serializer.startTag(null, "type");
serializer.text(cursor.getString(2) + "");
serializer.endTag(null, "type");
serializer.startTag(null, "body");
serializer.text(cursor.getString(3));
serializer.endTag(null, "body");
serializer.endTag(null, "sms");
//把当前备份第几条短信创建去
publishProgress(++currentPosition);
//停1秒,让进度条变化明显些
Thread.sleep(1000);
}
}
serializer.endTag(null, "smss");
serializer.endDocument();
//注意关闭不用的资源
fos.close();
cursor.close();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 任务执行之前的一些界面操作,把进度条显示出来,按钮是否可以点击的设置
*/
@Override
protected void onPreExecute() {
// TODO Auto-generated method stub
super.onPreExecute();
backup.setClickable(false);
cancel.setClickable(true);
progressBar.setVisibility(View.VISIBLE);
System.out.println("onpreexecute");
currentPosition=0;
progressBar.setProgress(currentPosition);
}
/**
* 任务完成后,关闭进度条,界面更新上次备份时间
*/
@Override
protected void onPostExecute(Void result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
Toast.makeText(MainActivity.this, "备份成功", 1).show();
textView.setText("上次备份时间:" + backupTime);
backup.setClickable(true);
cancel.setClickable(false);
progressBar.setVisibility(View.GONE);
}
//publishProgress更新调用的方法,当参数大于0表示是由1条短信备份好了,更新进度条,小于0表示设置progressbar最大值
@Override
protected void onProgressUpdate(Integer... values) {
// TODO Auto-generated method stub
super.onProgressUpdate(values);
if (values[0] <= 0) {
progressBar.setMax(-values[0]);
progressBar.setProgress(0);
} else {
progressBar.setProgress(values[0]);
}
}
//任务中断了
@Override
protected void onCancelled() {
// TODO Auto-generated method stub
super.onCancelled();
Toast.makeText(MainActivity.this, "备份中断", Toast.LENGTH_SHORT)
.show();
backup.setClickable(true);
cancel.setClickable(false);
}
}.execute();
}
/**
* 取消按钮点击事件
*
* @param view
*/
public void cancel(View view) {
asyncTask.cancel(true);
asyncTask=null;
}
/**
* 传入xml文件流对象,根据xml解析器获取更新时间
* @param is
* @return
*/
private String getBackupTime(InputStream is) {
try {
XmlPullParser parser=Xml.newPullParser();
parser.setInput(is, "utf-8");
int type=parser.getEventType();
while(type!=XmlPullParser.END_DOCUMENT){
switch (type) {
case XmlPullParser.START_TAG:
if("backupTime".equals(parser.getName())){
return parser.nextText();
}
break;
default:
break;
}
type=parser.next();
}
} catch (XmlPullParserException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "";
}
/**
* 获取当前系统时间,并格式化
* @return
*/
public String geTime() {
Date date = new Date();
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time = df.format(date);
return time;
}
}
该案例当取消备份时,sms.xml会被为清空,可以在任务完成前先复制sms.xml文件,当取消时,用复制的替换sms.xml,当没取消时删除复制后的就好。