day05

1使用httpurlconnection方式把数据提交到服务器
    基于什么协议?http
    get方式:组拼URL地址把数据拼到url上 有大小限制 1kb  4kb
    post方式: post方式提交安全  没有大小限制
    post方式和get方式区别:
        [1]路径不同
        [2]请求方式不同
        [3]post方式要自己组拼请求体的内容
            post方式比get方式多了2个头(content-length和content-type)

get方式提交数据代码如下
  1. new Thread(){public void run() {
  2. try{
  3. //[2]获取用户名和密码
  4. String name = et_username.getText().toString().trim();
  5. String pwd = et_password.getText().toString().trim();
  6. //[2.1]定义get方式要提交的路径
  7. String path = "http://192.168.11.73:8000/login/loginServlet?username=" + name + "&password=" + pwd;
  8. //[2]
  9. URL url = new URL(path);
  10. HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  11. conn.setRequestMethod("GET");
  12. conn.setConnectTimeout(5000);
  13. int code = conn.getResponseCode();
  14. if(code == 200){
  15. InputStream inputStream = conn.getInputStream();
  16. String content = StreamTools.readStream(inputStream);
  17. //把服务器的数据展示到Toast上
  18. showToast(content);
  19. }
  20. }
  21. catch(Exception e)
  22. {
  23. e.printStackTrace();
  24. }
  25. };}.start();

