AsyncTask介绍
Android的AsyncTask比Handler更轻量级一些,适用于简单的异步处理。
首先明确Android之所以有Handler和AsyncTask,都是为了不阻塞主线程(UI线程),且UI的更新只能在主线程中完成,因此异步处理是不可避免的。
Android为了降低这个开发难度,提供了AsyncTask。AsyncTask就是一个封装过的后台任务类,顾名思义就是异步任务。
AsyncTask直接继承于Object类,位置为android.os.AsyncTask。要使用AsyncTask工作我们要提供三个泛型参数,并重载几个方法(至少重载一个)。
AsyncTask定义了三种泛型类型 Params,Progress和Result。
- Params 启动任务执行的输入参数,比如HTTP请求的URL。
- Progress 后台任务执行的百分比,一般是整数型。
- Result 后台执行任务最终返回的结果,比如File。
使用过AsyncTask 的同学都知道一个异步加载数据最少要重写以下这两个方法:
- doInBackground(Params…) 后台执行,比较耗时的操作都可以放在这里。注意这里不能直接操作UI。此方法在后台线程执行,完成任务的主要工作,通常需要较长的时间。在执行过程中可以调用publicProgress(Progress…)来更新任务的进度。
- onPostExecute(Result) 相当于Handler 处理UI的方式,在这里面可以使用在doInBackground 得到的结果处理操作UI。 此方法在主线程执行,任务执行的结果作为此方法的参数返回
有必要的话你还得重写以下这三个方法,但不是必须的:
- onProgressUpdate(Progress…) 可以使用进度条增加用户体验度。 此方法在主线程执行,用于显示任务执行的进度。
- onPreExecute() 这里是最终用户调用Excute时的接口,当任务执行之前开始调用此方法,可以在这里显示进度对话框。
- onCancelled() 用户调用取消时,要做的操作
使用AsyncTask类,以下是几条必须遵守的准则:
- Task的实例必须在UI thread中创建;
- execute方法必须在UI thread中调用;
- 不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params…), onProgressUpdate(Progress…)这几个方法;
- 该task只能被执行一次,否则多次调用时将会出现异常;
今天写了一段下载文件的AsyncTask类,以下是MainActivity类的启动代码:

package com.lanxin.testasynctask; import android.os.Bundle; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.Snackbar; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.view.View; import android.view.Menu; import android.view.MenuItem; public class MainActivity extends AppCompatActivity { private static final String TAG = "AsyncTask"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); //noinspection SimplifiableIfStatement if (id == R.id.action_settings) { return true; }else if(id == R.id.action_down){ //实例化一个AsyncTask类,并使用execute执行一个MP3下载任务; AsyncTaskEx ate = new AsyncTaskEx(this,"文件下载","下载中...",false,true); ate.execute("http://192.168.1.205/1.mp3"); return true; } return super.onOptionsItemSelected(item); } }
以下是AsyncTaskEx类的的源代码(代码中已经做了详细的解释,这里就不细说了):

