Android类FileDownloadList分析
先上代码,再来分析
publicclassFileDownloadList{ /**上下文*/ privateContextmContext; /**请求对象*/ privateBaseRequestLimsfileRequest=null; /**进度条对话框*/ privateAlertDialogprogressDialog=null; /**进度条控件变量*/ privateProgressBarmProgress; /**百分比显示控件*/ privateTextViewmProgressPercent; privateFilelocalFile=null; /**接收HttpHelper中获取到文件大小后发送的广播,确定文件大小*/ privateDownLoadReceiverreceiver; /**文件大小*/ privatelongfileLength=-1L; /**是否已注册广播标志*/ privatebooleancastFlag=false; /**是否显示进度条标志*/ privatebooleanshowDialog=false; /**文件下载完的回调接口*/ privateRunnablemCallback=null; privateHandlermHandler=newHandler(){ @Override publicvoidhandleMessage(Messagemsg){ super.handleMessage(msg); inttempSize=(int)localFile.length(); if(tempSize<fileLength){ //文件下载中 if(showDialog){ //显示了进度条的情况下,更新进度条 intprogress=(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(Exceptione){ e.printStackTrace(); } } } } }; /** *构造器 *@paramactivity */ /** *构造器 *@paramactivity *@paramshowDialog显示进度条标志 */ publicFileDownloadList(Contextcontext,booleanshowDialog){ mContext=context; this.showDialog=showDialog; fileRequest=newBaseRequestLims(context,ClientServiceType.FILE_DOWN); fileRequest.setMethodType(BaseRequestLims.METHOD_TYPE_POST); fileRequest.setContext(mContext); } publicBaseRequestLimsgetFileRequest(){ returnfileRequest; } /** *通过关联类型来下载文件 *@paramfileName文件名称或文件在服务器上的相对路径加名称 *@paramsaveDir保存在本地的文件目录 *@paramsaveName保存在本地的文件名称 *@paramgllx关联类型 *@paramcallback下载后的处理线程 */ publicvoiddownloadFile(StringfileName,StringsaveDir,StringsaveName,Runnablecallback){ if(callback!=null){ mCallback=callback; } FilesaveDirFile=newFile(saveDir); //judgethesavedirpathexistornot if(!saveDirFile.exists()){ saveDirFile.mkdirs(); } localFile=newFile(saveDir,saveName); if(localFile.isDirectory()){ newAlertDialog.Builder(mContext).setTitle("提示").setMessage("thesavefileisdirectory").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); } } /** *进入文件下载子线程 *@paramrequest */ privatevoidinvokeFile(finalBaseRequestLimsrequest){ try{ if(showDialog){ buildProgressDialog().show(); } receiver=newDownLoadReceiver(); IntentFilterfilter=newIntentFilter(); filter.addAction("SAVE_DOWNLOAD_FILE"); mContext.registerReceiver(receiver,filter); castFlag=true; //下载的子线程 newThread(){ @Override publicvoidrun(){ super.run(); HttpHelper.invoke(request); } }.start(); }catch(Exceptione){ e.printStackTrace(); } } /** *创建进度对话框 *@return */ privateAlertDialogbuildProgressDialog(){ AlertDialog.Builderbuilder=newBuilder(mContext); builder.setTitle("正在下载文件,请稍候..."); RelativeLayoutcontainer=newRelativeLayout(mContext); mProgress=newProgressBar(mContext); mProgress.setId("progress".hashCode()); BeanUtils.setFieldValue(mProgress,"mOnlyIndeterminate",Boolean.valueOf(false)); mProgress.setIndeterminate(false); LayerDrawablelayerDrawable=(LayerDrawable)mContext.getResources().getDrawable(android.R.drawable.progress_horizontal); ClipDrawableclipDrawable=(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=newTextView(mContext); mProgressPercent.setId("percent".hashCode()); mProgressPercent.setText("0%"); mProgressPercent.setTextSize(18); intcontainerPadding=DimensionUtils.dip2Px(mContext,10); container.setPadding(containerPadding,containerPadding,containerPadding,containerPadding); LayoutParamsprogressLayoutParams=newLayoutParams( LayoutParams.MATCH_PARENT,DimensionUtils.dip2Px(mContext,4)); progressLayoutParams.addRule(RelativeLayout.CENTER_VERTICAL); progressLayoutParams.addRule(RelativeLayout.LEFT_OF,mProgressPercent.getId()); mProgress.setLayoutParams(progressLayoutParams); LayoutParamspercentLayoutParams=newLayoutParams( 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("取消",newOnClickListener(){ @Override publicvoidonClick(DialogInterfacedialog,intwhich){ dialog.dismiss(); } }); progressDialog=builder.create(); returnprogressDialog; } classDownLoadReceiverextendsBroadcastReceiver{ @Override publicvoidonReceive(Contextcontext,Intentintent){ //显示进度条 fileLength=intent.getLongExtra("FILE_LENGTH",-1); if(showDialog){ mProgress.setMax((int)fileLength); } //更新进度条的线程 newThread(){ @Override publicvoidrun(){ super.run(); while(true){ try{ Thread.sleep(500); }catch(Exceptione){ e.printStackTrace(); } mHandler.sendMessage(mHandler.obtainMessage()); //获取下载文件的大小 intloadedSize=(int)localFile.length(); if(loadedSize>=fileLength){ break; } } } }.start(); } } publicDownLoadReceivergetReciver() { returnreceiver; } }
它的逻辑:
创建一个FileDownloadList对象后,就可以直接使用该下述方法来实现下载功能。
downloadFile(StringfileName,StringsaveDir,StringsaveName,Runnablecallback)
在实现上是这么个意思:
1.在当前上下文,开启下载线程。当获取到要下载的文件的大小时,发送一个广播过来(这部分没有展示在上述代码中)。
2.在当前上下文中,注册一个广播监听器,监听广播标识为SAVE_DOWNLOAD_FILE的广播。首次监听到发出来的广播后,首次发送过来的广播,包含了要下载的文件的大小信息,然后就每隔5毫秒检测本地文件的大小,直到本地文件的大小(loadedSize)大于等于要下载的文件(fileLength)大小时,退出该循环。
在不断检测的过程中,通过mHandler.sendMessage(mHandler.obtainMessage());,让UI线程更新进度条。
下载线程,会不断将服务器返回的数据流,写到本地文件中,所以,本地文件的大小会不断变化,直到,它的大小跟要下载的文件的大小相等时,就退出这个不断检测本地文件大小的线程。
其它没有在上述代码中表现出来的内容(在其它部分的代码中):
1.在invokeFile(finalBaseRequestLimsrequest)方法中,开了一个如下的下载线程.该下载线程,会将服务器返回的文件流,写到本地文件(localFile)中;然后,它还会发送一个标识为SAVE_DOWNLOAD广播,包含的信息有要下载文件的文件大小fileLength。
//下载的子线程 newThread(){ @Override publicvoidrun(){ super.run(); HttpHelper.invoke(request); } }.start();
上述代码存在的问题:
1.上下文,使用的是某个Activity,如果发生系统调用了该Activity的onDestroy()时,下载线程还没有完成,也就意味着,loadedSize的大小还是小于fileLength。从而,那个不断检测本地文件大小的线程就一直在执行着。
即是检测本地文件大小的线程和下载线程还在执行着:
检测本地文件大小的线程:
newThread(){ @Override publicvoidrun(){ super.run(); while(true){ try{ Thread.sleep(500); }catch(Exceptione){ e.printStackTrace(); } mHandler.sendMessage(mHandler.obtainMessage()); //获取下载文件的大小 intloadedSize=(int)localFile.length(); if(loadedSize>=fileLength){ break; } } } }.start();
下载线程:
newThread(){ @Override publicvoidrun(){ super.run(); HttpHelper.invoke(request); } }.start();
那么,会出现什么问题呢?
1).我可以确定的就是,mContext会出现泄漏。
2).DownLoadReceiver不能正常被取消注册。
分析,待续。