post方式提交数据代码如下(主要☆☆☆☆☆处
  1. new Thread(){public void run() {
  2. try{
  3. //[2]获取用户名和密码
  4. String name = et_username.getText().toString().trim();
  5. String pwd = et_password.getText().toString().trim();
  6. //☆☆☆☆☆和get方式提交数据区别四:设置请求体
  7. //String data = "username=" + name + "&password=" + pwd;
  8. //[2.1]定义get方式要提交的路径
  9. //☆☆☆☆☆和get方式提交数据区别一:路径不同
  10. //String path = "http://192.168.11.73:8000/login/loginServlet";
  11. String path = "http://192.168.11.73:8000/login/loginServlet?username=" + name + "&password=" + pwd;
  12. //[2]
  13. URL url = new URL(path);
  14. HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  15. //☆☆☆☆☆和get方式提交数据区别二:设置请求方式是post
  16. //conn.setRequestMethod("POST");
  17. conn.setRequestMethod("GET");
  18. conn.setConnectTimeout(5000);
  19. //☆☆☆☆☆和get方式提交数据区别三:多设置两个请求头信息
  20. //conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
  21. //conn.setRequestProperty("Content-Length", data.length() + "");
  22. //☆☆☆☆☆和get方式提交数据区别五:向服务器提交数据
  23. //设置一个标记 允许输出
  24. //conn.setDoOutput(true);
  25. //conn.getOutputStream().write(data.getBytes());
  26. int code = conn.getResponseCode();
  27. if(code == 200){
  28. InputStream inputStream = conn.getInputStream();
  29. String content = StreamTools.readStream(inputStream);
  30. //把服务器的数据展示到Toast上
  31. showToast(content);
  32. }
  33. }
  34. catch(Exception e)
  35. {
  36. e.printStackTrace();
  37. }
  38. };}.start();



2乱码问题解决

3以httpclient方式把数据提交到服务器
    [1]开源项目
    [2]谷歌一般以 base default simple 进行命名
    [3]对http请求的封装

get方式提交数据代码如下
  1. new Thread() {
  2. public void run() {
  3. try {
  4. // [2]获取用户名和密码
  5. String name = et_username.getText().toString().trim();
  6. String pwd = et_password.getText().toString().trim();
  7. name = URLEncoder.encode(name, "utf-8");
  8. pwd = URLEncoder.encode(pwd, "utf-8");
  9. String path = "http://192.168.11.73:8000/login/loginServlet?username="
  10. + name + "&password=" + pwd;
  11. // 获取httpclient实例
  12. DefaultHttpClient client = new DefaultHttpClient();
  13. // 准备get请求 定义一个httpget实现
  14. HttpGet get = new HttpGet(path);
  15. // 执行一个get请求
  16. HttpResponse response = client.execute(get);
  17. // 获取服务器返回的状态码
  18. int code = response.getStatusLine().getStatusCode();
  19. if (code == 200) {
  20. InputStream inputStream = response.getEntity()
  21. .getContent();
  22. String content = StreamTools.readStream(inputStream);
  23. showToast(content + "");
  24. }
  25. } catch (Exception e) {
  26. e.printStackTrace();
  27. }
  28. };
  29. }.start();
post方式提交数据代码如下
  1. new Thread() {
  2. public void run() {
  3. try {
  4. // [2]获取用户名和密码
  5. String name = et_username.getText().toString().trim();
  6. String pwd = et_password.getText().toString().trim();
  7. // [3]以httpclient方式进行提交
  8. DefaultHttpClient client = new DefaultHttpClient();
  9. // [3.1]准备post请求
  10. String Path = "http://192.168.11.73:8080/login/LoginServlet";
  11. HttpPost post = new HttpPost(Path);
  12. //[3.1.0]准备parameters
  13. List<NameValuePair> lists = new ArrayList<NameValuePair>();
  14. //[3.1.1]准备NameValuePair 实际上就是我们要提交的用户名和密码
  15. BasicNameValuePair nameValuePair = new BasicNameValuePair("username", name);
  16. BasicNameValuePair pwdValuePair = new BasicNameValuePair("password", pwd);
  17. lists.add(nameValuePair);
  18. lists.add(pwdValuePair);
  19. //[3.1.2]准备entity
  20. UrlEncodedFormEntity entity = new UrlEncodedFormEntity(lists);
  21. post.setEntity(entity);
  22. HttpResponse response = client.execute(post);
  23. int code = response.getStatusLine().getStatusCode();
  24. if (code == 200) {
  25. InputStream inputStream = response.getEntity()
  26. .getContent();
  27. String content = StreamTools.readStream(inputStream);
  28. showToast(content + "");
  29. }
  30. } catch (Exception e) {
  31. e.printStackTrace();
  32. }
  33. };
  34. }.start();


4开源项目方式把数据提交到服务器
asynchttpclient
get方式提交数据代码如下
  1. String name = et_username.getText().toString().trim();
  2. String pwd = et_password.getText().toString().trim();
  3. name = URLEncoder.encode(name, "utf-8");
  4. pwd = URLEncoder.encode(pwd, "utf-8");
  5. path = "http://192.168.11.73:8000/login/loginServlet?username=" + name
  6. + "&password=" + pwd;
  7. // [3]使用开源项目进行get请求
  8. // [3.1]创建asynchttpclient
  9. AsyncHttpClient client = new AsyncHttpClient();
  10. // [3.2]进行get请求
  11. client.get(path, new AsyncHttpResponseHandler() {
  12. //请求成功
  13. public void onSuccess(int statusCode, Header[] headers,
  14. byte[] responseBody) {
  15. Toast.makeText(getApplicationContext(), new String(responseBody,"gbk"), 1).show();
  16. }
  17. //请求失败
  18. public void onSuccess(int statusCode, Header[] headers,
  19. byte[] responseBody, Throwable error) {
  20. //Toast.makeText(getApplicationContext(), new String(responseBody,"gbk"), 1).show();
  21. }
  22. });

post方式提交数据代码如下
  1. String name = et_username.getText().toString().trim();
  2. String pwd = et_password.getText().toString().trim();
  3. String Path = "http://192.168.11.73:8080/login/LoginServlet";
  4. //[3.1]创建asyncHttpClient
  5. AsyncHttpClient client = new AsyncHttpClient();
  6. //[3.1.0]准备请求体的内容
  7. RequestParams params = new RequestParams();
  8. params.put("username", name);
  9. params.put("password", pwd);
  10. //[3.2]进行post请求
  11. client.post(Path, params, new AsyncHttpResponseHandler() {
  12. //请求成功
  13. public void onSuccess(int statusCode, Header[] headers,
  14. byte[] responseBody) {
  15. Toast.makeText(getApplicationContext(), new String(responseBody,"gbk"), 1).show();
  16. }
  17. //请求失败
  18. public void onSuccess(int statusCode, Header[] headers,
  19. byte[] responseBody, Throwable error) {
  20. //Toast.makeText(getApplicationContext(), new String(responseBody,"gbk"), 1).show();
  21. }
  22. });

总结:
    [1]    httpurlconnection
    [2]    httpclient    (了解 没人用)
    [3]    开源项目(asynchttpclient)

5javase多线程下载
    设置请求服务器文件的位置
    conn.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex);

