📄 download.java
字号:
}
Thread.currentThread().interrupt();//中断线程,跳出循环
System.out.println("计时线程中断."+Thread.currentThread().isInterrupted());
return;
}
}
}
}
/**
* 计算下载任务的进度.
*
*/
private void getProgress()
{
this.downLoadSchedule= this.interceptFolat((point/(1024f*1024))+readTotal/(1024f*1024))+"M";
}
/**
* 通过断点值得到下载任务进度.公有方法,被外部调用.
* @param point
* @return
*/
public String getProgress(long point)
{
return this.interceptFolat(point/(1024f*1024))+"M";
}
/**
* 格式化float,保留两个小数点.
* @param f
* @return
*/
private float interceptFolat(float f)
{
return new BigDecimal(f).setScale(2, BigDecimal.ROUND_HALF_DOWN).floatValue();
}
/**
* 格式化时间:(0:0:0)
* @param f
* @return
*/
public String getTime(float f)
{
int i=(int)f;
//小时
int hour=i/3600;
//分
int min=(i%3600)/60;
//秒
int sec=(i%3600)%60;
if(hour>99)
{
hour=99;
}
return hour+":"+min+":"+sec;
}
}
/**
* 同步方法,有任务完成时调用.传递一个线程锁作参数.
* @param downLoad
*/
private synchronized void saveTaskEnd(DownLoad downLoad)
{
JTable taskEndTable=SaveRunTime.getTASKEND_TABLE();
taskEndTable.setValueAt(new XDownTable().getImageIcon("/image/Ok.png"), TASK_COMPLETE, 0);
taskEndTable.setValueAt(downLoad.saveFileName, TASK_COMPLETE, 1);
//下载进度.
taskEndTable.setValueAt(new CounterThread().interceptFolat(downLoad.fileLength/(1024*1024f))+"M", TASK_COMPLETE, 2);
taskEndTable.setValueAt("--", TASK_COMPLETE, 3);
taskEndTable.setValueAt("--", TASK_COMPLETE, 4);
taskEndTable.setValueAt(new CounterThread().getTime(downLoad.second+0f), TASK_COMPLETE, 5);
taskEndTable.setValueAt(downLoad.fileType, TASK_COMPLETE, 6);
TASK_COMPLETE++;//静态变量.
}
/**
* 用于改造从URL得到的文件名,如/music/c.mp3 ,只取c.mp3
* @param fileName
* @return
*/
public String interceptURL(String fileName)
{
return new StringBuilder(fileName).delete(0, fileName.lastIndexOf("/")+1).toString();
}
/**
* 控制线程运行状态时调用.
* @param downLoadSign
*/
public void setDownLoadSign(int downLoadSign)
{
this.downLoadSign=downLoadSign;
}
/**
*返回线程的运行状态.
* @return
*/
public int getDownLoadSign()
{
return this.downLoadSign;
}
/**
* 返回保存的文件名.
* @return
*/
public String getDownLoadFileName()
{
return this.saveFileName;
}
/**
* 返回保存的文件路径.
* @return
*/
public String getDownLoadFileDirectory()
{
return this.saveDirectory;
}
/**
* 有任务完成,或删除任务时调用.传递行索引值.判断此索引行周围的线程运行状态.作出处理.
* @param row
*/
public synchronized void taskEnd(int row)
{
List list=SaveRunTime.getLIST();
if(row==(list.size()-1))//如果最后一个任务完成.
{
//直接清空行.
removeLock(row);//删除锁
setLastRowNull();//先删除锁,再清空最后一行.
return;
}
else
{
setRowIndexInfo(row);//身后还有任务.
}
}
/**
* 返回给定行的线程对象.只被setRowIndexInfo()方法调用.
* @param row
* @return
*/
private DownLoad getRowIndexDownLoadObj(int row)
{
return (DownLoad)SaveRunTime.getLIST().get(row);
}
/**
* 有任务完成,或删除任务时由taskEnd()方法调用.
* @param row
*/
private synchronized void setRowIndexInfo(int row)
{
int rowFirst=row;
DownLoad downLoad=null;
JTable DOWNTABLE=SaveRunTime.getDOWN_TABLE();
for(;row<SaveRunTime.getLIST().size()-1;row++)
{
downLoad=getRowIndexDownLoadObj(row+1);//紧挨着身后的一行.
if(downLoad.downLoadSign==1)//一个正在运行的线程.
{
//正在运行图片.
DOWNTABLE.setValueAt(new XDownTable().getImageIcon("/image/ing.png"), row, 0);
DOWNTABLE.setValueAt("",row,3);//速度
DOWNTABLE.setValueAt("",row,4);//剩余时间
}
else if(downLoad.downLoadSign==0||downLoad.downLoadSign==-3)//线程在等待状态
{
//暂停图片.
DOWNTABLE.setValueAt(new XDownTable().getImageIcon("/image/dpause.png"), row, 0);
DOWNTABLE.setValueAt("--",row,3);//速度
DOWNTABLE.setValueAt("--",row,4);//剩余时间
}
else if(downLoad.downLoadSign==-1)//任务停止中.
{
//链接中断图片.
DOWNTABLE.setValueAt(new XDownTable().getImageIcon("/image/die.png"), row, 0);
DOWNTABLE.setValueAt("--",row,3);//速度
DOWNTABLE.setValueAt("--",row,4);//剩余时间
}//将下一行的内容设置向上移动一行
DOWNTABLE.setValueAt(DOWNTABLE.getValueAt(row+1, 1), row, 1);//文件名.
DOWNTABLE.setValueAt(DOWNTABLE.getValueAt(row+1, 2), row, 2);//下载进度表.
DOWNTABLE.setValueAt(DOWNTABLE.getValueAt(row+1, 5), row, 5);//已用时间.
DOWNTABLE.setValueAt(DOWNTABLE.getValueAt(row+1, 6), row, 6);//文件类型.
downLoad.row--;//每个线程对应行都要向上移动一个位置.
}
removeLock(rowFirst);//移除完成任务的锁.有效行减一.
setLastRowNull();//最后一行清空.
}
/**
* 返回给定线程对象的的链接地址.
* @param downLoad
* @return
*/
public String getURL(DownLoad downLoad)
{
return downLoad.link;
}
/**
* 返回给定线程对象的文件长度.
* @param downLoad
* @return
*/
public long getFileLength(DownLoad downLoad)
{
return downLoad.fileLength;
}
/**
* 返回给定线程对象的主机名.
* @param downLoad
* @return
*/
public String getHost(DownLoad downLoad)
{
return downLoad.hostName;
}
/**
*清空最后一行.有任务完成,或删除任务时调用
*
*/
private void setLastRowNull()
{
for(int i=0;i<7;i++)
{
SaveRunTime.getDOWN_TABLE().setValueAt("", SaveRunTime.getNEWTASK().getRow(), i);
}
}
/**
* 移除给定行的下载线程锁,计时线程锁,保存在HashMap中对应的任务信息.表格有效行减一.
* @param row
*/
private synchronized void removeLock(int row)
{
SaveRunTime.getLIST().remove(row);//线程锁,
SaveRunTime.getTIMELIST().remove(row);//计时锁.
SaveRunTime.getSAVE_HASHMAP().remove(row);//如果是一个无效的链接,hashMap中也将存放部着下载任务的部分信息
SaveRunTime.getNEWTASK().setRow(SaveRunTime.getNEWTASK().getRow()-1);//有效行减一.
}
}
/**
* 线程类,测试给定链接是否有效.如果是,在启动下载线程后退出.
*
*/
class TestLink implements Runnable
{
private int count=0;//测试链接次数,
private int row=0; //行索引
private String url=null;//链接地址.
private String choos=null;//保存路径.
private String saveAs=null;//另存为文件名.
private long point=0;//断点
private static JTable tm=SaveRunTime.getDOWN_TABLE();//表格对象.
private DownLoad downLoad=null;//下载线程.
/**
* 构造方法,参数列表:1,行索引.2,链接地址,3,保存路径.4,保存文件名,5,断点.
* @param row
* @param url
* @param choos
* @param saveAs
* @param point
*/
public TestLink(int row,String url,String choos,String saveAs,long point)
{
this.row=row;
this.url=url;
this.choos=choos;
this.saveAs=saveAs;
this.point=point;
}
/**
* 测试链接地址.并决定是否去启动下载线程.
*/
public void run()
{
System.out.println("启动一个测试线程...");
//创建一个下载线程.等待测试成功后启动.
downLoad=new DownLoad(row,url,choos,saveAs,point);
//不管链接是否有效,往集合类中添加锁.
if(row==SaveRunTime.getLIST().size()) //如果是新行,将是最后一行.
{
SaveRunTime.getLIST().add(downLoad);//下载线程.
}
if(row==SaveRunTime.getTIMELIST().size())//计时线程.永远不会是正在运行的计时线程对象.
{
SaveRunTime.getTIMELIST().add(downLoad.new CounterThread());
}
//表格内容显示,
linkError(0);
boolean f=downLoad.fileInfo();//测试连接是否有效.
if(f)//如果true,启动一个下载下线程.此方法被调用时,下载线程类部分属性将被赋值.
{
linkError(1);
System.out.println("启动一个下载线程.....");
new Thread(downLoad).start();
try
{
Thread.sleep(1000);//睡眠一秒再写xml.
new XmlOperation().writerTaskInfo();
} catch (Exception ex)
{
ex.printStackTrace();
}
((DownLoad)SaveRunTime.getLIST().get(row)).setDownLoadSign(1);//设置下载线程运行状态.
Thread.currentThread().interrupt();//中断测试线程.
System.out.println("得到连接,测试线程中断:"+Thread.currentThread().isInterrupted());
}
else
{
HashMap<String,String> hm=new HashMap<String,String>(7);//有七个元素.
hm.put("正在获取链接...","");
hm.put("文件名称",saveAs);
hm.put("文件长度","未知");
hm.put("文件类型","未知");
hm.put("保存路径",choos); //不在测试十次后添加键值对.防止测试还没完成,右击删除键抛出异常.
hm.put("链接地址",url);
hm.put("对方主机","未知");
SaveRunTime.getSAVE_HASHMAP().add(hm);
while(count<11)
{
try
{
f=downLoad.fileInfo();
if(f)
{
new Thread(downLoad).start();
break;
}
//如果没得到链接.尝试连接5秒.
System.out.println("尝试连接"+count+"次");
Thread.sleep(500);
if(count==10)
{
//传递连接失败信息.
linkError(-1);
try
{
//写入xml
new XmlOperation().writerTaskInfo();
} catch (Exception e)
{
e.printStackTrace();
}
System.out.println("连接失败,测试线程中断:"+Thread.currentThread().isInterrupted());
}
count++;
} catch (InterruptedException e)
{
}
}
Thread.currentThread().interrupt();//中断线程.
}
}
/**
* 控制表格信息变化.
* @param s
*/
private void linkError(int s)
{
tm.setValueAt(new XDownTable().getImageIcon("/image/ing.png"), row, 0);
if(s==1)
{ //返回连接成功信息.
SaveRunTime.getDOWN_INFOTABLE().setValueAt("连接成功", 0, 1);
}
else if(s==-1)
{
//返回连接失败信息.
tm.setValueAt(new XDownTable().getImageIcon("/image/die.png"), row, 0);
SaveRunTime.getDOWN_INFOTABLE().setValueAt("连接失败", 0, 1);
}
//其它设置不变.
tm.setValueAt(saveAs,row,1);
tm.setValueAt(downLoad.new CounterThread().getProgress(point),row,2);
tm.setValueAt("--",row,3);
tm.setValueAt("--",row,4);
}
}
/**
*
* 附:
* 每个下载任务的开始会启动三个线程:首先由测试线程测试链接是否有效,接着启动下载线程,读到第一个字节后启动计时线程.
*
* 计时线程总是由下载线程控制.
*
* 每个下载任务都有一个下载线程锁和计时线程锁.被分别保存在同步的List中,存放顺序与行索引相对应.
*
* 锁在测试线程中被添加,如果已经存在则不再添加.即控制线程运行状态的可能并不是正在运行的线程对象.
* 方便下载异常中断后,再次重新启动线程时继续控制.通过查看锁的downLoadSign值得知下载任务运行状态.
*
* 关键变量: 行索引rowIndex,在newTaskDialog新建任务时进行累加.而不管下载线程是否会运行.
*
* 下载线程的开关变量,在碰到异常中断后,保留其-1值,不去改变.再次启动时判断.
* 对应的计时线程开关变量,在强制中断后设置成可运行状态.
*
* 一个下载任务完成后行索引值减一,需要在list中移除一个元素.其身后的所有对象的行索引都减一.
*
* 所有通过SaveRunTime类得到的对象,都应该在使用前赋值.
*
* 读取到历史任务后的处理, 历史任务中任务condition值 为-3时任务图标设为暂停状态,与真正的线程暂停区分开来.
* -1时为断开状态,不必区分.
*
* 测试线程还没有退出时,避免右键删除带来的HashMap值为空时抛出的异常.(未完成)
*
* 程序加载历史任务时需要完成以下几个动作:
* 1.给集合类中添加下载线程锁和计时线程锁.添加下载线程锁时需要调用DownLoad的带参构造方法,给部分属性赋值.
* 计时线程锁,直接new.
* 2.DOWNINFOTABLE对应的hashMap添加键值对.
* 3.NewTask的属性rowIndex值等于历史任务的个数.
*
*
*
*
*
*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -