实际开发过程中,我们难免会遇到需要异步操作的场景:
例如用户下载升级文件,要让用户能够看到下载进度,同时完成后能点击安装,那下载的过程就是在另一个线程中完成,同时进度的显示则是要实时通知到UI主线程中的。 类似这样的轻量级的异步操作还有很多,那Android就给我们提供了一个方便的类AsyncTask
来实现。
照旧先上代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 private class DownloadFileTask extends AsyncTask <String ,Double ,Double > { @Override protected void onPreExecute () { LogUtils.i(TAG, "onPreExecute" ); super .onPreExecute(); } @Override protected Double doInBackground (String... params) { LogUtils.i(TAG, "doInBackground param:" + params[0 ] + "," + params[1 ] + "," + params[2 ] + "," + params[3 ]); publishProgress(你自己的获取到的进度值); double time_interval = CommonUtils.downloadFileFromURL(params[1 ],params[2 ],params[3 ]); return time_interval; } @Override protected void onProgressUpdate (Double... progresses) { Log.i(TAG, "onProgressUpdate(Progress... progresses) called" ); super .onProgressUpdate(); } @Override protected void onPostExecute (Double result) { Log.d(TAG, "onPostExecute result=" + result); }
以上的代码是一个最基本、最简单的没有经过太多处理的AsyncTask类,我们来分别详细看一下关于这个类的说明。
如何调用
我们可以看到在AsyncTask类后是有三种泛型类型,分别代表“启动任务执行的输入参数” 、“后台任务执行的进度” 、“后台计算结果的类型” 。在特定场合下,并不是所有类型都被使用,如果没有被使用,可以用java.lang.Void
类型代替;
通过调用new DownloadConfigureFile.execute(Params... params)
,执行一个异步任务,需要我们在代码中调用此方法,触发异步任务的执行,其中可以输入多个参数。
参数个数和类型看我们每个人的程序实际需要来完成了。比如我的程序中,CommonUtils.downloadFileFromURL(params[1],params[2],params[3])
方法需要3个参数,那么我要在UI主线程中启动AsyncTask异步线程,就要这样写:new DownloadFileTask().execute("2",download_url,"FILE","PATH");
那么最终download_url,FILE和PATH就回被作为函数的形参正确地传了进去。
onPreExecute 方法 正如我在注释中写到的内容一样:本方法主要用于异步动作执行之前,一般用来处理UI,例如显示提示框等。 在我的样例代码中并没有写任何内容,因为本身我的处理逻辑就是后台下载文件的任务,不需要用户做出什么响应,也不需要用户感知到。但是我们可以在这个方法中随意做一些我们希望提示在UI界面上的内容,比如如果当前不是WiFi环境,要弹出提示框,询问用户是否要继续下载等等。
doInBackground 方法 注意这个方法是有返回值的,返回值的类型和AsyncTask泛型中的第3个参数类型保持一致。从本方法的字面上我们也能很清楚地了解到,这个方法就是用于在后台执行任务的,它可以随时在UI界面上做提醒,相应的动作可以通过调用publishProgress
方法来实现,方法中传入的值会被onProgressUpdate
接收到,同时在执行完之后,返回的值会被onPostExecute
接收并处理。 这里附带上我的程序中后台下载任务CommonUtils.downloadFileFromURL()
方法的原代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 public static double downloadFileFromURL (String url_address, String filename, String local_path) { File file_dir = context.getDir(local_path,Context.MODE_PRIVATE); File file = new File(file_dir,filename); URL url; double time_interval; try { url = new URL(url_address); long start_time = System.currentTimeMillis(); URLConnection url_conn = url.openConnection(); url_conn.setReadTimeout(5000 ); url_conn.setConnectTimeout(5000 ); InputStream input_stream = url_conn.getInputStream(); BufferedInputStream buffer_input_stream = new BufferedInputStream(input_stream); ByteArrayBuffer byte_array_buff = new ByteArrayBuffer(1024 *1024 ); int current; while ((current = buffer_input_stream.read()) != -1 ) { byte_array_buff.append((byte ) current); } FileOutputStream file_output_stream = new FileOutputStream(file); file_output_stream.write(byte_array_buff.toByteArray()); file_output_stream.flush(); file_output_stream.close(); time_interval = (System.currentTimeMillis() - start_time)/1000.0 ; LogUtils.i("文件下载成功" , "耗时" + (System.currentTimeMillis() - start_time) + "毫秒" ); return time_interval; } catch (IOException e) { e.printStackTrace(); return -1 ; } }
onProgressUpdate 和 onPostExecute 这两个方法也能从字面上理解出来,一个是进度的更新,一个是完成之后的更新。这里就不再展开罗列了。 进度的更新我们可以做一个进度条,显示下载的进度;完成之后的动作比如说是否提示用户安装等等。