为什么多线程能加速下载?
    [1]不是说线程开的越多下载越快
    [2]还受服务器带宽的影响
    [3]获得更多的cup资源

 每个线程下载的计算公式
  1. package com.phone.download;
  2. import java.io.InputStream;
  3. import java.io.RandomAccessFile;
  4. import java.net.HttpURLConnection;
  5. import java.net.URL;
  6. public class MutiDownLoad {
  7. // 定义下载路径
  8. static String path = "http://192.168.11.73:8080/feiq.exe";
  9. private static final int threadCount = 3;//线程数
  10. public static void main(String[] args){
  11. //[2]获取服务器文件的大小
  12. try
  13. {
  14. URL url = new URL(path);
  15. HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  16. conn.setRequestMethod("GET");
  17. conn.setReadTimeout(5000);
  18. int code = conn.getResponseCode();
  19. if(code == 200)
  20. {
  21. //[☆☆☆☆]一、获取服务器文件的大小 要计算每个线程下载的开始位置和结束位置
  22. int length = conn.getContentLength();
  23. //[☆☆☆☆]二、创建一个大小和服务器一模一样的文件 目的是提前把空间申请出来
  24. RandomAccessFile rafAccessFile = new RandomAccessFile("temp.exe", "rw");
  25. rafAccessFile.setLength(length);
  26. //算出每个线程下载的大小
  27. int blocksize = length / threadCount;
  28. //[☆☆☆☆]三、计算每个线程下载的开始位置和结束位置
  29. for(int i=0; i<threadCount; i++)
  30. {
  31. //每个线程下载的开始位置
  32. int startIndex = i*blocksize;
  33. //每个线程下载的结束位置
  34. int endIndex = (i+1)*blocksize - 1;
  35. if(i == threadCount - 1)
  36. {
  37. //说明是最后一个线程
  38. endIndex = length-1;
  39. }
  40. //[☆☆☆☆]四、开启线程去服务器下载文件
  41. DownLoadThread downLoadThread = new DownLoadThread(startIndex, endIndex, i);
  42. downLoadThread.start();
  43. }
  44. }
  45. }
  46. catch(Exception e)
  47. {
  48. e.printStackTrace();
  49. }
  50. }
  51. //定义线程去服务器下载文件
  52. private static class DownLoadThread extends Thread
  53. {
  54. //通过构造方法把每个线程下载的开始位置和结束位置传递过来
  55. private int startIndex;
  56. private int endIndex;
  57. private int threadId;
  58. public DownLoadThread(int startIndex,int endIndex,int threadId){
  59. this.startIndex = startIndex;
  60. this.endIndex = endIndex;
  61. this.threadId = threadId;
  62. }
  63. public void run() {
  64. //四、开启线程去服务器下载文件
  65. try {
  66. URL url = new URL(path);
  67. HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  68. conn.setRequestMethod("GET");
  69. conn.setConnectTimeout(5000);
  70. //设置一个请求头Range(作用告诉服务器每个线程下载的开始位置和结束位置)
  71. conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);
  72. int code = conn.getResponseCode();
  73. //200代表获取服务器资源全部成功
  74. //206请求部分资源成功
  75. //if(code == 200)
  76. if(code == 206)
  77. {
  78. //[6]创建随机读写文件对象
  79. RandomAccessFile rafAccessFile = new RandomAccessFile("temp.exe", "rw");
  80. //[6]每个线程要从自己的位置开始写
  81. rafAccessFile.seek(startIndex);
  82. InputStream inputStream = conn.getInputStream();
  83. //[7]把数据写到文件中
  84. int len = -1;
  85. byte[] Buffer = new byte[1024];
  86. while((len = inputStream.read(Buffer))!=-1)
  87. {
  88. rafAccessFile.write(Buffer,0,len);
  89. }
  90. rafAccessFile.close();
  91. }
  92. } catch (Exception e) {
  93. e.printStackTrace();
  94. }
  95. }
  96. }
  97. }

