1. 写在前面
前面一两个月一直在赶需求,不管什么功能先在网上或者github上找到做出来再说,最近半个月没事做,一直在改代码。发现以前项目中的软件更新没做好,刚好看到郭神书里面这文件下载例子用于更新下载不错,让后台下载任务在服务中执行,然后通过activity和服务绑定,使activity和service可以进行通信。并且好好的学习了asyncTask,现在框架用的比较多,很少有机会接触到这个了。这个文件下载比较好的地方一个是可以像QQ样,更新时可以在界面上还可以操作,还有一个是实现了断点下载,可以接着上次没下载完的地方继续下载。效果图如下:
2. 学习
a.继承AsyncTask需要指定的三个参数,AsyncTask(params,progress,result),看着api,可以了解他们的含义
params:在使用asynctask时需要传入的参数,可以供后台任务使用,可以为void;
progress:后台任务执行时,如果需要在界面上显示当前进度,则使用这里指定的泛型为单位;
result:当后台任务执行完毕时,需要对结果进行返回,这里指定什么泛型就返回什么样的数据类型,比如,这里指定boolean,则后台任务执行完毕返回true或者false;
当然,继承AsyncTask我们经常要重写的几个方法,看api
解释过来,onPreExecute():在后台任务之前在ui线程中调用,这一步通常用于设置后台任务,比如展示一个进度条在用户面前;
doInBackground(Params…):(这翻译过来很直观)在onpreExecute()方法一结束,立马在后台线程调用,这一步在后台运行计算,可能需要一段时间,异步任务的参数传递到这一步,这一步的这些计算一定要有返回,并且要将这些返回结果返回到最后一步。这一步同样是可以使用publishProgress(Progress…)这个方法去发布一个或多个单元的进度,这些值被发布在ui线程的 onProgressUpdate(Progress…) 这一步中;
onProgressUpdate(Progress…):当publishProgress(Progress…)被使用后,被调用在ui线程,执行的时间是不能确定的,这个方法用与在后台任务一直被执行时,展示在用户面前的进度,它能够被用于带动画的进度条或者文本log;
onPostExecute(Result): 被调用在ui线程,当后台任务计算完成,后台任务计算的结果被用来当做参数在这一步中;
b. 有关File这个类的一些构造方法。郭神在保存下载文件时是将它保存在公共下载目录下,我搞错了一个地方所以对他获取file对象的方法很不理解。网上也查不到自己想要的答案,后面在api中看了了File的构造方法。
c. 活动和服务通信。在活动中控制服务。通过在service中继承Binder写个自己的Binder类,里面写上自己想要在活动中调用的方法。同时注意在service中重写的onBind()中返回我们自己的Binder对象。然后在活动中 创建ServiceConnection的匿名类,在里面重写onServiceConnected()方法和onServiceDisconnected()方法,这两个方法会在活动和服务成功绑定和断开时调用。并且在onServiceConnected()方法中,获得自己的Binder对象,这样子就可以在活动中想调用Binder中的方法就调用什么方法了。
3. 代码
a.
DownloadTask extends AsyncTask<String,Integer,Integer>,
在选择了这样三种参数,即我们传入String类型参数供后台任务使用,用Integer来表示后台任务的进度,后台任务返回的是Integer类型,这样的的返回值就是我们在onPostExecute()方法中的参数。
这里没有使用onPreExecute()方法,所以在执行这个asynctask直接从后台任务开始。
@Override
protected Integer doInBackground(String... params) {
InputStream is = null;
RandomAccessFile savedFile = null ;
File file = null;
try {
long downloadFileLength = 0; //记录已下载的文件长度
String downloadUrl = params[0];
String fileName = downloadUrl.substring(downloadUrl.lastIndexOf("/"));
//---> /storage/emulated/0/Download
String directroy = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
Log.i(TAG, "directroyPath--->" + directroy);
file = new File(directroy+fileName);
if(file.exists()){ //存在,拿到这个文件已经下载的长度,从这里开始下载
downloadFileLength = file.length();
}
//获得这个下载文件的总长度,使用okhttp
long contentLength = getContentLength(downloadUrl);
if(contentLength==0){
//url地址文件长度为0,下载失败
return TYPE_FAILED;
}else if(contentLength==downloadFileLength){ //下载完成
return TYPE_SUCCESS;
}
//运行到这里,说明既不会这个url地址有问题,也不会说这个已经下载的文件长度已经下载完成了
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(downloadUrl)
.addHeader("RANGE","bytes="+downloadFileLength+"-")
.build();
Response response = client.newCall(request).execute();//同步堵塞
if(response!=null){
is = response.body().byteStream();
savedFile = new RandomAccessFile(file, "rw");
savedFile.seek(downloadFileLength);