先上代码,再来分析
public class FileDownloadList {/**上下文*/ private Context mContext; /**请求对象*/ private BaseRequestLims fileRequest = null; /**进度条对话框*/ private AlertDialog progressDialog = null; /**进度条控件变量*/ private ProgressBar mProgress; /**百分比显示控件*/ private TextView mProgressPercent;private File localFile = null; /**接收HttpHelper中获取到文件大小后发送的广播,确定文件大小*/ private DownLoadReceiver receiver; /**文件大小*/ private long fileLength = -1L; /**是否已注册广播标志*/ private boolean castFlag = false; /**是否显示进度条标志*/ private boolean showDialog = false; /**文件下载完的回调接口*/ private Runnable mCallback = null;private Handler mHandler = new Handler(){@Overridepublic void handleMessage(Message msg) { super.handleMessage(msg); int tempSize = (int)localFile.length(); if(tempSize < fileLength){//文件下载中if(showDialog){//显示了进度条的情况下,更新进度条int progress = (int)((Double.valueOf(tempSize) / Double.valueOf(fileLength)) * 100);mProgress.setProgress(tempSize);mProgressPercent.setText(progress + "%");}}else{//下载文件完毕if(castFlag){//如已注册广播,注销广播mContext.unregisterReceiver(receiver);castFlag = false;}if(showDialog){ mProgress.setProgress((int)fileLength); mProgressPercent.setText("100%"); progressDialog.dismiss();}if(mCallback != null){ try{Thread.sleep(500);mCallback.run(); }catch (Exception e) {e.printStackTrace(); }} }} };/*** 构造器* @param activity*//*** 构造器* @param activity* @param showDialog 显示进度条标志*/ public FileDownloadList(Context context, boolean showDialog){mContext = context;this.showDialog = showDialog;fileRequest = new BaseRequestLims(context,ClientServiceType.FILE_DOWN);fileRequest.setMethodType(BaseRequestLims.METHOD_TYPE_POST);fileRequest.setContext(mContext); }public BaseRequestLims getFileRequest(){return fileRequest; }/*** 通过关联类型来下载文件* @param fileName 文件名称或文件在服务器上的相对路径加名称* @param saveDir 保存在本地的文件目录* @param saveName 保存在本地的文件名称* @param gllx 关联类型* @param callback 下载后的处理线程*/ public void downloadFile(String fileName, String saveDir, String saveName, Runnable callback){if(callback != null){ mCallback = callback;}File saveDirFile = new File(saveDir);//judge the save dir path exist or not if(!saveDirFile.exists()){ saveDirFile.mkdirs();}localFile = new File(saveDir,saveName);if(localFile.isDirectory()){ new AlertDialog.Builder(mContext).setTitle("提示").setMessage("the save file is directory").show(); return;}if(fileRequest.getServiceType()==null){ fileRequest.setServiceType(ClientServiceType.FILE_DOWN);}fileRequest.addParameter("fpath", fileName);fileRequest.addParameter("fname", saveName);fileRequest.setStreamPath(localFile.getAbsolutePath());fileRequest.setStream(true);if(localFile.exists()){ if(localFile.length() == 0){invokeFile(fileRequest); }else{//文件存在直接打开if(showDialog) buildProgressDialog().show();mHandler.sendMessage(mHandler.obtainMessage()); }}else{ invokeFile(fileRequest);} }/*** 进入文件下载子线程* @param request*/ private void invokeFile(final BaseRequestLims request){try{ if(showDialog){buildProgressDialog().show(); } receiver = new DownLoadReceiver(); IntentFilter filter = new IntentFilter(); filter.addAction("SAVE_DOWNLOAD_FILE"); mContext.registerReceiver(receiver, filter); castFlag = true; //下载的子线程 new Thread(){@Overridepublic void run() { super.run(); HttpHelper.invoke(request);} }.start();}catch (Exception e) { e.printStackTrace();} }/*** 创建进度对话框* @return*/ private AlertDialog buildProgressDialog(){AlertDialog.Builder builder = new Builder(mContext);builder.setTitle("正在下载文件,请稍候...");RelativeLayout container = new RelativeLayout(mContext);mProgress = new ProgressBar(mContext);mProgress.setId("progress".hashCode());BeanUtils.setFieldValue(mProgress, "mOnlyIndeterminate", Boolean.valueOf(false));mProgress.setIndeterminate(false);LayerDrawable layerDrawable = (LayerDrawable)mContext.getResources().getDrawable(android.R.drawable.progress_horizontal);ClipDrawable clipDrawable = (ClipDrawable)layerDrawable.getDrawable(2);clipDrawable.setColorFilter(Color.parseColor("#32B5E5"), Mode.SRC_IN);mProgress.setProgressDrawable(layerDrawable);mProgress.setPadding(0, 0, 0, 0);mProgress.setIndeterminateDrawable(mContext.getResources().getDrawable(android.R.drawable.progress_indeterminate_horizontal));mProgressPercent = new TextView(mContext);mProgressPercent.setId("percent".hashCode());mProgressPercent.setText("0%");mProgressPercent.setTextSize(18);int containerPadding = DimensionUtils.dip2Px(mContext, 10);container.setPadding(containerPadding, containerPadding, containerPadding, containerPadding);LayoutParams progressLayoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, DimensionUtils.dip2Px(mContext, 4));progressLayoutParams.addRule(RelativeLayout.CENTER_VERTICAL);progressLayoutParams.addRule(RelativeLayout.LEFT_OF, mProgressPercent.getId());mProgress.setLayoutParams(progressLayoutParams);LayoutParams percentLayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);percentLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);percentLayoutParams.addRule(RelativeLayout.CENTER_VERTICAL);mProgressPercent.setLayoutParams(percentLayoutParams);container.addView(mProgressPercent);container.addView(mProgress);builder.setView(container);builder.setNegativeButton("取消", new OnClickListener() {@Override public void onClick(DialogInterface dialog, int which) {dialog.dismiss(); }});progressDialog = builder.create();return progressDialog; }class DownLoadReceiver extends BroadcastReceiver{@Overridepublic void onReceive(Context context, Intent intent) { //显示进度条 fileLength = intent.getLongExtra("FILE_LENGTH", -1); if(showDialog){mProgress.setMax((int)fileLength); } //更新进度条的线程 new Thread(){@Overridepublic void run() { super.run(); while(true){try{ Thread.sleep(500);}catch (Exception e) { e.printStackTrace();}mHandler.sendMessage(mHandler.obtainMessage());//获取下载文件的大小int loadedSize = (int)localFile.length();if(loadedSize >= fileLength){ break;} }} }.start();} }public DownLoadReceiver getReciver() {return receiver; }}
它的逻辑:
创建一个FileDownloadList对象后,就可以直接使用该下述方法来实现下载功能。
downloadFile(String fileName, String saveDir, String saveName, Runnable callback)
在实现上是这么个意思:
1.在当前上下文,开启下载线程。当获取到要下载的文件的大小时,发送一个广播过来(这部分没有展示在上述代码中)。
2.在当前上下文中,注册一个广播监听器,监听广播标识为SAVE_DOWNLOAD_FILE的广播。首次监听到发出来的广播后,首次发送过来的广播,包含了要下载的文件的大小信息,然后就每隔5毫秒检测本地文件的大小,直到本地文件的大小(loadedSize)大于等于要下载的文件(fileLength)大小时,退出该循环。
在不断检测的过程中,通过mHandler.sendMessage(mHandler.obtainMessage()); ,让UI线程更新进度条。
下载线程,会不断将服务器返回的数据流,写到本地文件中,所以,本地文件的大小会不断变化,直到,它的大小跟要下载的文件的大小相等时,就退出这个不断检测本地文件大小的线程。
其它没有在上述代码中表现出来的内容(在其它部分的代码中):
1.在invokeFile( final BaseRequestLims request)方法中,开了一个如下的下载线程.该下载线程,会将服务器返回的文件流,写到本地文件(localFile)中;然后,它还会发送一个标识为SAVE_DOWNLOAD广播,包含的信息有要下载文件的文件大小fileLength。
//下载的子线程 new Thread(){@Overridepublic void run() { super.run(); HttpHelper.invoke(request);} }.start();
上述代码存在的问题:
1.上下文,使用的是某个Activity,如果发生系统调用了该Activity的onDestroy()时,下载线程还没有完成,也就意味着,loadedSize的大小还是小于fileLength。从而,那个不断检测本地文件大小的线程就一直在执行着。
即是检测本地文件大小的线程和下载线程还在执行着:
检测本地文件大小的线程:
new Thread(){@Overridepublic void run() { super.run(); while(true){try{ Thread.sleep(500);}catch (Exception e) { e.printStackTrace();}mHandler.sendMessage(mHandler.obtainMessage());//获取下载文件的大小int loadedSize = (int)localFile.length();if(loadedSize >= fileLength){ break;} }} }.start();
下载线程:
new Thread(){@Overridepublic void run() {super.run();HttpHelper.invoke(request);}}.start();
那么,会出现什么问题呢?
1).我可以确定的就是,mContext会出现泄漏。
2). DownLoadReceiver不能正常被取消注册。
分析,待续。