Skip to content

Latest commit

 

History

History
1425 lines (1425 loc) · 55.2 KB

net.md

File metadata and controls

1425 lines (1425 loc) · 55.2 KB

上海黑马程序员——只要学不死,就往死里学,冲击年薪20 万! 1 第5 天Android 基础 第五章网络编程(下)............................................................................................................................................ 2 1.1 前言............................................................................................................................................................... 2 1.1.1 需求说明............................................................................................................................................ 2 1.1.2 服务器搭建........................................................................................................................................ 2 1.1.3 编写布局............................................................................................................................................ 5 1.1.4 添加权限............................................................................................................................................ 6 1.2 使用HttpURLConnection 提交数据........................................................................................................... 6 1.2.1 代码.....................................................................................................................................................6 1.2.2 总结...................................................................................................................................................11 1.3 使用HttpClient 提交数据.......................................................................................................................... 11 1.3.1 get 方式提交......................................................................................................................................11 1.3.2 post 方式提交....................................................................................................................................13 1.3.3 总结.................................................................................................................................................. 14 1.4 使用AsyncHttpClient 框架提交数据........................................................................................................16 1.4.1 get 方式提交..................................................................................................................................... 17 1.4.2 post 方式提交....................................................................................................................................18 1.4.3 总结.................................................................................................................................................. 19 1.5 JavaSE 实现多线程下载............................................................................................................................. 21 1.5.1 多线程下载原理.............................................................................................................................. 21 1.5.2 代码实现.......................................................................................................................................... 21 1.6 Android 实现多线程下载............................................................................................................................25 1.6.1 ProgressBar 的使用...........................................................................................................................26 1.6.2 编写布局.......................................................................................................................................... 26 1.6.3 编写代码.......................................................................................................................................... 28 1.6.4 添加权限.......................................................................................................................................... 33 1.7 xUtils 实现多线程下载............................................................................................................................... 33 1.7.1 xUtils 简介.........................................................................................................................................33 1.7.2 xUtils 之HttpUtils 的使用............................................................................................................... 34 上海黑马程序员——只要学不死,就往死里学,冲击年薪20 万! 2 第五章网络编程(下)  使用HttpURLConnection 提交数据  使用HttpClient 提交数据  使用AsyncHttpClient 框架提交数据  Android 实现多线程下载  使用xUtils 框架实现多线程下载 1.1 前言 移动互联网时代哪个app 不需要跟服务器进行交互呢?Android 给服务器提交数据的方式都有哪些 呢?这正是本文前3 节讨论的话题,每一节介绍一种提交数据的方式,但是Android 提交数据的方式绝非 仅仅这三种,这里给出的只是最基础的3 中方式。将这些基础的方式学会了,其他再高级的方式对我们来 说也不过是小菜一碟了。 本文的1.1、1.2、1.3 三节中使用的需求和布局是一模一样的,甚至1.2 和1.3 节的工程就是直接从1.1 节中的工程拷贝过来的,唯一不同的就是使用提交数据的框架(类)不同。因此这里一次性将需求给出。 1.1.1 需求说明 如图1-1 所示,界面整体采用垂直的线性布局,前两行为两个EditText,分别代表用户名和密码。第 三、四两行为两个Button,前者点击后采用get 方式提交数据,后者点击后采用post 方式提交数据。数 据提交成功后,服务器会有返回值,并将返回值用Toast 显示出来。 1.1.2 服务器搭建 服务端采用Servlet 编写,名为LoginServlet,并使用Tomcat 作为其服务器。LoginServlet.java 源码见 【文件1-1】,其中黄色高亮部分为核心代码。该Servlet 在web.xml 中的配置见【文件1-2】。因为服务 器不是本文的重点,因此这里只简单介绍。 【文件1-1】LoginServlet.java

  1. package com.itheima.servlet;
  2. import java.io.IOException;
  3. import javax.servlet.ServletException; 上海黑马程序员——只要学不死,就往死里学,冲击年薪20 万! 3
  4. import javax.servlet.http.HttpServlet;
  5. import javax.servlet.http.HttpServletRequest;
  6. import javax.servlet.http.HttpServletResponse;
  7. public class LoginServlet extends HttpServlet {
  8. /**
    • Constructor of the object.
  9. */
  10. public LoginServlet() {
  11. super();
  12. }
  13. /**
    • Destruction of the servlet.
  14. */
  15. public void destroy() {
  16. super.destroy(); // Just puts "destroy" string in log
  17. // Put your code here
  18. }
  19. /**
    • The doGet method of the servlet.
    • This method is called when a form has its tag value method equals to get.
    • @param request the request send by the client to the server
    • @param response the response send by the server to the client
    • @throws ServletException if an error occurred
    • @throws IOException if an error occurred
  20. */
  21. public void doGet(HttpServletRequest request, HttpServletResponse response)
  22. throws ServletException, IOException {
  23. request.setCharacterEncoding("utf-8");
  24. String username = request.getParameter("username");
  25. String password = request.getParameter("password");
  26. if ("GET".equals(request.getMethod().toUpperCase())) {
  27. byte[] bytes = username.getBytes("iso-8859-1");
  28. username = new String(bytes, "utf-8");
  29. }
  30. System.out.println("usernmae===="+username);
  31. System.out.println("password==="+password);
  32. response.setCharacterEncoding("utf-8");
  33. response.getWriter().write("成功收到信息"+username+"/"+password); 上海黑马程序员——只要学不死,就往死里学,冲击年薪20 万! 4
  34. }
  35. /**
    • The doPost method of the servlet.
    • This method is called when a form has its tag value method equals to post.
    • @param request the request send by the client to the server
    • @param response the response send by the server to the client
    • @throws ServletException if an error occurred
    • @throws IOException if an error occurred
  36. */
  37. public void doPost(HttpServletRequest request, HttpServletResponse response)
  38. throws ServletException, IOException {
  39. doGet(request, response);
  40. }
  41. /**
    • Initialization of the servlet.
    • @throws ServletException if an error occurs
  42. */
  43. public void init() throws ServletException {
  44. // Put your code here
  45. }
  46. } 【文件1-2】web.xml
  47. <web-app version="2.5"
  48. xmlns="http://java.sun.com/xml/ns/javaee"
  49. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  50. xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
  51. http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
  52. LoginServlet
  53. com.itheima.servlet.LoginServlet
  54. FileuploadServlet
  55. com.itheima.servlet.FileuploadServlet

上海黑马程序员——只要学不死,就往死里学,冲击年薪20 万! 5 16. LoginServlet 17. /servlet/LoginServlet 18. 19. 20. FileuploadServlet 21. /servlet/FileuploadServlet 22. 23. 24. index.jsp 25. 26. 27. 图1-1 多种方式实现用户登录(数据提交) 1.1.3 编写布局 考虑到1.2、1.3、1.4 节使用的工程布局是一模一样的,因此在这里先将布局给出。 【文件1-3】activity_main.java

  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. <EditText 上海黑马程序员——只要学不死,就往死里学,冲击年薪20 万! 6
  7. android:id="@+id/et_username"
  8. android:layout_width="match_parent"
  9. android:layout_height="wrap_content"
  10. android:hint="请输入用户名" />
  11. <EditText
  12. android:id="@+id/et_password"
  13. android:inputType="textPassword"
  14. android:layout_width="match_parent"
  15. android:layout_height="wrap_content"
  16. android:hint="请输入密码" />
  17. <Button
  18. android:onClick="login"
  19. android:layout_width="match_parent"
  20. android:layout_height="wrap_content"
  21. android:layout_gravity="right"
  22. android:text="get 方式登录" />
  23. <Button
  24. android:onClick="login2"
  25. android:layout_width="match_parent"
  26. android:layout_height="wrap_content"
  27. android:layout_gravity="right"
  28. android:text="post 方式登录" />

31. 1.1.4 添加权限 凡是网络访问的操作,必须添加如下权限。 1.2 使用HttpURLConnection 提交数据 1.2.1 代码 MainActivity.java 代码清单见【文件1-4】。 【文件1-4】MainActivity.java

  1. package com.itheima.userlogin;
  2. import java.io.InputStream;
  3. import java.io.OutputStream;
  4. import java.net.HttpURLConnection; 上海黑马程序员——只要学不死,就往死里学,冲击年薪20 万! 7
  5. import java.net.URL;
  6. import java.net.URLEncoder;
  7. import com.itheima.userlogin1.R;
  8. import android.os.Bundle;
  9. import android.os.Handler;
  10. import android.app.Activity;
  11. import android.text.TextUtils;
  12. import android.view.View;
  13. import android.widget.EditText;
  14. import android.widget.Toast;
  15. /**
    • 使用HttpURLConnection 提交数据
    • 在该案例中分别使用了get 和post 两种
    • 方式提交数据。
    • 大家可以对比这两种方式使用上的不同。
    • @author wzy 2015-11-5
  16. */
  17. public class MainActivity extends Activity {
  18. protected static final int TIME_OUT = 5000;
  19. private EditText et_password;
  20. private EditText et_username;
  21. private Handler handler = new Handler(){
  22. @Override
  23. public void handleMessage(android.os.Message msg) {
  24. switch (msg.what) {
  25. case RESULT_OK:
  26. Toast.makeText(MainActivity.this, "成功:"
  27. +msg.obj.toString(), Toast.LENGTH_LONG).show();
  28. break;
  29. case RESULT_CANCELED:
  30. Toast.makeText(MainActivity.this, "失败:"
  31. +msg.obj.toString(), Toast.LENGTH_LONG).show();
  32. break;
  33. default:
  34. break;
  35. }
  36. };
  37. };
  38. @Override
  39. protected void onCreate(Bundle savedInstanceState) { 上海黑马程序员——只要学不死,就往死里学,冲击年薪20 万! 8
  40. super.onCreate(savedInstanceState);
  41. setContentView(R.layout.activity_main);
  42. //获取控件
  43. et_username = (EditText) findViewById(R.id.et_username);
  44. et_password = (EditText) findViewById(R.id.et_password);
  45. }
  46. /**
    • 使用get 方式完成用户的登录
    • @param view
  47. */
  48. public void login(View view){
  49. //获取用户数据
  50. final String username = et_username.getText().toString().trim();
  51. final String password = et_password.getText().toString().trim();
  52. //校验数据
  53. if (TextUtils.isEmpty(password)||TextUtils.isEmpty(username)) {
  54. Toast.makeText(this, "用户名或密码不能为空!", Toast.LENGTH_SHORT).show();
  55. return;
  56. }
  57. //开启子线程
  58. new Thread(new Runnable() {
  59. @Override
  60. public void run() {
  61. /*
    • 因为是get 方式提交参数,因此对提交的参数必须使用
  62. URLEncoder.encode(String)方法进行编码,
    • 该编码可以将中文、空格、特殊字符等转义为16 进制的数字,
  63. 这样可以兼容不同平台的差异性,而Tomcat 内部会自动将这些
    • 16 进制数给重新变为普通文本。
  64. */
  65. String path =
  66. "http://10.0.2.2:8080/userlogin/servlet/LoginServlet?username="
  67. +URLEncoder.encode(username)+"&password="+password;
  68. try {
  69. URL url = new URL(path);
  70. HttpURLConnection connection =
  71. (HttpURLConnection) url.openConnection();
  72. //配置参数
  73. connection.setRequestMethod("GET");
  74. connection.setConnectTimeout(TIME_OUT);
  75. connection.setReadTimeout(TIME_OUT);
  76. //打开链接
  77. connection.connect(); 上海黑马程序员——只要学不死,就往死里学,冲击年薪20 万! 9
  78. //获取状态码
  79. int responseCode = connection.getResponseCode();
  80. /*
    • 判断返回的状态码是否等于200,
    • 如果返回200 则代表请求成功
  81. */
  82. if (200==responseCode) {
  83. //获取返回值
  84. InputStream inputStream = connection.getInputStream();
  85. //将字节输入流转化为字符串
  86. String data = StreamUtils.inputStream2String(inputStream);
  87. //将数据通过handler 发送出去
  88. handler.obtainMessage(RESULT_OK, data).sendToTarget();
  89. }else {
  90. //如果返回状态码不等于200 则代码请求失败
  91. //将失败消息也发送出去
  92. handler.obtainMessage(RESULT_CANCELED, responseCode).sendToTarget();
  93. }
  94. } catch (Exception e) {
  95. e.printStackTrace();
  96. //将异常消息发送出去
  97. handler.obtainMessage(RESULT_CANCELED, e).sendToTarget();
  98. }
  99. }
  100. }).start();
  101. }
  102. /**
    • 使用post 方式完成用户的登录
    • @param view
  103. */
  104. public void login2(View view){
  105. //获取用户数据
  106. final String username = et_username.getText().toString().trim();
  107. final String password = et_password.getText().toString().trim();
  108. //校验数据
  109. if (TextUtils.isEmpty(password)||TextUtils.isEmpty(username)) {
  110. Toast.makeText(this, "用户名或密码不能为空!", Toast.LENGTH_SHORT).show();
  111. return;
  112. }
  113. //开启子线程
  114. new Thread(new Runnable() {
  115. @Override
  116. public void run() { 上海黑马程序员——只要学不死,就往死里学,冲击年薪20 万! 10
  117. String path =
  118. "http://10.0.2.2:8080/userlogin/servlet/LoginServlet";
  119. try {
  120. URL url = new URL(path);
  121. HttpURLConnection connection =
  122. (HttpURLConnection) url.openConnection();
  123. //配置参数
  124. connection.setRequestMethod("POST");
  125. /*
    • 设置该参数,才能以流的形式提交数据
    • 需要将要提交的数据转换为字节输出流
  126. */
  127. connection.setDoOutput(true);
  128. connection.setConnectTimeout(TIME_OUT);
  129. connection.setReadTimeout(TIME_OUT);
  130. //将提交的参数进行URL 编码
  131. String param =
  132. "username="+URLEncoder.encode(username)+"&password="+password;
  133. /*
    • 设置请求属性,相当于封装http 的请求头参数
  134. */
  135. //设置请求体的的长度
  136. connection.setRequestProperty("Content-Length", param.length()+"");
  137. //设置请求体的类型
  138. connection.setRequestProperty(
  139. "Content-Type", application/x-www-form-urlencoded");
  140. //打开链接
  141. connection.connect();
  142. //获取输出流
  143. OutputStream os = connection.getOutputStream();
  144. //通过输出流将要提交的数据提交出去
  145. os.write(param.getBytes());
  146. //关闭输出流
  147. os.close();
  148. //判断状态码
  149. int responseCode = connection.getResponseCode();
  150. if (200==responseCode) {
  151. //获取返回值
  152. InputStream inputStream = connection.getInputStream();
  153. //将字节流转换为字符串
  154. String data = StreamUtils.inputStream2String(inputStream);
  155. handler.obtainMessage(RESULT_OK, data).sendToTarget();
  156. }else {
  157. handler.obtainMessage(RESULT_CANCELED, responseCode).sendToTarget(); 上海黑马程序员——只要学不死,就往死里学,冲击年薪20 万! 11
  158. }
  159. } catch (Exception e) {
  160. e.printStackTrace();
  161. handler.obtainMessage(RESULT_CANCELED, e).sendToTarget();
  162. }
  163. }
  164. }).start();
  165. }
  166. } 1.2.2 总结 在http 协议中,get 请求协议没有请求体,只有请求头和请求行,因此如果想使用get 方式提交数据, 只能通过在url 后面加上要提交的参数。这也意味着get 方式只能提交比较小的参数。 如果get 方式提交的参数有特殊字符或者中文字符那么必须对其进行URL 编码,不然会导致服务器接 收到的数据乱码。 对于post 请求,提交的数据位于请求体中,因此需要设置connection.setDoOutput(true);参数,该参数 设置后才允许将提交的数据通过输出流的形式提交。不过需要说明的是虽然采用post 方式提交,依然需要 将参数进行URL 编码。 其实当我们使用浏览器进行form 表单提交的时候,浏览器内部已经帮我们实现了URL 编码。因为我 们这里是使用代码模拟浏览器的提交数据过程,因此需要使用代码特殊处理。 1.3 使用HttpClient 提交数据 HttpClient 是Apache Jakarta Common 下的子项目,提供了高效的、最新的、功能丰富的支持HTTP 协 议的客户端编程工具包,并且它支持HTTP 协议最新的版本。 HttpClient 被内置到Android SDK 中,因此可以在不添加任何额外jar 包情况下,直接使用。 1.3.1 get 方式提交 在1.2 节工程的基础上,只需要修改部分代码即可。因此这里只给出核心代码。 【文件1-5】get 方式提交数据代码片段
  167. /**
    • HttpCLient 使用get 方式完成用户的登录
    • @param view
  168. */
  169. public void login3(View view) {
  170. // 获取用户数据
  171. final String username = et_username.getText().toString().trim(); 上海黑马程序员——只要学不死,就往死里学,冲击年薪20 万! 12
  172. final String password = et_password.getText().toString().trim();
  173. // 校验数据
  174. if (TextUtils.isEmpty(password) || TextUtils.isEmpty(username)) {
  175. Toast.makeText(this, "用户名或密码不能为空!", Toast.LENGTH_SHORT).show();
  176. return;
  177. }
  178. // 开启子线程
  179. new Thread(new Runnable() {
  180. @Override
  181. public void run() {
  182. String path =
  183. "http://10.0.2.2:8080/userlogin/servlet/LoginServlet?username="
    • URLEncoder.encode(username) + "&password=" + password;
  184. try {
  185. // 创建一个httpClient 对象
  186. HttpClient client = new DefaultHttpClient();
  187. // 创建一个请求方式
  188. HttpGet request = new HttpGet(path);
  189. // 执行操作
  190. HttpResponse response = client.execute(request);
  191. // 获取放回状态对象
  192. StatusLine statusLine = response.getStatusLine();
  193. // 获取状态码
  194. int statusCode = statusLine.getStatusCode();
  195. if (200 == statusCode) {
  196. // 获取服务器返回的对象
  197. HttpEntity entity = response.getEntity();
  198. // 获取输入流
  199. InputStream inputStream = entity.getContent();
  200. // 将输入流转化为字符串
  201. String data = StreamUtils.inputStream2String(inputStream);
  202. handler.obtainMessage(RESULT_OK, data).sendToTarget();
  203. } else {
  204. handler.obtainMessage(RESULT_CANCELED, statusCode).sendToTarget();
  205. }
  206. } catch (Exception e) {
  207. e.printStackTrace();
  208. handler.obtainMessage(RESULT_CANCELED, e).sendToTarget();
  209. }
  210. }
  211. }).start();
  212. } 上海黑马程序员——只要学不死,就往死里学,冲击年薪20 万! 13 1.3.2 post 方式提交 【文件1-6】post 方式提交数据代码片段
  213. /**
    • HttpCLient 使用post 方式完成用户的登录
    • @param view
  214. */
  215. public void login4(View view) {
  216. // 获取用户数据
  217. final String username = et_username.getText().toString().trim();
  218. final String password = et_password.getText().toString().trim();
  219. // 校验数据
  220. if (TextUtils.isEmpty(password) || TextUtils.isEmpty(username)) {
  221. Toast.makeText(this, "用户名或密码不能为空!", Toast.LENGTH_SHORT).show();
  222. return;
  223. }
  224. // 开启子线程
  225. new Thread(new Runnable() {
  226. @Override
  227. public void run() {
  228. String path = "http://10.0.2.2:8080/userlogin/servlet/LoginServlet";
  229. try {
  230. // 创建一个httpClient 对象
  231. HttpClient client = new DefaultHttpClient();
  232. // 创建一个post 请求方式
  233. HttpPost request = new HttpPost(path);
  234. //创建集合用于添加请求的数据
  235. List parameters =
  236. new ArrayList();
  237. //将请求数据添加到集合中
  238. parameters.add(new BasicNameValuePair("username", username));
  239. parameters.add(new BasicNameValuePair("password", password));
  240. /*
    • 创建请求实体对象
    • UrlEncodedFormEntity 是HttpEntity 的子类,
  241. 该类专门用于封装字符串类型的参数
    • 指定数据的编码格式
  242. */
  243. HttpEntity requestEntity =
  244. new UrlEncodedFormEntity(parameters, "utf-8");
  245. //设置请求体
  246. request.setEntity(requestEntity); 上海黑马程序员——只要学不死,就往死里学,冲击年薪20 万! 14
  247. // 执行操作
  248. HttpResponse response = client.execute(request);
  249. // 获取放回状态对象
  250. StatusLine statusLine = response.getStatusLine();
  251. // 获取状态码
  252. int statusCode = statusLine.getStatusCode();
  253. if (200 == statusCode) {
  254. // 获取服务器返回的对象
  255. HttpEntity entity = response.getEntity();
  256. // 获取输入流
  257. InputStream inputStream = entity.getContent();
  258. // 将输入流转化为字符串
  259. String data = StreamUtils.inputStream2String(inputStream);
  260. handler.obtainMessage(RESULT_OK, data).sendToTarget();
  261. } else {
  262. handler.obtainMessage(RESULT_CANCELED, statusCode).sendToTarget();
  263. }
  264. } catch (Exception e) {
  265. e.printStackTrace();
  266. handler.obtainMessage(RESULT_CANCELED, e).sendToTarget();
  267. }
  268. }
  269. }).start();
  270. } 1.3.3 总结 一、get 请求方式步骤 1、创建一个httpClient 对象 HttpClient client = new DefaultHttpClient(); 2、创建一个请求方式 String path = "http://10.0.2.2:8080/userlogin/servlet/LoginServlet?username="
  • URLEncoder.encode(username) + "&password=" + password; //因为是get 方式,因此需要在path 中添加请求参数 HttpGet request = new HttpGet(path); 3、开始访问网络,并接收返回值 HttpResponse response = client.execute(request); 4、获取返回状态码 //获取返回值的状态行 StatusLine statusLine = response.getStatusLine(); 上海黑马程序员——只要学不死,就往死里学,冲击年薪20 万! 15 // 获取状态码 int statusCode = statusLine.getStatusCode(); 5、判断状态码 //如果状态码为200 则代表请求成功 if (200 == statusCode) {//TODO} 6、获取返回数据 // 获取服务器返回的对象 HttpEntity entity = response.getEntity(); // 获取输入流 InputStream inputStream = entity.getContent(); 二、post 请求方式步骤 1、创建一个httpClient 对象 HttpClient client = new DefaultHttpClient(); 2、创建一个post 请求方式 String path = "http://10.0.2.2:8080/userlogin/servlet/LoginServlet"; HttpPost request = new HttpPost(path); 3、设置请求体 //创建集合用于添加请求的数据 List parameters = new ArrayList(); //将请求数据添加到集合中 parameters.add(new BasicNameValuePair("username", username)); parameters.add(new BasicNameValuePair("password", password)); /*
  • 创建请求实体对象
  • UrlEncodedFormEntity 是HttpEntity 的子类,该类专门用于封装字符串类型的参数
  • 指定数据的编码格式 */ HttpEntity requestEntity = new UrlEncodedFormEntity(parameters, "utf-8"); //设置请求体 request.setEntity(requestEntity); 4、开始访问网络,并接收返回值 // 执行操作 HttpResponse response = client.execute(request); 5、获取返回状态码 //获取返回值的状态行 StatusLine statusLine = response.getStatusLine(); // 获取状态码 int statusCode = statusLine.getStatusCode(); 6、判断状态码 //如果状态码为200 则代表请求成功 if (200 == statusCode) {//TODO} 7、获取返回数据 上海黑马程序员——只要学不死,就往死里学,冲击年薪20 万! 16 // 获取服务器返回的对象 HttpEntity entity = response.getEntity(); // 获取输入流 InputStream inputStream = entity.getContent(); 1.4 使用AsyncHttpClient 框架提交数据 AsyncHttpClient 是开源免费的基于HttpClient 的网络请求框架,其源码托管在githu 上。下载地址: https://github.com/loopj/android-async-http。 如图1-2 所示为AsyncHttpClient 在github 的网页,大家可以点击右下角的按钮,将源码下载下来,然 后将下载好的压缩包解压,把src 目录中源码拷贝到自己的工程的src 目录下,比如图1-3 所示。 图1-2 AsyncHttpClient 网页 上海黑马程序员——只要学不死,就往死里学,冲击年薪20 万! 17 图1-3 将AsyncHttpClient 添加到工程中 1.4.1 get 方式提交 在1.2 节工程的基础上,只需要修改部分代码即可。因此这里只给出核心代码。 【文件1-7】get 方式提交数据代码片段
  1. /**
    • 使用AsyncHttpClient 的get 方式提交数据
    • @param view
  2. */
  3. public void login5(View view) {
  4. // 获取用户数据
  5. final String username = et_username.getText().toString().trim();
  6. final String password = et_password.getText().toString().trim();
  7. // 校验数据
  8. if (TextUtils.isEmpty(password) || TextUtils.isEmpty(username)) {
  9. Toast.makeText(this, "用户名或密码不能为空!", Toast.LENGTH_SHORT).show();
  10. return;
  11. }
  12. String path = "http://10.0.2.2:8080/userlogin/servlet/LoginServlet?username="
    • URLEncoder.encode(username) + "&password=" + password;
  13. // 创建一个AsyncHttpClient
  14. AsyncHttpClient client = new AsyncHttpClient();
  15. //执行get 方式请求
  16. client.get(path, new DataAsyncHttpResponseHandler() {
  17. /*
    • 请求网络是在子线程中进行的,当请求成功后回调onSuccess 方法,
    • 该方法是在主线程中被调用了,其内部是通过Handler 实现的
    • 当请求成功后回调该方法
    • 参数1:返回的状态码
    • 参数2:响应头
    • 参数3:返回的数据
  18. */
  19. @Override
  20. public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
  21. Log.d("tag", Thread.currentThread().getName());
  22. //将responseBody 字节数组转化为字符串
  23. Toast.makeText(MainActivity.this, new String(responseBody),
  24. Toast.LENGTH_LONG).show();
  25. }
  26. /*
    • 当请求失败后回调该方法,该方法依然在主线程中被执行
  27. */ 上海黑马程序员——只要学不死,就往死里学,冲击年薪20 万! 18
  28. @Override
  29. public void onFailure(int statusCode, Header[] headers, byte[] responseBody,
  30. Throwable error) {
  31. Toast.makeText(MainActivity.this, new String(responseBody),
  32. Toast.LENGTH_LONG).show();
  33. }
  34. });
  35. } 1.4.2 post 方式提交 【文件1-8】post 方式提交数据代码片段
  36. /**
    • 使用AsyncHttpClient 的post 方式提交数据
    • @param view
  37. */
  38. public void login6(View view) {
  39. // 获取用户数据
  40. final String username = et_username.getText().toString().trim();
  41. final String password = et_password.getText().toString().trim();
  42. // 校验数据
  43. if (TextUtils.isEmpty(password) || TextUtils.isEmpty(username)) {
  44. Toast.makeText(this, "用户名或密码不能为空!", Toast.LENGTH_SHORT).show();
  45. return;
  46. }
  47. String path = "http://10.0.2.2:8080/userlogin/servlet/LoginServlet";
  48. // 创建一个AsyncHttpClient
  49. AsyncHttpClient client = new AsyncHttpClient();
  50. //封装请求参数
  51. RequestParams params = new RequestParams();
  52. params.put("username", username);
  53. params.put("password", password);
  54. /*
    • 执行post 请求
    • 参数1:url 地址
    • 参数2:请求参数
    • 参数3:回调接口,在该接口中实现成功和失败方法,该过程是异步的。
  55. */
  56. client.post(path, params, new DataAsyncHttpResponseHandler() {
  57. @Override
  58. public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) 上海黑马程序员——只要学不死,就往死里学,冲击年薪20 万! 19 {
  59. Toast.makeText(MainActivity.this, new String(responseBody),
  60. Toast.LENGTH_LONG).show();
  61. }
  62. @Override
  63. public void onFailure(int statusCode, Header[] headers, byte[] responseBody,
  64. Throwable error) {
  65. Toast.makeText(MainActivity.this, new String(responseBody),
  66. Toast.LENGTH_LONG).show();
  67. }
  68. });
  69. } 1.4.3 总结 一、get 请求方式步骤 1、创建一个AsyncHttpClient 对象 AsyncHttpClient client = new AsyncHttpClient(); 2、执行get 方法 /*
  • 执行get 方式请求
  • 参数1:请求的url 地址
  • 参数2:回调接口。在该接口中实现相应成功和失败方法,该过程是异步的。 / client.get(path, new DataAsyncHttpResponseHandler() { /
  • 请求网络是在子线程中进行的,当请求成功后回调onSuccess 方法,
  • 该方法是在主线程中被调用了,其内部是通过Handler 实现的
  • 当请求成功后回调该方法
  • 参数1:返回的状态码
  • 参数2:响应头
  • 参数3:返回的数据 / @Override public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) { Log.d("tag", Thread.currentThread().getName()); //将responseBody 字节数组转化为字符串 Toast.makeText(MainActivity.this, new String(responseBody), Toast.LENGTH_LONG).show(); } /
  • 当请求失败后回调该方法,该方法依然在主线程中被执行 上海黑马程序员——只要学不死,就往死里学,冲击年薪20 万! 20 / @Override public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) { Toast.makeText(MainActivity.this, new String(responseBody), Toast.LENGTH_LONG).show(); } }); 在执行get 方法的时候需要传递一个ResponseHandlerInterface 接口的子类, 在上面使用了 DataAsyncHttpResponseHandler 抽象类,除此之外其实ResponseHandlerInterface 还有很多子类,比如用于 接收json 字符串的TextHttpResponseHandler,用于接收文件下载的TextHttpResponseHandler。因为我们只 需要接收字节数组,并将字节数组转化为字符串即可,因此我们使用DataAsyncHttpResponseHandler 抽象 类。 二、post 方式请求步骤 1、创建一个AsyncHttpClient 对象 AsyncHttpClient client = new AsyncHttpClient(); 2、封装请求参数 //封装请求参数 RequestParams params = new RequestParams(); params.put("username", username); params.put("password", password); 3、执行post 方法 /
  • 执行post 请求
  • 参数1:url 地址
  • 参数2:请求参数
  • 参数3:回调接口,在该接口中实现成功和失败方法,该过程是异步的。 / client.post(path, params, new DataAsyncHttpResponseHandler() { @Override public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) { Toast.makeText(MainActivity.this, new String(responseBody), Toast.LENGTH_LONG).show(); } @Override public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) { Toast.makeText(MainActivity.this, new String(responseBody), Toast.LENGTH_LONG).show(); } }); post 方法跟get 方式不同的就是多了一个RequestParams 参数。 上海黑马程序员——只要学不死,就往死里学,冲击年薪20 万! 21 1.5 JavaSE 实现多线程下载 1.5.1 多线程下载原理 多线程下载就是将同一个网络上的原始文件根据线程个数分成均等份,然后每个单独的线程下载对应 的一部分,然后再将下载好的文件按照原始文件的顺序“拼接”起来就构成了完整的文件了。这样就大大 提高了文件的下载效率。 多线程下载大致可分为以下几个步骤: 一、获取服务器上的目标文件的大小 显然这一步是需要先访问一下网络,只需要获取到目标文件的总大小即可。目的是为了计算每个线程 应该分配的下载任务。 二、计算每个线程下载的起始位置和结束位置 我们可以把原始文件当成一个字节数组,每个线程只下载该“数组”的指定起始位置和指定结束位置 之间的部分。在第一步中我们已经知道了“数组”的总长度。因此只要再知道总共开启的线程的个数就好 计算每个线程要下载的范围了。 每个线程需要下载的字节个数(blockSize)=总字节数(totalSize)/线程数(threadCount)。 假设给线程按照0,1,2,3...n 的方式依次进行编号,那么第n 个线程下载文件的范围为: 起始脚标startIndex=nblockSize。 结束脚标endIndex=(n-1)*blockSize-1。 考虑到totalSize/threadCount 不一定能整除,因此对已最后一个线程应该特殊处理,最后一个线程的起 始脚标计算公式不变,但是结束脚标endIndex=totalSize-1;即可。 三、在本地创建一个跟原始文件同样大小的文件 在本地可以通过RandomAccessFile 创建一个跟目标文件同样大小的文件,该api 支持文件任意位置的 读写操作。这样就给多线程下载提供了方便,每个线程只需在指定起始和结束脚标范围内写数据即可。 四、开启多个子线程开始下载 五、记录下载进度 为每一个单独的线程创建一个临时文件,用于记录该线程下载的进度。对于单独的一个线程,每下载 一部分数据就在本地文件中记录下当前下载的字节数。这样子如果下载任务异常终止了,那么下次重新开 始下载时就可以接着上次的进度下载。 六、删除临时文件 当多个线程都下载完成之后,最后一个下载完的线程将所有的临时文件删除。 1.5.2 代码实现 【文件1-9】多线程下载JavaSE 代码
  1. package com.itheima.se.download;
  2. import java.io.BufferedReader;
  3. import java.io.BufferedWriter;
  4. import java.io.File;
  5. import java.io.FileReader; 上海黑马程序员——只要学不死,就往死里学,冲击年薪20 万! 22
  6. import java.io.FileWriter;
  7. import java.io.InputStream;
  8. import java.io.RandomAccessFile;
  9. import java.net.HttpURLConnection;
  10. import java.net.URL;
  11. /*
    • 实现多线程断点续下载
  12. */
  13. public class MultiDownlodTest {
  14. /**
    • @param args
  15. */
  16. public static void main(String[] args) {
  17. String sourcePath = "http://localhost:8080/FeiQ.exe";
  18. String targetPath = "d://FeiQ.exe";
  19. new MultiDownloader(sourcePath,3,targetPath).download();
  20. }
  21. static class MultiDownloader {
  22. private String sourcePath;
  23. private int threadCount;
  24. private String targetPath;
  25. //未完成任务的线程
  26. private int threadRunning;
  27. public MultiDownloader(String sourcePath,int threadCount,String targetPath){
  28. this.sourcePath = sourcePath;
  29. this.threadCount = threadCount;
  30. this.targetPath = targetPath;
  31. }
  32. public void download(){
  33. try {
  34. URL url = new URL(sourcePath);
  35. HttpURLConnection connection =
  36. (HttpURLConnection) url.openConnection();
  37. //配置连接参数
  38. connection.setRequestMethod("GET");
  39. connection.setConnectTimeout(5000);
  40. //打开连接
  41. connection.connect();
  42. int responseCode = connection.getResponseCode();
  43. if (responseCode==200) {
  44. int totalSize = connection.getContentLength(); 上海黑马程序员——只要学不死,就往死里学,冲击年薪20 万! 23
  45. //计算每个线程下载的平均字节数
  46. int avgSize = totalSize/threadCount;
  47. for(int i=0;i<threadCount;i++){
  48. final int startIndex = i*avgSize;
  49. int endIndex = 0;
  50. if (i==threadCount-1) {
  51. endIndex = totalSize-1;
  52. }else {
  53. endIndex = (i+1)*avgSize-1;
  54. }
  55. threadRunning++;
  56. //开启子线程,实现下载
  57. new MyDownloadThread(i,
  58. startIndex, endIndex,targetPath,sourcePath).start();
  59. }
  60. }else {
  61. System.out.println("返回码为:"+responseCode+" 请求文件长度失败!");
  62. return;
  63. }
  64. } catch (Exception e) {
  65. e.printStackTrace();
  66. }
  67. }
  68. /**
    • @author wzy 2015-10-26
  69. */
  70. class MyDownloadThread extends Thread{
  71. private int id;
  72. private int startIndex;
  73. private int endIndex;
  74. private String targetPath;
  75. private String sourcePath;
  76. public MyDownloadThread(int id,int startIndex,
  77. int endIndex,String targetPath,String sourcePath){
  78. this.id = id;
  79. this.startIndex = startIndex;
  80. this.endIndex = endIndex;
  81. this.targetPath = targetPath;
  82. this.sourcePath = sourcePath;
  83. }
  84. @Override 上海黑马程序员——只要学不死,就往死里学,冲击年薪20 万! 24
  85. public void run() {
  86. try {
  87. URL url = new URL(sourcePath);
  88. HttpURLConnection connection =
  89. (HttpURLConnection) url.openConnection();
  90. //设置断点下载参数
  91. connection.setRequestMethod("GET");
  92. File file = new File("d://"+id+".tmp");
  93. /*
    • 该属性设置后,返回的状态码就不再是200,而是206
  94. */
  95. int currentIndex = -1;
  96. //读进度
  97. if (file.exists()) {
  98. BufferedReader reader =
  99. new BufferedReader(new FileReader(file));
  100. String readLine = reader.readLine();
  101. currentIndex = Integer.valueOf(readLine);
  102. reader.close();
  103. }else {
  104. currentIndex = startIndex;
  105. }
  106. ////只有设置了该属性才能下载指定范围的文件
  107. connection.setRequestProperty("Range", "bytes="+currentIndex+"-"+endIndex);
  108. connection.setConnectTimeout(5000);
  109. connection.setReadTimeout(5000);
  110. connection.connect();
  111. int responseCode = connection.getResponseCode();
  112. //因为请求的是一个文件的部分,因此返回的状体码是206
  113. if (responseCode==206) {
  114. InputStream inputStream = connection.getInputStream();
  115. //支持随机读写的文件类
  116. RandomAccessFile raf =
  117. new RandomAccessFile(targetPath, "rws");
  118. //将文件指针定位到要写的位置
  119. raf.seek(currentIndex);
  120. byte[] buffer = new byte[1024*16];
  121. int len = -1;
  122. int totalDownloded = 0;
  123. while((len=inputStream.read(buffer))!=-1){
  124. raf.write(buffer, 0, len);
  125. totalDownloded+=len;
  126. //写进度
  127. BufferedWriter writer = 上海黑马程序员——只要学不死,就往死里学,冲击年薪20 万! 25
  128. new BufferedWriter(new FileWriter(file));
  129. writer.write(currentIndex+totalDownloded+"");
  130. writer.close();
  131. System.out.println(id+"下载了:"+totalDownloded+","+
  132. (totalDownloded+currentIndex-startIndex)*100/(endIndex-startIndex)+"%");
  133. }
  134. raf.close();
  135. inputStream.close();
  136. connection.disconnect();
  137. //线程执行完了
  138. threadRunning--;
  139. if (threadRunning==0) {
  140. for(int i=0;i<threadCount;i++){
  141. File tmpFile = new File("d://"+i+".tmp");
  142. tmpFile.delete();
  143. }
  144. }
  145. }else {
  146. System.out.println("返回的状态码为:"+responseCode+
  147. " 下载失败!");
  148. }
  149. } catch (Exception e) {
  150. e.printStackTrace();
  151. }
  152. }
  153. }
  154. }
  155. }