6断点续传实现
  1. package com.phone.download;
  2. import java.io.BufferedReader;
  3. import java.io.File;
  4. import java.io.FileInputStream;
  5. import java.io.FileOutputStream;
  6. import java.io.InputStream;
  7. import java.io.InputStreamReader;
  8. import java.io.RandomAccessFile;
  9. import java.net.HttpURLConnection;
  10. import java.net.URL;
  11. public class MutiDownLoad {
  12. // 定义下载路径
  13. static String path = "http://192.168.11.73:8080/feiq.exe";
  14. private static final int threadCount = 3;//线程数
  15. //代表当前正在运行的线程
  16. private static int runningThread;
  17. public static void main(String[] args){
  18. //[2]获取服务器文件的大小
  19. try
  20. {
  21. URL url = new URL(path);
  22. HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  23. conn.setRequestMethod("GET");
  24. conn.setReadTimeout(5000);
  25. int code = conn.getResponseCode();
  26. if(code == 200)
  27. {
  28. //[☆☆☆☆]一、获取服务器文件的大小 要计算每个线程下载的开始位置和结束位置
  29. int length = conn.getContentLength();
  30. //把线程的数量赋值给正在运行的线程
  31. runningThread = threadCount;
  32. //[☆☆☆☆]二、创建一个大小和服务器一模一样的文件 目的是提前把空间申请出来
  33. RandomAccessFile rafAccessFile = new RandomAccessFile("temp.exe", "rw");
  34. rafAccessFile.setLength(length);
  35. //算出每个线程下载的大小
  36. int blocksize = length / threadCount;
  37. //[☆☆☆☆]三、计算每个线程下载的开始位置和结束位置
  38. for(int i=0; i<threadCount; i++)
  39. {
  40. //每个线程下载的开始位置
  41. int startIndex = i*blocksize;
  42. //每个线程下载的结束位置
  43. int endIndex = (i+1)*blocksize - 1;
  44. if(i == threadCount - 1)
  45. {
  46. //说明是最后一个线程
  47. endIndex = length-1;
  48. }
  49. //[☆☆☆☆]四、开启线程去服务器下载文件
  50. DownLoadThread downLoadThread = new DownLoadThread(startIndex, endIndex, i);
  51. downLoadThread.start();
  52. }
  53. }
  54. }
  55. catch(Exception e)
  56. {
  57. e.printStackTrace();
  58. }
  59. }
  60. //定义线程去服务器下载文件
  61. private static class DownLoadThread extends Thread
  62. {
  63. //通过构造方法把每个线程下载的开始位置和结束位置传递过来
  64. private int startIndex;
  65. private int endIndex;
  66. private int threadId;
  67. public DownLoadThread(int startIndex,int endIndex,int threadId){
  68. this.startIndex = startIndex;
  69. this.endIndex = endIndex;
  70. this.threadId = threadId;
  71. }
  72. public void run() {
  73. //四、开启线程去服务器下载文件
  74. try {
  75. URL url = new URL(path);
  76. HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  77. conn.setRequestMethod("GET");
  78. conn.setConnectTimeout(5000);
  79. //如果中间断过 继续上次的位置继续下载 从文件中读取上次下载的位置
  80. File file = new File(getFilename(path) + threadId + ".txt");
  81. if(file.exists() && file.length()>0)
  82. {
  83. FileInputStream fis = new FileInputStream(file);
  84. BufferedReader bufr = new BufferedReader(new InputStreamReader(fis));
  85. //读取出的内容就是上一次下载的位置
  86. String lastPositionn = bufr.readLine();
  87. int lastPosition = Integer.parseInt(lastPositionn);
  88. //改变startIndex位置
  89. startIndex = lastPosition;
  90. fis.close();
  91. }
  92. //设置一个请求头Range(作用告诉服务器每个线程下载的开始位置和结束位置)
  93. conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);
  94. int code = conn.getResponseCode();
  95. //200代表获取服务器资源全部成功
  96. //206请求部分资源成功
  97. //if(code == 200)
  98. if(code == 206)
  99. {
  100. //[6]创建随机读写文件对象
  101. RandomAccessFile rafAccessFile = new RandomAccessFile(getFilename(path), "rw");
  102. //[6]每个线程要从自己的位置开始写
  103. rafAccessFile.seek(startIndex);
  104. InputStream inputStream = conn.getInputStream();
  105. //[7]把数据写到文件中
  106. int len = -1;
  107. byte[] Buffer = new byte[1024*1024];
  108. //代表当前线程下载的大小
  109. int total = 0;
  110. while((len = inputStream.read(Buffer))!=-1)
  111. {
  112. rafAccessFile.write(Buffer,0,len);
  113. total += len;
  114. //[8]实现断点续传 就是把当前线程下载的位置给存起来 下次再下载的时候按照上次下载的位置继续下载就可以了
  115. //存到一个普通的.txt文本中
  116. int currentThreadPosition = startIndex + total;
  117. //[9]用来存当前线程下载的位置
  118. RandomAccessFile raff = new RandomAccessFile(getFilename(path) + threadId + ".txt", "rwd");
  119. raff.write(String.valueOf(currentThreadPosition).getBytes());
  120. raff.close();
  1. /*
  2. * 用这种方法有时候可能存不成功,因为硬盘有个缓存区
  3. File file = new File(threadId + ".txt");
  4. FileOutputStream fos = new FileOutputStream(file);
  5. fos.write(String.valueOf(currentThreadPosition).getBytes());
  6. fos.close();
  7. */
  8. }
  9. rafAccessFile.close();
  10. synchronized(DownLoadThread.class){
  11. runningThread--;
  12. if(runningThread == 0)
  13. {
  14. //说明所有的线程都执行完毕了 就把.txt文件删除
  15. for(int i = 0; i < threadCount; i++)
  16. {
  17. File delFile = new File(getFilename(path) + i + ".txt");
  18. delFile.delete();
  19. }
  20. }
  21. }
  22. }
  23. } catch (Exception e) {
  24. e.printStackTrace();
  25. }
  26. }
  27. }
  28. /*
  29. * 获取文件的名字
  30. */
  31. public static String getFilename(String path){
  32. int start = path.lastIndexOf("/");
  33. return path.substring(start);
  34. }
  35. }

