1. Android网络前提

  1. 必须在子线程中执行。
  2. 需要申请INTERNET权限。

    2. Android网络请求基础

    2.1 HttpUrlConnection基础类

    get请求

    1. // final String PATH = "接口地址";
    2. private void doHttpUrlGet(final String uName, final String pwd) {
    3. new Thread() {
    4. @Override
    5. public void run() {
    6. try {
    7. URL url = new URL(PATH + "?CompCode=" + COMP_CODE + "&UserName=" + uName + "&Pwd=" + pwd);
    8. HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    9. connection.setRequestMethod("GET");
    10. connection.setConnectTimeout(10000);
    11. int code = connection.getResponseCode();
    12. if (code == 200) {
    13. InputStream stream = connection.getInputStream();
    14. String content = Utils.getStringFromStream(stream);
    15. // showToast("请求成功!");
    16. Log.i("I",content);
    17. } else {
    18. // setTextCount("请求错误!错误码:" + code);
    19. }
    20. } catch (Exception e) {
    21. e.printStackTrace();
    22. // setTextCount("请求错误!异常:" + e.getMessage());
    23. }
    24. }
    25. }.start();
    26. }

POST请求

  1. private void doHttpUrlPost(final String uName, final String pwd) {
  2. new Thread() {
  3. @Override
  4. public void run() {
  5. try {
  6. String params = "CompCode=" + COMP_CODE + "&UserName=" + URLEncoder.encode(uName, "utf-8") + "&Pwd=" + URLEncoder.encode(pwd, "utf-8");
  7. URL url = new URL(PATH);
  8. HttpURLConnection connection = (HttpURLConnection) url.openConnection();
  9. connection.setRequestMethod("POST");
  10. connection.setConnectTimeout(10000);
  11. //设置跟post请求相关的请求头
  12. //告诉服务器 要传递的数据类型
  13. connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
  14. //告诉服务器,传递的数据长度
  15. connection.setRequestProperty("Content-Length", String.valueOf(params.length()));
  16. //打开输出流
  17. connection.setDoOutput(true);
  18. //通过流把请求体写到服务端
  19. connection.getOutputStream().write(params.getBytes());
  20. int code = connection.getResponseCode();
  21. if (code == 200) {
  22. InputStream stream = connection.getInputStream();
  23. String count = Utils.getStringFromStream(stream);
  24. showToast("请求成功!");
  25. setTextCount(count);
  26. } else {
  27. setTextCount("请求错误!错误码:" + code);
  28. }
  29. } catch (Exception e) {
  30. e.printStackTrace();
  31. setTextCount("请求错误!异常:" + e.getMessage());
  32. }
  33. }
  34. }.start();
  35. }

2.2. android-async-http

第三方开源项目 https://github.com/android-async-http/android-async-http

GET请求

  1. private void doAsyncHttpClientGet(String uName, String pwd) {
  2. AsyncHttpClient client = new AsyncHttpClient();
  3. RequestParams params = new RequestParams();
  4. params.add("CompCode", COMP_CODE);
  5. params.add("UserName", uName);
  6. params.add("Pwd", pwd);
  7. client.get(PATH, params, new MyAsyncHttpResponseHandler());
  8. }

POST请求

  1. private void doAsyncHttpClientPost(String uName, String pwd) {
  2. AsyncHttpClient client = new AsyncHttpClient();
  3. RequestParams params = new RequestParams();
  4. params.add("CompCode", COMP_CODE);
  5. params.add("UserName", uName);
  6. params.add("Pwd", pwd);
  7. client.post(PATH, params, new MyAsyncHttpResponseHandler());
  8. }
  9. class MyAsyncHttpResponseHandler extends AsyncHttpResponseHandler {
  10. @Override
  11. public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
  12. if (statusCode == 200) {
  13. try {
  14. String count = new String(responseBody, "utf-8");
  15. showToast("请求成功!");
  16. setTextCount(count);
  17. } catch (UnsupportedEncodingException e) {
  18. e.printStackTrace();
  19. setTextCount("请求失败:异常" + e.getMessage());
  20. }
  21. } else {
  22. setTextCount("请求失败:错误码=" + statusCode);
  23. }
  24. }
  25. @Override
  26. public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
  27. setTextCount("请求失败:异常" + error.getMessage());
  28. }
  29. }