新技能: 1、多线程下载文件的请求属性 如果我们想请求服务器上某文件的一部分,而不是将整个文件都下载下来,那么就必须设置如下 属性:connection.setRequestProperty("Range", "bytes="+currentIndex+"-"+endIndex); 2、请求部分文件返回成功的状态码是206 当我们请求部分文件成功后,服务器返回的状态码不是200,而是206。 1.6 Android 实现多线程下载 将上面JavaSE 实现多线程下载的代码经过一定的改造,便可移植到Android 上。有所不同的是Android 有界面可以跟用户进行良好的交互,在界面(如图1-4)上让用户输入原文件地址、线程个数,然后点击 上海黑马程序员——只要学不死,就往死里学,冲击年薪20 万! 26 确定开始下载。为了让用户可以清晰的看到每个线程下载的进度根据线程个数动态的生成等量的进度条 (ProgressBar)。 图1-4 Android 实现多线程下载 1.6.1 ProgressBar 的使用 ProgressBar 是一个进度条控件,用于显示一项任务的完成进度。其有两种样式,一种是圆形的, 该种样式是系统默认的,由于无法显示具体的进度值,适合用于不确定要等待多久的情形下;另一种是长 条形的, ,此类进度条有两种颜色,高亮颜色代表任务完成的总进度。对 于我们下载任务来说,由于总任务(要下载的字节数)是明确的,当前已经完成的任务(已经下载的字节 数)也是明确的,因此特别适合使用后者。 由于在我们的需求里ProgressBar 是需要根据线程的个数动态添加的,而且要求是长条形的。因此可以 事先在布局文件中编写好ProgressBar 的样式。当需要用到的时候再将该布局(见【文件1-11】)填充起 来。 ProgressBar 的max 属性代表其最大刻度值,progress 属性代表当前进度值。使用方法如下: ProgressBar.setMax(int max);设置最大刻度值。 ProgressBar.setProgress(int progress);设置当前进度值。 给ProgressBar 设置最大刻度值和修改进度值可以在子线程中操作的,其内部已经特殊处理过了,因此 不需要再通过发送Message 让主线程修改进度。 1.6.2 编写布局 这里给出两个布局,【文件1-10】是MainActivity 的布局,其显示效果如图1-4。【文件1-11】是ProgressBar 布局。 【文件1-10】activity_main.java

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 上海黑马程序员——只要学不死,就往死里学,冲击年薪20 万! 27
  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. <EditText
  7. android:id="@+id/et_url"
  8. android:layout_width="match_parent"
  9. android:layout_height="wrap_content"
  10. android:text="http://10.0.2.2:8080/FeiQ.exe" />
  11. <LinearLayout
  12. android:layout_width="match_parent"
  13. android:layout_height="wrap_content"
  14. android:orientation="horizontal"
  15. <EditText
  16. android:id="@+id/et_threadCount"
  17. android:layout_width="0dp"
  18. android:layout_weight="1"
  19. android:layout_height="wrap_content"
  20. android:text="3"
  21. />
  22. <Button
  23. android:onClick="download"
  24. android:layout_width="wrap_content"
  25. android:layout_height="wrap_content"
  26. android:text="下载"
  27. />
  28. <LinearLayout
  29. android:id="@+id/ll_pbs"
  30. android:layout_width="match_parent"
  31. android:layout_height="wrap_content"
  32. android:orientation="vertical"

【文件1-11】progress_bar.xml

  1. <ProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
  2. style="?android:attr/progressBarStyleHorizontal"
  3. android:layout_width="match_parent" 上海黑马程序员——只要学不死,就往死里学,冲击年薪20 万! 28
  4. android:layout_height="wrap_content" >

1.6.3 编写代码 【文件1-12】MainActivity.java

  1. package com.itheima.android.downloader;
  2. import java.io.BufferedReader;
  3. import java.io.BufferedWriter;
  4. import java.io.File;
  5. import java.io.FileReader;
  6. import java.io.FileWriter;
  7. import java.io.InputStream;
  8. import java.io.RandomAccessFile;
  9. import java.net.HttpURLConnection;
  10. import java.net.URL;
  11. import android.os.Bundle;
  12. import android.support.v4.app.FragmentActivity;
  13. import android.text.TextUtils;
  14. import android.view.View;
  15. import android.widget.EditText;
  16. import android.widget.LinearLayout;
  17. import android.widget.ProgressBar;
  18. import android.widget.Toast;
  19. /**
    • Android 实现多线程断点续传
    • @author wzy 2015-11-6
  20. */
  21. public class MainActivity extends FragmentActivity {
  22. private EditText et_url;
  23. private EditText et_threadCount;
  24. //用于添加ProgressBar 的容器
  25. private LinearLayout ll_pbs;
  26. @Override
  27. protected void onCreate(Bundle savedInstanceState) {
  28. super.onCreate(savedInstanceState);
  29. setContentView(R.layout.activity_main);
  30. //初始化控件 上海黑马程序员——只要学不死,就往死里学,冲击年薪20 万! 29
  31. et_url = (EditText) findViewById(R.id.et_url);
  32. et_threadCount = (EditText) findViewById(R.id.et_threadCount);
  33. ll_pbs = (LinearLayout) findViewById(R.id.ll_pbs);
  34. }
  35. /**
    • 点击Button 绑定的方法
    • 开始下载任务
    • @param view
  36. */
  37. public void download(View view) {
  38. // 获取用户输入的初始值
  39. final String sourcePath = et_url.getText().toString().trim();
  40. final int threadCount =
  41. Integer.valueOf(et_threadCount.getText().toString().trim());
  42. // 校验数据
  43. if (TextUtils.isEmpty(sourcePath) || threadCount < 1) {
  44. Toast.makeText(this, "输入的内容不合法!", Toast.LENGTH_SHORT).show();
  45. return;
  46. }
  47. //存储到Android 本地的位置
  48. final String targetPath = "/mnt/sdcard/FeiQ.exe";
  49. //初始化进度条
  50. //填充一个ProgressBar
  51. for(int i=0;i<threadCount;i++){
  52. //将进度条布局填充为ProgressBar
  53. ProgressBar pb = (ProgressBar) View.inflate(MainActivity.this,
  54. R.layout.progress_bar, null);
  55. //将ProgressBar 添加到LinearLayout 中
  56. ll_pbs.addView(pb);
  57. }
  58. //开启子线程开始下载任务
  59. new Thread(new Runnable() {
  60. @Override
  61. public void run() {
  62. //开启多线程下载器
  63. new MultiDownloader(sourcePath,threadCount,targetPath).download();
  64. }
  65. }).start();
  66. }
  67. class MultiDownloader {
  68. //服务器原始文件地址
  69. private String sourcePath; 上海黑马程序员——只要学不死,就往死里学,冲击年薪20 万! 30
  70. //线程个数
  71. private int threadCount;
  72. //本地目标存储文件路径
  73. private String targetPath;
  74. //未完成任务的线程
  75. private int threadRunning;
  76. public MultiDownloader(String sourcePath,int threadCount,String targetPath){
  77. this.sourcePath = sourcePath;
  78. this.threadCount = threadCount;
  79. this.targetPath = targetPath;
  80. }
  81. public void download(){
  82. try {
  83. URL url = new URL(sourcePath);
  84. HttpURLConnection connection = (HttpURLConnection) url.openConnection();
  85. //配置连接参数
  86. connection.setRequestMethod("GET");
  87. connection.setConnectTimeout(5000);
  88. //打开连接
  89. connection.connect();
  90. int responseCode = connection.getResponseCode();
  91. if (responseCode==200) {
  92. //获取总字节数
  93. int totalSize = connection.getContentLength();
  94. //计算每个线程下载的平均字节数
  95. int avgSize = totalSize/threadCount;
  96. //计算每个线程下载的范围
  97. for(int i=0;i<threadCount;i++){
  98. final int startIndex = i*avgSize;
  99. int endIndex = 0;
  100. if (i==threadCount-1) {
  101. endIndex = totalSize-1;
  102. }else {
  103. endIndex = (i+1)*avgSize-1;
  104. }
  105. threadRunning++;
  106. //开启子线程,实现下载
  107. new MyDownloadThread(i, startIndex,
  108. endIndex,targetPath,sourcePath).start();
  109. }
  110. }else {
  111. System.out.println("返回码为:"+responseCode+" 请求文件长度失败!"); 上海黑马程序员——只要学不死,就往死里学,冲击年薪20 万! 31
  112. return;
  113. }
  114. } catch (Exception e) {
  115. e.printStackTrace();
  116. }
  117. }
  118. /**
    • 下载线程
    • @author wzy 2015-10-26
  119. */
  120. class MyDownloadThread extends Thread{
  121. private int id;
  122. private int startIndex;
  123. private int endIndex;
  124. private String targetPath;
  125. private String sourcePath;
  126. public MyDownloadThread(int id,int startIndex,
  127. int endIndex,String targetPath,String sourcePath){
  128. this.id = id;
  129. this.startIndex = startIndex;
  130. this.endIndex = endIndex;
  131. this.targetPath = targetPath;
  132. this.sourcePath = sourcePath;
  133. }
  134. @Override
  135. public void run() {
  136. try {
  137. URL url = new URL(sourcePath);
  138. HttpURLConnection connection =
  139. (HttpURLConnection) url.openConnection();
  140. //设置断点下载参数
  141. connection.setRequestMethod("GET");
  142. //根据线程的id 创建临时文件,用于记录下载的进度
  143. File file = new File("/mnt/sdcard/"+id+".tmp");
  144. /*
    • 该属性设置后,返回的状态码就不再是200,而是206
  145. */
  146. int currentIndex = -1;
  147. //读进度
  148. if (file.exists()) {
  149. BufferedReader reader =
  150. new BufferedReader(new FileReader(file)); 上海黑马程序员——只要学不死,就往死里学,冲击年薪20 万! 32
  151. String readLine = reader.readLine();
  152. //读取历史进度
  153. currentIndex = Integer.valueOf(readLine);
  154. reader.close();
  155. }else {
  156. currentIndex = startIndex;
  157. }
  158. //设置多线程下载属性
  159. connection.setRequestProperty("Range", "bytes="+currentIndex+"-"+endIndex);
  160. connection.setConnectTimeout(5000);
  161. connection.setReadTimeout(5000);
  162. connection.connect();
  163. int responseCode = connection.getResponseCode();
  164. if (responseCode==206) {
  165. InputStream inputStream = connection.getInputStream();
  166. //支持随机读写的文件类
  167. RandomAccessFile raf =
  168. new RandomAccessFile(targetPath, "rws");
  169. //将文件指针定位到要写的位置
  170. raf.seek(currentIndex);
  171. byte[] buffer = new byte[1024*16];
  172. int len = -1;
  173. int totalDownloded = 0;
  174. //获取线性布局的子控件(ProgressBar)
  175. ProgressBar pb = (ProgressBar) ll_pbs.getChildAt(id);
  176. //设置对打的进度值
  177. pb.setMax(endIndex-startIndex);
  178. while((len=inputStream.read(buffer))!=-1){
  179. raf.write(buffer, 0, len);
  180. totalDownloded+=len;
  181. //写进度
  182. BufferedWriter writer =
  183. new BufferedWriter(new FileWriter(file));
  184. writer.write(currentIndex+totalDownloded+"");
  185. writer.close();
  186. //修改进度条

pb.setProgress(currentIndex+totalDownloded-startIndex); 210. System.out.println(id+"下载了:"+totalDownloded+","+ 211. (totalDownloded+currentIndex-startIndex)*100/(endIndex-startIndex)+"%"); 212. } 213. raf.close(); 上海黑马程序员——只要学不死,就往死里学,冲击年薪20 万! 33 214. inputStream.close(); 215. connection.disconnect(); 216. //线程执行完了 217. threadRunning--; 218. //如果所有的线程都下载完了,则删除所有的临时文件 219. if (threadRunning==0) { 220. for(int i=0;i<threadCount;i++){ 221. File tmpFile = new File("/mnt/sdcard/"+i+".tmp"); 222. tmpFile.delete(); 223. } 224. } 225. }else { 226. System.out.println("返回的状态码为:"+responseCode+ 227. " 下载失败!"); 228. } 229. } catch (Exception e) { 230. e.printStackTrace(); 231. } 232. } 233. } 234. 235. } 236. } 237. 1.6.4 添加权限 在该工程中不仅用到了网络访问还用到了sdcard 存储,因此需要添加两个权限。 1.7 xUtils 实现多线程下载 1.7.1 xUtils 简介 xUtils 是开源免费的Android 工具包,代码托管在github 上。目前xUtils 主要有四大模块:  DbUtils 模块 操作数据库的框架。  ViewUtils 模块 通过注解的方式可以对UI,资源和事件绑定进行管理。  HttpUtils 模块 提供了方便的网络访问,断点续传等功能。 上海黑马程序员——只要学不死,就往死里学,冲击年薪20 万! 34  BitmapUtils 模块 提供了强大的图片处理工具。 我们在这里只简单实用xUtils 工具中的HttpUtils 工具。xUtils 的下载地址: https://github.com/wyouflf/xUtils 图1-5 xUtils 在Github 网页截图 1.7.2 xUtils 之HttpUtils 的使用 一、下载xUtils 工具如图1-5 所示,右下角“Download ZIP” 二、将下载好的zip 包解压,然后将src 下的源代码拷贝在自己工程中如图1-6。 图1-6 xUtils 拷贝到src 目录中 上海黑马程序员——只要学不死,就往死里学,冲击年薪20 万! 35 三、使用HttpUtils HttpUtils 的使用非常简单,因此这里直接给出核心代码。 【文件1-13】使用HttpUtils 完成文件下载

  1. /*
    • 使用xUtils 中的HttpUtils 模块进行下载
  2. */
  3. public void downloadHttpUtils(View view){
  4. HttpUtils httpUtils = new HttpUtils();
  5. String url="http://10.0.2.2:8080/FeiQ.exe";
  6. String target = "/mnt/sdcard/FeiQ2.exe";
  7. /*
    • 参数1:原文件网络地址
    • 参数2:本地保存的地址
    • 参数3:是否支持断点续传,true:支持,false:不支持
    • 参数4:回调接口,该接口中的方法都是在主线程中被调用的,
    • 也就是该接口中的方法都可以修改UI
  8. */
  9. httpUtils.download(url, target, true, new RequestCallBack() {
  10. //当下载任务开始时被调用
  11. @Override
  12. public void onStart() {
  13. Log.d("tag", "onStart"+Thread.currentThread().getName());
  14. }
  15. //每下载一部分就被调用一次,通过该方法可以知道当前下载进度
  16. /*
    • 参数1:原文件总字节数
    • 参数2:当前已经下载好的字节数
    • 参数3:是否在上传,对于下载,该值为false
  17. */
  18. @Override
  19. public void onLoading(long total, long current, boolean isUploading) {
  20. Log.d("tag", "onLoading"+Thread.currentThread().getName()+"//"+
  21. "current"+current+"//"+"total="+total);
  22. }
  23. //下载成功后调用一次
  24. @Override
  25. public void onSuccess(ResponseInfo responseInfo) {
  26. Toast.makeText(MainActivity.this,
  27. "下载成功", Toast.LENGTH_SHORT).show();
  28. }
  29. //失败后调用一次
  30. @Override
  31. public void onFailure(HttpException error, String msg) {
  32. Toast.makeText(MainActivity.this,
  33. "下载失败:"+msg, Toast.LENGTH_LONG).show(); 上海黑马程序员——只要学不死,就往死里学,冲击年薪20 万! 36
  34. }
  35. });
  36. }