7断点续传逻辑移植到Android上
MainActivity.java
  1. package com.phone.download;
  2. import java.io.BufferedReader;
  3. import java.io.File;
  4. import java.io.FileInputStream;
  5. import java.io.InputStream;
  6. import java.io.InputStreamReader;
  7. import java.io.RandomAccessFile;
  8. import java.net.HttpURLConnection;
  9. import java.net.URL;
  10. import java.util.ArrayList;
  11. import java.util.List;
  12. import android.os.Bundle;
  13. import android.os.Environment;
  14. import android.support.v7.app.ActionBarActivity;
  15. import android.view.View;
  16. import android.widget.EditText;
  17. import android.widget.LinearLayout;
  18. import android.widget.ProgressBar;
  19. public class MainActivity extends ActionBarActivity {
  20. private EditText et_path;
  21. private String path;
  22. private EditText et_threadCount;
  23. private LinearLayout ll_pb_layout;
  24. // 代表当前正在运行的线程
  25. private static int runningThread;
  26. //用来存进度条的引用
  27. private List<ProgressBar> pbLists;
  28. @Override
  29. protected void onCreate(Bundle savedInstanceState) {
  30. super.onCreate(savedInstanceState);
  31. setContentView(R.layout.activity_main);
  32. // [1]获取控件
  33. et_path = (EditText) findViewById(R.id.et_path);
  34. et_threadCount = (EditText) findViewById(R.id.et_threadCount);
  35. ll_pb_layout = (LinearLayout) findViewById(R.id.ll_pb);
  36. pbLists = new ArrayList<ProgressBar>();
  37. }
  38. // 点击按钮实现下载的逻辑
  39. public void click(View v) {
  40. // 获取下载的路径
  41. path = et_path.getText().toString().trim();
  42. // [3]获取线程的数量
  43. String threadCountt = et_threadCount.getText().toString().trim();
  44. // [3.0]先移除进度条再添加
  45. ll_pb_layout.removeAllViews();
  46. final int threadCount = Integer.parseInt(threadCountt);
  47. pbLists.clear();
  48. for (int i = 0; i < threadCount; i++) {
  49. // [3.1]把我定义的item布局转换成一个View对象
  50. ProgressBar child = (ProgressBar) View.inflate(getApplicationContext(), R.layout.item,
  51. null);
  52. //把child添加到集合中
  53. pbLists.add(child);
  54. // [4]动态的添加进度条
  55. ll_pb_layout.addView(child);
  56. }
  57. // [5]开始移植
  58. new Thread() {
  59. public void run() {
  60. // [2]获取服务器文件的大小
  61. try {
  62. URL url = new URL(path);
  63. HttpURLConnection conn = (HttpURLConnection) url
  64. .openConnection();
  65. conn.setRequestMethod("GET");
  66. conn.setReadTimeout(5000);
  67. int code = conn.getResponseCode();
  68. if (code == 200) {
  69. // [☆☆☆☆]一、获取服务器文件的大小 要计算每个线程下载的开始位置和结束位置
  70. int length = conn.getContentLength();
  71. // 把线程的数量赋值给正在运行的线程
  72. runningThread = threadCount;
  73. // [☆☆☆☆]二、创建一个大小和服务器一模一样的文件 目的是提前把空间申请出来
  74. RandomAccessFile rafAccessFile = new RandomAccessFile(
  75. "temp.exe", "rw");
  76. rafAccessFile.setLength(length);
  77. // 算出每个线程下载的大小
  78. int blocksize = length / threadCount;
  79. // [☆☆☆☆]三、计算每个线程下载的开始位置和结束位置
  80. for (int i = 0; i < threadCount; i++) {
  81. // 每个线程下载的开始位置
  82. int startIndex = i * blocksize;
  83. // 每个线程下载的结束位置
  84. int endIndex = (i + 1) * blocksize - 1;
  85. if (i == threadCount - 1) {
  86. // 说明是最后一个线程
  87. endIndex = length - 1;
  88. }
  89. // [☆☆☆☆]四、开启线程去服务器下载文件
  90. DownLoadThread downLoadThread = new DownLoadThread(
  91. startIndex, endIndex, i);
  92. downLoadThread.start();
  93. }
  94. }
  95. } catch (Exception e) {
  96. e.printStackTrace();
  97. }
  98. };
  99. }.start();
  100. }
  101. // 定义线程去服务器下载文件
  102. private class DownLoadThread extends Thread {
  103. // 通过构造方法把每个线程下载的开始位置和结束位置传递过来
  104. private int startIndex;
  105. private int endIndex;
  106. private int threadId;
  107. //代表当前线程下载的最大值
  108. private int PbMaxSize;
  109. //如果中断过 获取上次下载的位置
  110. private int PbLastPosition;
  111. public DownLoadThread(int startIndex, int endIndex, int threadId) {
  112. this.startIndex = startIndex;
  113. this.endIndex = endIndex;
  114. this.threadId = threadId;
  115. }
  1. public void run() {
  2. // 四、开启线程去服务器下载文件
  3. try {
  4. //计算当前进度条的最大值
  5. PbMaxSize = endIndex - startIndex;
  6. URL url = new URL(path);
  7. HttpURLConnection conn = (HttpURLConnection) url
  8. .openConnection();
  9. conn.setRequestMethod("GET");
  10. conn.setConnectTimeout(5000);
  11. // 如果中间断过 继续上次的位置继续下载 从文件中读取上次下载的位置
  12. File file = new File(getFilename(path) + threadId + ".txt");
  13. if (file.exists() && file.length() > 0) {
  14. FileInputStream fis = new FileInputStream(file);
  15. BufferedReader bufr = new BufferedReader(
  16. new InputStreamReader(fis));
  17. // 读取出的内容就是上一次下载的位置
  18. String lastPositionn = bufr.readLine();
  19. int lastPosition = Integer.parseInt(lastPositionn);
  20. //赋值给我们定义的进度条进度位置
  21. PbLastPosition = lastPosition - startIndex;
  22. // 改变startIndex位置
  23. startIndex = lastPosition;
  24. fis.close();
  25. }
  26. // 设置一个请求头Range(作用告诉服务器每个线程下载的开始位置和结束位置)
  27. conn.setRequestProperty("Range", "bytes=" + startIndex + "-"
  28. + endIndex);
  29. int code = conn.getResponseCode();
  30. // 200代表获取服务器资源全部成功
  31. // 206请求部分资源成功
  32. // if(code == 200)
  33. if (code == 206) {
  34. // [6]创建随机读写文件对象
  35. RandomAccessFile rafAccessFile = new RandomAccessFile(
  36. getFilename(path), "rw");
  37. // [6]每个线程要从自己的位置开始写
  38. rafAccessFile.seek(startIndex);
  39. InputStream inputStream = conn.getInputStream();
  40. // [7]把数据写到文件中
  41. int len = -1;
  42. byte[] Buffer = new byte[1024 * 1024];
  43. // 代表当前线程下载的大小
  44. int total = 0;
  45. while ((len = inputStream.read(Buffer)) != -1) {
  46. rafAccessFile.write(Buffer, 0, len);
  47. total += len;
  48. // [8]实现断点续传 就是把当前线程下载的位置给存起来 下次再下载的时候按照上次下载的位置继续下载就可以了
  49. // 存到一个普通的.txt文本中
  50. int currentThreadPosition = startIndex + total;
  51. // [9]用来存当前线程下载的位置
  52. RandomAccessFile raff = new RandomAccessFile(
  53. getFilename(path) + threadId + ".txt", "rwd");
  54. raff.write(String.valueOf(currentThreadPosition)
  55. .getBytes());
  56. raff.close();
  57. /*
  58. * 用这种方法有时候可能存不成功,因为硬盘有个缓存区 File file = new
  59. * File(threadId + ".txt"); FileOutputStream fos = new
  60. * FileOutputStream(file);
  61. * fos.write(String.valueOf(currentThreadPosition
  62. * ).getBytes()); fos.close();
  63. */
  64. //设置当前进度条的最大值和当前进度
  65. pbLists.get(threadId).setMax(PbMaxSize);
  66. pbLists.get(threadId).setProgress(PbLastPosition+total);
  67. }
  68. rafAccessFile.close();
  69. // 获取线程的数量
  70. String threadCountt = et_threadCount.getText().toString().trim();
  71. final int threadCount = Integer.parseInt(threadCountt);
  72. synchronized (DownLoadThread.class) {
  73. runningThread--;
  74. if (runningThread == 0) {
  75. // 说明所有的线程都执行完毕了 就把.txt文件删除
  76. for (int i = 0; i < threadCount; i++) {
  77. File delFile = new File(getFilename(path) + i
  78. + ".txt");
  79. delFile.delete();
  80. }
  81. }
  82. }
  83. }
  84. } catch (Exception e) {
  85. e.printStackTrace();
  86. }
  87. }
  88. }
  89. /*
  90. * 获取文件的名字
  91. */
  92. public static String getFilename(String path){
  93. int start = path.lastIndexOf("/");
  94. String substring = path.substring(start);
  95. String fileName = Environment.getExternalStorageDirectory().getPath() + "/" + substring;
  96. return fileName;
  97. }
  98. }
