上面的图片展示的只是我们在搭建好服务器的情况下,新建一个Serverlet的方式。具体的从头开始搭建Serverlet的方式大家可以查看下面的连接。
Serverlet具体的搭建方法
注:如果之前已经搭建好的服务器,但是现在不能用了,出现404 错误的,可以参考以上代码重新搭建一遍就能用了。
单线程的下载思想就是通过URL来连接Web的下载路径,这样我们一边读入再写出到手机上就可以了。
由于主线程中不能操作UI界面、不能有耗时操作,这里我们通过之前的异步加载AsyncTask(封装好的Thread)来实现。
注:异步加载AsyncTask中不能使用Toast语句,可以使用Log来代替。
1、新建一个类继承AsyncTask,在它的doInBackground方法中进行URL连接、读入与写出
2、对Button点击事件监听,当有点击时开启该线程即可。
class SingleDownload extends AsyncTask<String, Integer, String> {
@Override
protected String doInBackground(String... params) {
try {
//获得URL路径
URL url = new URL(
"http://192.168.0.35:8080/Myserverlets/music/aa.mp3");
//获得URL连接
URLConnection connection = url.openConnection();
//获得文件的总长度
int length = connection.getContentLength();
//获得输入流
InputStream is = connection.getInputStream();
//新建要写入的文件
File file = new File(Environment.getExternalStorageDirectory(),
"bb.mp3");
if (!file.exists()) {
file.createNewFile();
}
//获得输出流
FileOutputStream os = new FileOutputStream(file);
//通过字节读取,每次读取1024B
byte[] array = new byte[1024];
int sum = 0;
int index = is.read(array);
while (index != -1) {
//写出array,长度0~index
os.write(array, 0, index);
sum += index;
publishProgress(sum, length);
index = is.read(array);
}
os.flush();
os.close();
is.close();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
Toast.makeText(getApplicationContext(), "执行" + values[0],
Toast.LENGTH_SHORT).show();
progressbar.setProgress((int) (values[0] * 100.0 / values[1]));
Toast.makeText(getApplicationContext(), "执行" + values[1],
Toast.LENGTH_SHORT).show();
}
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
}
}
}
Button监听
case R.id.download_single:
SingleDownload singledownload = new SingleDownload();
singledownload.execute();
break;
多线程下载的思想是开启多个线程将一整个文件进行分段现在,下载开始的位置不同,比如我们的一个文件大小为11,假设我们想要开启3个线程,这样我们第一线程的写出位置为0~2,第二个线程的写出位置为3~5,第三个线程的写出位置本来应该是6~8这样我们还剩了一小部分没有写出,因此我们将第三个线程的写出的结束位置修改为10 ,这样就可以全部写出了。
同样因为网络下载是一种耗时操作,我们需要新启线程,并在线程中新启多个线程同时下载。当按钮点击时调用下面方法。
步骤:
1、先写要开启的多个线程
2、再在MainActivity中开启线程来开启多线程(这里我们用了一个Thread数组对多线程进行管理,其实也可以使用线程池来管理Executor来管理)
1、开启的多个线程
public class MultiDownLoad extends Thread {
private String urlpath;
private String filepath;
private long start;
private long end;
private long length;
private long sum=0;
public MultiDownLoad(String url, String filepath, long start, long end) {
this.urlpath = url;
this.filepath = filepath;
this.start = start;
this.end = end;
}
public long getSum() {
return sum;
}
@Override
public void run() {
try {
URL url=new URL(urlpath);
URLConnection conn=url.openConnection();
conn.setAllowUserInteraction(true);
conn.setRequestProperty("Range", "bytes="+start+"-"+end);
InputStream is=conn.getInputStream();
byte[] array=new byte[1024];
File file=new File(filepath);
RandomAccessFile randomAccessFile=new RandomAccessFile(file, "rw");
randomAccessFile.seek(start);
int index=is.read(array);
while(index!=-1){
randomAccessFile.write(array, 0, index);
sum+=index;
index=is.read();
}
if(randomAccessFile!=null){
randomAccessFile.close();
}
if(is!=null){
is.close();
}
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
2、在MainActivity中开启线程来开启多线程,按钮点击时调用该方法。
private void mutidownload() {
new Thread(){
@Override
public void run() {
String urlpath="http://192.168.0.35:8080/Myserverlets/music/aa.mp3";
try {
URL url=new URL(urlpath);
URLConnection conn=url.openConnection();
length=conn.getContentLength();
//
String fileName=getFileName(urlpath);
Log.d("文件名",fileName);
File file=new File(Environment.getExternalStorageDirectory(),fileName);
if(!file.exists()){
file.createNewFile();
}
MultiDownLoad[] multithread=new MultiDownLoad[4];
for(int i=0;i<4;i++){
MultiDownLoad thread=null;
long start=length/4*i;
long end=length/4*(i+1);
if(i==3){
end=length;
}
thread=new MultiDownLoad(urlpath,file.getAbsolutePath(), start, end);
thread.start();
multithread[i]=thread;
}
boolean isfinish=true;
while(isfinish){
long sum=0;
for(MultiDownLoad thread:multithread){
sum+=thread.getSum();
}
Message msg=handler.obtainMessage();
msg.obj=sum;
msg.what=0x233;
handler.sendMessage(msg);
if(sum+10>=length){
isfinish=false;
}
Thread.sleep(1000);
}
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}.start();
}
//该方法用于获得要保存成的文件名
private String getFileName(String url){
return url.substring(url.lastIndexOf("/")+1);
}
1、单线程与AsycTask的结合使用
2、多线程的思路(线程中再开多个线程)
3、获得要保存成的文件名的方式