2.3. OkHttp

第三方开源项目 https://github.com/square/okhttp

GET请求

  1. private void doOkHttpGet(String uName, String pwd) {
  2. OkHttpClient client = new OkHttpClient();
  3. try {
  4. URL url = new URL(PATH + "?CompCode=" + COMP_CODE + "&UserName=" + uName + "&Pwd=" + pwd);
  5. Request request = new Request.Builder().url(url).build();
  6. client.newCall(request).enqueue(new MyCallback());
  7. } catch (MalformedURLException e) {
  8. e.printStackTrace();
  9. }
  10. }

POST请求

  1. private void doOkHttpPost(String uName, String pwd) {
  2. OkHttpClient client = new OkHttpClient();
  3. FormBody body = new FormBody.Builder()
  4. .add("CompCode", COMP_CODE)
  5. .add("UserName", uName)
  6. .add("Pwd", pwd)
  7. .build();
  8. Request request = new Request.Builder().url(PATH).post(body).build();
  9. client.newCall(request).enqueue(new MyCallback());
  10. }
  11. class MyCallback implements Callback {
  12. @Override
  13. public void onFailure(@NotNull Call call, @NotNull IOException e) {
  14. setTextCount("请求失败:异常" + e.getMessage());
  15. }
  16. @Override
  17. public void onResponse(@NotNull Call call, @NotNull Response response) {
  18. if (response.code() == 200) {
  19. try {
  20. String count = response.body().string();
  21. showToast("请求成功!");
  22. setTextCount(count);
  23. } catch (IOException e) {
  24. e.printStackTrace();
  25. setTextCount("请求失败:异常" + e.getMessage());
  26. }
  27. } else {
  28. setTextCount("请求失败:错误码=" + response.code());
  29. }
  30. }
  31. }

3. 文件下载

3.1 多线程下载

多线程下载可以突破服务器对单个线程的速度限制。

原理

  1. 服务器支持 Range请求头,其响应码为206.
  2. 客户端支持随机读取文件任意位置指针,并存储。

    一般步骤

  3. 获取下载文件的总长度 MAX。

  4. 在本地创建一个一样大小的文件,并根据线程总数来计算分配给每个线程下载的取值范围。
  5. 开启多个线程,每个线程从计算好的起始位置下载到终止位置。
  6. 线程下载的同时保存对应内容。
  7. 所有线程结束时,文件下载完成。
  1. public void onClick(View v) {
  2. final String path = "文件网络路径";
  3. final int threadCount = 4;// 线程总数
  4. new Thread() {
  5. @Override
  6. public void run() {
  7. try {
  8. URL url = new URL(path);
  9. HttpURLConnection connection = (HttpURLConnection) url.openConnection();
  10. connection.setRequestMethod("GET");
  11. connection.setConnectTimeout(10000);
  12. int code = connection.getResponseCode();
  13. if (code == 200) {
  14. // 获取文件总大小
  15. int length = connection.getContentLength();
  16. // 创建一个相同大小的本地文件
  17. RandomAccessFile file = new RandomAccessFile(getFileName(path), "rw");
  18. file.setLength(length);
  19. blockSize = length / threadCount;
  20. for (int i = 0; i < threadCount; i++) {
  21. int startIndex = i * blockSize;
  22. int endIndex = (i + 1) * blockSize - 1;
  23. if (i == threadCount - 1) {
  24. endIndex = length - 1;
  25. }
  26. new DownLoadThred(startIndex, endIndex, i, path).start();
  27. }
  28. }
  29. } catch (Exception e) {
  30. e.printStackTrace();
  31. }
  32. }
  33. }.start();
  34. }
  35. private class DownLoadThred extends Thread {
  36. private int startIndex;
  37. private int endIndex;
  38. private int threadIndex;
  39. private String path;
  40. public DownLoadThred(int startIndex, int endIndex, int threadIndex, String path) {
  41. this.startIndex = startIndex;
  42. this.endIndex = endIndex;
  43. this.threadIndex = threadIndex;
  44. this.path = path;
  45. }
  46. @Override
  47. public void run() {
  48. try {
  49. URL url = new URL(path);
  50. HttpURLConnection connection = (HttpURLConnection) url.openConnection();
  51. connection.setRequestMethod("GET");
  52. connection.setConnectTimeout(10000);
  53. // 设置Range头,用于计算好的开始索引和技术索引到服务器请求数据
  54. connection.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);
  55. if (connection.getResponseCode() == 206) {
  56. //System.out.println("线程 " + threadIndex + "开始下载" + startIndex);
  57. InputStream inputStream = connection.getInputStream();
  58. int len = -1;
  59. byte[] buffer = new byte[1024];
  60. RandomAccessFile file = new RandomAccessFile(getFileName(path), "rw");
  61. // 移动文件指针
  62. file.seek(startIndex);
  63. int count = 0;
  64. while ((len = inputStream.read(buffer)) != -1) {
  65. file.write(buffer, 0, len);
  66. }
  67. file.close();
  68. inputStream.close();
  69. // System.out.println("线程 " + threadIndex + " 结束下载");
  70. }
  71. } catch (Exception e) {
  72. e.printStackTrace();
  73. }
  74. }
  75. }
  76. public String getFileName(String path) {
  77. String name = getCacheDir() + path.substring(path.lastIndexOf("/"));
  78. return name;
  79. }

