由于AsyncTask是一个抽象类,所以我们想使用它,就必须创建一个子类去继承它。
在继承时我们可以为AsyncTask指定3个泛型参数:
params:在执行AsyncTask时需要传入的参数,用于在后台任务中使用;
progress:后台任务执行时,如果需要对结果进行返回,则使用这里指定的泛型作为返回值;
result:当后台任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型;
这是我定义的一个AsyncTask子类
private class DownloadTask extends AsyncTask<String,Object,Long>{}
在DownloadTask子类里,我继承了AsyncTask,把第一个泛型参数指定为String,表示在执行时要使用的数据,第二个参数指定为Object,表示在返回数据时的类型,第三个指定为Long,表示使用Long数据来反馈执行结果;
这时我们定义的AsyncTask还只是个空任务,需要重写几个方法才能完成对任务的制定。经常需要重写的方法有以下四个:
1.onpreExecute()
这个方法会在后台任务执行前调用,用于进行一些界面上的初始化操作,这个方法也是在主线程中调用的
这是我定义的onpreExecute()
@Override protected void onPreExecute() { Log.i("iSpring", "MainActivity - > oncreate , Thread name: " + Thread.currentThread().getName()); super.onPreExecute(); btn_start.setEnabled(false); textView.setText("开始下载...."); }
初始化程序界面,一个Button值设为不可点击和TextView显示开始下载
2.doInBackGround(params...)
这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有耗时任务。任务一旦完成就可以通过return语句来将任务结果执行返回,在方法里我们是不可进行UI更新的,可以调用publishprogress()方法来完成反馈任务执行进度;
这是我定义的doInBackGround
@Override protected Long doInBackground(String... strings) { Log.i("iSpring", "MainActivity - > oncreate , Thread name: " + Thread.currentThread().getName()); long totalByte = 0; for(String url: strings){ Object[] result= downloadSingleFile(url); int byteCount = (int) result[0]; totalByte += byteCount; publishProgress(result); if(isCancelled()){ break; } } return totalByte; }
在方法里,我传入了一个String[]数组泛型参数用于调用任务使用,用一个for()遍历取出String进行任务操作,for循环里调用了一个自己定义的downloadsing
private Object[] downloadSingleFile(String str){ Object[] result = new Object[2]; int byteCount = 0; String blogName = ""; HttpURLConnection conn = null; try{ URL url = new URL(str); conn = (HttpURLConnection)url.openConnection(); InputStream is = conn.getInputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buf = new byte[1024]; int length = -1; while ((length = is.read(buf)) != -1) { baos.write(buf, 0, length); byteCount += length; } String respone = new String(baos.toByteArray(), "utf-8"); int startIndex = respone.indexOf("<title>"); if(startIndex > 0){ startIndex += 7; int endIndex = respone.indexOf("</title>"); if(endIndex > startIndex){ //解析出博客中的标题 blogName = respone.substring(startIndex, endIndex); } } }catch(MalformedURLException e){ e.printStackTrace(); }catch(IOException e){ e.printStackTrace(); }finally { if(conn != null){ conn.disconnect(); } } result[0] = byteCount; result[1] = blogName; return result; }
3.onProgressUpdate(progress...)
这个方法会在后台任务调用publishProgress(Progress...)方法后调用,接收一个指定的泛型参数,参数用于更新UI操作
@Override protected void onProgressUpdate(Object... values) { Log.i("iSpring", "MainActivity - > oncreate , Thread name: " + Thread.currentThread().getName()); super.onProgressUpdate(values); int byteCound = (int) values[0]; String blogName = (String) values[1]; String text = textView.getText().toString(); text += "\n博客《"+blogName+"》下载完成,共"+byteCound+"字节;"; textView.setText(text); }
在我重写的onProgressUpdate()里接收一个Object[]数组,取出数组里的值进行更新UI
4.onPostExecute(Result)
这个方法会在后台任务执行完毕后并通过return返回时被调用。返回的数据会作为参数被传递到该方法中.
@Override protected void onPostExecute(Long aLong) { Log.i("iSpring", "MainActivity - > oncreate , Thread name: " + Thread.currentThread().getName()+",Long值:"+aLong); super.onCancelled(); btn_start.setText("下载完成"); btn_start.setEnabled(true); }
一个比较完整的自定义AsyncTask就完成了
可以在Main中实例化,并调用execute()方法执行任务了;
@Override public void onClick(View view) { String[] urls = { "https://www.jianshu.com/p/17bb465173a6", "https://www.jianshu.com/p/f03a3dc55941", "https://www.jianshu.com/p/f9e8de453583", "https://www.jianshu.com/p/e1ea9e542112", "https://www.jianshu.com/p/0d36cefdb1a5"}; DownloadTask task = new DownloadTask(); task.execute(urls); }
在onClick()方法中初始化一个String[]数组作为参数在execute()中传入
简单来说,使用AsyncTask的诀窍就是,在doInbackGround()中执行具体的耗时操作,在onProgressUpdate()中进行UI更新,在onPostExecute()中执行任务收尾工作。
ps:在使用HttpURLConnection时会用到网络权限,别忘记在清单文件中添加权限
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
效果: