Android类FileDownloadList分析
先上代码,再来分析
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(){ @override public 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(){ @override public 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{ @override public void onreceive(context context, intent intent) { //显示进度条 filelength = intent.getlongextra("file_length", -1); if(showdialog){ mprogress.setmax((int)filelength); } //更新进度条的线程 new thread(){ @override public 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(){ @override public void run() { super.run(); httphelper.invoke(request); } }.start();
上述代码存在的问题:
1.上下文,使用的是某个activity,如果发生系统调用了该activity的ondestroy()时,下载线程还没有完成,也就意味着,loadedsize的大小还是小于filelength。从而,那个不断检测本地文件大小的线程就一直在执行着。
即是检测本地文件大小的线程和下载线程还在执行着:
检测本地文件大小的线程:
new thread(){ @override public 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(){ @override public void run() { super.run(); httphelper.invoke(request); } }.start();
那么,会出现什么问题呢?
1).我可以确定的就是,mcontext会出现泄漏。
2). downloadreceiver不能正常被取消注册。
分析,待续。