3.2 断点下载

原理

通过一个本地文件,记录每个线程下载了多少数据,一旦下载中断,下次再下载的时候,读取日志内容,重新从记录的位置开始请求数据并写入文件

实现

多线程+断点下载

  1. @Override
  2. public void onClick(View v) {
  3. if (v.getId() == R.id.btn_down) {
  4. final String path = etUrl.getText().toString().trim();
  5. final int threadCount = Integer.valueOf(etCount.getText().toString().trim());
  6. new Thread() {
  7. @Override
  8. public void run() {
  9. try {
  10. URL url = new URL(path);
  11. HttpURLConnection connection = (HttpURLConnection) url.openConnection();
  12. connection.setRequestMethod("GET");
  13. connection.setConnectTimeout(10000);
  14. int code = connection.getResponseCode();
  15. if (code == 200) {
  16. int length = connection.getContentLength();
  17. RandomAccessFile file = new RandomAccessFile(getFileName(path), "rw");
  18. file.setLength(length);
  19. blockSize = length / threadCount;
  20. for (int i = 0; i < threadCount; i++) {
  21. int startIndex = i * blockSize;
  22. int endIndex = (i + 1) * blockSize - 1;
  23. if (i == threadCount - 1) {
  24. endIndex = length - 1;
  25. }
  26. new DownLoadThred(startIndex, endIndex, i, path).start();
  27. }
  28. }
  29. } catch (Exception e) {
  30. e.printStackTrace();
  31. }
  32. }
  33. }.start();
  34. }
  35. }
  36. private class DownLoadThred extends Thread {
  37. private int startIndex;
  38. private int endIndex;
  39. private int threadIndex;
  40. private String path;
  41. public DownLoadThred(int startIndex, int endIndex, int threadIndex, String path) {
  42. this.startIndex = startIndex;
  43. this.endIndex = endIndex;
  44. this.threadIndex = threadIndex;
  45. this.path = path;
  46. }
  47. @Override
  48. public void run() {
  49. try {
  50. // 读取出记录下来的位置
  51. File temp = new File(getFileName(path) + threadIndex + ".log");
  52. if (temp != null && temp.length() > 0) {
  53. // 说明日志文件有内容
  54. FileInputStream fis = new FileInputStream(temp);
  55. BufferedReader reader = new BufferedReader(new InputStreamReader(fis));
  56. String result = reader.readLine();
  57. // 读出记录下来的位置更新下载请求数据的起始位置
  58. startIndex = Integer.parseInt(result);
  59. }
  60. URL url = new URL(path);
  61. HttpURLConnection connection = (HttpURLConnection) url.openConnection();
  62. connection.setRequestMethod("GET");
  63. connection.setConnectTimeout(10000);
  64. // 设置Range头,用于计算好的开始索引和技术索引到服务器请求数据
  65. connection.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);
  66. if (connection.getResponseCode() == 206) {
  67. // System.out.println("线程 " + threadIndex + "开始下载" + startIndex);
  68. InputStream inputStream = connection.getInputStream();
  69. int len = -1;
  70. byte[] buffer = new byte[1024];
  71. RandomAccessFile file = new RandomAccessFile(getFileName(path), "rw");
  72. file.seek(startIndex);
  73. int count = 0;
  74. while ((len = inputStream.read(buffer)) != -1) {
  75. file.write(buffer, 0, len);
  76. count = count + len;
  77. int position = count + startIndex;
  78. // 断点下载存储位置
  79. RandomAccessFile tempFile = new RandomAccessFile(getFileName(path) + threadIndex + ".log", "rwd");
  80. tempFile.write(String.valueOf(position).getBytes());
  81. // 一定要加在,因为android中,所有的IO操作最终都是需要句柄来操作文件实现的。
  82. // 句柄数量对于每一个app都有一个上限,常见的是1024个。一旦某个app同时使用的句柄数超过了这个限制,就会看到这个异常:too many open files。
  83. tempFile.close();
  84. }
  85. file.close();
  86. inputStream.close();
  87. // 下载完成后,删除日志
  88. if (temp != null) {
  89. temp.delete();
  90. }
  91. // System.out.println("线程 " + threadIndex + " 结束下载");
  92. }
  93. } catch (Exception e) {
  94. e.printStackTrace();
  95. }
  96. }
  97. }
  98. public String getFileName(String path) {
  99. String name = getCacheDir() + path.substring(path.lastIndexOf("/"));
  100. return name;
  101. }