activity_main.xml
  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. android:orientation="vertical"
  6. tools:context="com.phone.download.MainActivity" >
  7. <EditText
  8. android:id="@+id/et_path"
  9. android:layout_width="match_parent"
  10. android:layout_height="wrap_content"
  11. android:hint="请输入下载路径"/>
  12. <EditText
  13. android:id="@+id/et_threadCount"
  14. android:layout_width="match_parent"
  15. android:layout_height="wrap_content"
  16. android:hint="请输入线程的数量"/>
  17. <Button
  18. android:onClick="click"
  19. android:layout_width="wrap_content"
  20. android:layout_height="wrap_content"
  21. android:hint="下载"/>
  22. <LinearLayout
  23. android:id="@+id/ll_pb"
  24. android:layout_width="match_parent"
  25. android:layout_height="match_parent"
  26. android:orientation="vertical">
  27. </LinearLayout>
  28. </LinearLayout>

item.xml
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <ProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:id="@+id/progressBar1"
  4. style="?android:attr/progressBarStyleHorizontal"
  5. android:layout_width="match_parent"
  6. android:layout_height="wrap_content" />

8开源项目实现多线程下载(xutils)
  1. package com.phone.opensourcedownload;
  2. import java.io.File;
  3. import android.app.Activity;
  4. import android.os.Bundle;
  5. import android.view.View;
  6. import android.widget.EditText;
  7. import android.widget.ProgressBar;
  8. import android.widget.Toast;
  9. import com.lidroid.xutils.HttpUtils;
  10. import com.lidroid.xutils.exception.HttpException;
  11. import com.lidroid.xutils.http.ResponseInfo;
  12. import com.lidroid.xutils.http.callback.RequestCallBack;
  13. public class MainActivity extends Activity {
  14. private EditText et_path;
  15. private ProgressBar pb;
  16. @Override
  17. protected void onCreate(Bundle savedInstanceState) {
  18. super.onCreate(savedInstanceState);
  19. setContentView(R.layout.activity_main);
  20. //[0]获取控件
  21. et_path = (EditText) findViewById(R.id.et_path);
  22. pb = (ProgressBar) findViewById(R.id.progressBar1);
  23. }
  24. //点击按钮实现断点续传下载逻辑
  25. public void click(View v){
  26. //[1]获取下载路径
  27. String path = et_path.getText().toString().trim();
  28. //[2]创建httputils对象HttpUtils http = new HttpUtils();
  29. HttpUtils http = new HttpUtils();
  30. //[3]实现断点下载
  31. /**
  32. * path:路径
  33. * target:下载文件的路径,即下载到哪
  34. * autoResume:是否支持断点续传的逻辑
  35. *
  36. */
  37. http.download(path, "/mnt/sdcard/haha.exe", true, new RequestCallBack<File>() {
  38. //下载成功
  39. @Override
  40. public void onSuccess(ResponseInfo<File> responseInfo) {
  41. Toast.makeText(getApplicationContext(), "下载成功", 1);
  42. }
  43. @Override
  44. /**
  45. * total:代表总进度
  46. * current:代表当前进度
  47. */
  48. public void onLoading(long total, long current, boolean isUploading) {
  49. pb.setMax((int)total);
  50. pb.setProgress((int)current);
  51. }
  52. //下载失败的回调函数
  53. @Override
  54. public void onFailure(HttpException error, String msg) {
  55. }
  56. });
  57. }
  58. }
  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. android:orientation="vertical"
  6. tools:context="com.phone.download.MainActivity" >
  7. <EditText
  8. android:id="@+id/et_path"
  9. android:layout_width="match_parent"
  10. android:layout_height="wrap_content"
  11. android:hint="请输入下载路径" />
  12. <Button
  13. android:layout_width="wrap_content"
  14. android:layout_height="wrap_content"
  15. android:hint="下载"
  16. android:onClick="click" />
  17. <ProgressBar
  18. android:id="@+id/progressBar1"
  19. style="?android:attr/progressBarStyleHorizontal"
  20. android:layout_width="match_parent"
  21. android:layout_height="wrap_content" />
  22. </LinearLayout>
9今日总结
   [1]httpurlConnection 用于发送或者接收数据(掌握)
        get    组拼url地址
        post    要自己组拼请求正文    请求头信息
   [2]httpClient(了解)
   [3]asyncHttpClient(掌握)
   [4]多线程下载
        1.获取文件大小
        2.在客户端创建一个大小和服务器一模一样的文件(为了提前申请控件)
        3.算出每个线程下载的开始位置和结束位置
                需要考虑    最后一个线程    文件长度-1
        4.开启一个线程去实现下载逻辑    httpurlConnection
                设置Range    还要注意服务器返回的状态码不是200而是206
        5.断点的逻辑    就是把当前线程下载的位置给保存起来    RandomAccessFile    直接同步到底层设备
        6.移植到Android    算出进度条的进度    集合存进度条的引用
        7.与进度有关的都可以在子线程直接更新UI




只言片语任我说,提笔句句无需忖。落笔不知寄何人,唯有邀友共斟酌。
原文地址:https://www.cnblogs.com/phonecom/p/e1e6e17f89d0c4d5e321744a9a169aa8.html