package com.lanxin.testasynctask;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Environment;
import android.util.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
/**
* 后台异步执行类
* 请在执行的Acitivity的Manifest添加:android:configChanges="screenSize|orientation",否则当屏幕旋转时ProgressDialog会自动消失
* 权限申请:
* <uses-permission android:name="android.permission.INTERNET"></uses-permission>
* <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
* <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"></uses-permission>
* Created by Alan on 2016/6/3 0003.
*/
public class AsyncTaskEx extends AsyncTask<String,Integer,File> implements DialogInterface.OnCancelListener {
private static final String TAG = "AsyncTaskEx";
private Context mContext;
private ProgressDialog pd;
private boolean Indeterminate;//是否模糊的
private boolean is_auto_file = false;
private String PDTitle = "AsyncTask";
private String PDMsg = "请稍后...";
private static File _root;
private static String _file;
private static String filename;
/**
* 初始化
* @param ctx Activity上下文
* @param title ProgressDialog的标题
* @param msg ProgressDialog的显示信息
* @param isIndeterminate 是否模糊的,当为false时,会显示长条带百分比的进度条
* @param isopen 是否打开文件
*/
public AsyncTaskEx(Context ctx,String title,String msg,boolean isIndeterminate,boolean isopen){
mContext = ctx;
Indeterminate = isIndeterminate;
if(title != null && !"".equals(title))
PDTitle = title;
if(msg != null && !"".equals(msg))
PDMsg = msg;
is_auto_file = isopen;
_root = Environment.getExternalStorageDirectory();
_file = "/Download/"+ctx.getPackageName()+"/Download/";
}
/**
* 开始执行
* 这里初始化对话框
*/
protected void onPreExecute() {
pd = new ProgressDialog(mContext);
pd.setIndeterminate(Indeterminate);
pd.setCanceledOnTouchOutside(false);
if(!Indeterminate)
pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
pd.setCancelable(true);
pd.setOnCancelListener(this);
pd.setTitle(PDTitle);
pd.setMessage(PDMsg);
pd.setMax(100);
pd.setProgress(0);
pd.show();
Log.i(TAG, "AsyncTask Start");
}
/**
* 进度更新
* @param values 进度值 整数
*/
protected void onProgressUpdate(Integer... values) {
pd.setProgress(values[0]);
if(Indeterminate)
pd.setMessage(PDMsg+"..."+ values[0] + "%");
Log.i(TAG, "AsyncTasking..." + values[0]);
}
protected void onPostExecute(File result) {
pd.setMessage("下载完毕。");
if(result != null && is_auto_file){
openTheFile(result);
}
pd.dismiss();
Log.i(TAG, "AsyncTask Complete:" + result.getAbsolutePath());
}
/**
* 执行后台任务
* @param params 传递过来的参数数组
* @return
*/
@Override
protected File doInBackground(String... params) {
Log.i(TAG,"doInBackgrounding");
return doDownFile(params[0]);
}
/**
* 下载文件
* @param urls
* @return
*/
private File doDownFile(String urls){
if(urls == null || "".equals(urls))
return null;
filename = urls.substring(urls.lastIndexOf("/"));//example:DCS_0001.JPG
filename = this.checkFileExists(getDownloadDir(),filename);
try {
URL url = new URL(urls);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.connect();
if(conn.getResponseCode() == HttpURLConnection.HTTP_OK){
int length = conn.getContentLength();
int length_target = 0;
int length_tmp = 0;
InputStream in = conn.getInputStream();
FileOutputStream out = new FileOutputStream(new File(getDownloadDir() + filename));
byte[] b = new byte[1024*1024];
while ((length_tmp = in.read(b)) > 0){
length_target += length_tmp;
int progress = (length_target * 100) / length ;
publishProgress(progress);//更新进度
out.write(b, 0, length_tmp);
}
in.close();
out.flush();
out.close();
return new File(getDownloadDir()+filename);
}else{
throw new MalformedURLException("Error Code " + conn.getResponseCode());
}
} catch (MalformedURLException e) {
e.printStackTrace();
Log.e(TAG,"MalformedURLException:" + e.getMessage());
}catch (IOException e) {
e.printStackTrace();
Log.e(TAG,"IOException:" + e.getMessage());
}
return null;
}
/**
* 绑定取消,当用户点击返回键时,AsyncTask任务取消
* @param dialog
*/
@Override
public void onCancel(DialogInterface dialog) {
this.cancel(true);
File file = new File(getDownloadDir()+filename);
if(file.exists())
file.delete();
Log.i(TAG, "AsyncTask Cancel");
}
/**
* 取下载目录,默认目录是Download/PackageNmae(?)/Download目录
* @return
*/
public static String getDownloadDir(){
checkDownloadDir(_root + _file);
return _root+_file;
}
/**
* 检查下载目录是否存在,如果不存在则自动创建
* @param path
* @return
*/
public static boolean checkDownloadDir(String path){
if("".equals(path))
return false;
File file = new File(_root+_file);
if(!file.exists()){
return file.mkdirs();
}
return true;
}
/**
* 检查一个文件是否存在,如果醋在,曾返回一个新的文件名
* @param path 路径
* @param filename 文件名
* @return
*/
public static String checkFileExists(String path,String filename){
if(!path.substring(path.length()-1).equals("/")){
path += "/";
}
File file = new File(path + filename);
if(!file.exists()){
return filename;
}else{
String extend = getFileExtend(filename);
String newName = filename.replace(extend, "");
int f = 1;
File newFile = new File(path + newName + "(" + f + ")"+extend);
while (newFile.exists()){
f++;
newFile = new File(path + newName + "(" + f + ")"+extend);
}
return newName + "(" + f + ")"+extend;
}
}
/**
* 取文件的后缀名
* @param filename
* @return 返回类似: .jpg 小写字符串
*/
protected static String getFileExtend(String filename) {
return filename.substring(filename.lastIndexOf(".")).toLowerCase();
}
/**
* 判断数组是否存在某个值
* @param arr 数组
* @param name 要判断的字符串
* @return
*/
protected static boolean inArray(String[] arr, String name) {
return Arrays.asList(arr).contains(name);
}
/**
* 打开文件
* @param file
*/
public void openTheFile(File file){
Intent intent = new Intent();
String fileName = file.getAbsolutePath();
String[] imgs = {"image/*",".png",".jpg",".jpeg",".gif",".bmp"};
String[] videos = {"video/*",".mp4",".3gp",".avi",".flv",".wmv",".rmvb",".asf",".mkv",".mpg"};
String[] audios = {"audio/*",".mp3",".ogg",".ape",".wav",".wma"};
if(inArray(imgs,getFileExtend(fileName))){
intent.setAction(android.content.Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), "image/*");
mContext.startActivity(intent);
}else if(inArray(videos,getFileExtend(fileName))){
intent.setAction(android.content.Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), "video/*");
mContext.startActivity(intent);
}else if(inArray(audios,getFileExtend(fileName))){
intent.setAction(android.content.Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), "audio/*");
mContext.startActivity(intent);
}else if(fileName.endsWith(".apk")){
intent.setAction("android.intent.action.VIEW");
intent.addCategory("android.intent.category.DEFAULT");
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
mContext.startActivity(intent);
}
}
}
最后就是申请权限了,如果没有执行这步,会报错的:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.lanxin.testasynctask" >
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="screenSize|orientation"
android:theme="@style/AppTheme.NoActionBar" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"></uses-permission>
</manifest>
运行的效果图:
写给有需要的人。