3.3 xUtils

第三方库 https://github.com/wyouflf/xUtils3

  1. @Override
  2. protected void onCreate(Bundle savedInstanceState) {
  3. super.onCreate(savedInstanceState);
  4. setContentView(R.layout.activity_multi_down_file);
  5. // 初始化
  6. x.Ext.init(getApplication());
  7. }
  8. protected void downFile(String fileUrl,String saveFilePath) {
  9. // 设置下载地址
  10. RequestParams requestParams = new RequestParams(fileUrl);
  11. // 设置保存地址
  12. requestParams.setSaveFilePath(saveFilePath);
  13. x.http().get(requestParams, new Callback.ProgressCallback<File>() {
  14. @Override
  15. public void onSuccess(File result) {
  16. Log.i("xUtils3", "下载成功");
  17. showToast("下载成功!");
  18. }
  19. @Override
  20. public void onError(Throwable ex, boolean isOnCallback) {
  21. Log.i("xUtils3", "下载失败");
  22. showToast("下载失败!");
  23. }
  24. @Override
  25. public void onCancelled(CancelledException cex) {
  26. Log.i("xUtils3", "下载被取消");
  27. showToast("下载被取消");
  28. }
  29. @Override
  30. public void onFinished() {
  31. Log.i("xUtils3", "下载结束");
  32. showToast("下载结束!");
  33. }
  34. @Override
  35. public void onWaiting() {
  36. // 网络请求开始的时候调用
  37. Log.i("xUtils3", "等待下载");
  38. }
  39. @Override
  40. public void onStarted() {
  41. // 下载的时候不断回调
  42. Log.i("xUtils3", "onStarted()");
  43. }
  44. @Override
  45. public void onLoading(long total, long current, boolean isDownloading) {
  46. // 当前的下载进度和文件总大小
  47. Log.i("xUtils3", "下载进度......" + current + "/" + total);
  48. }
  49. });
  50. }