博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
多线程
阅读量:6250 次
发布时间:2019-06-22

本文共 24299 字,大约阅读时间需要 80 分钟。

hot3.png

1. 乱码问题

1.1. GET向服务器端提交中文数据乱码

服务器端返回中文数据乱码解决:

出现原因:由于tomcat服务器编码格式是ISO8859-1,所以当返回中文的时候,会默认使用此编码。但是此编码不包含中文,所以在这个码表中找不到会到本地码表查找,本地码表是gbk,安卓客户端是以UTF-8编码格式的,所以会出现乱码。 

解决方案: 
(1) 服务器端:使用UTF-8编码

URLEncoder.encode(name, "utf-8");

(2) 服务器端:

new String(name.getBytes("iso-8859-1"),"utf-8");

1.2. POST向服务器端提交中文数据乱码

解决方法:在客户端中对中文进行URL编码

URLEncoder.encode(name, "utf-8");

1.3. 总结

不管是使用GET还是POST方式提交,解决办法都是保证服务器端和客户端使用的字符集编码一致。

2. HttpClient

2.1. HttpClient开源项目简介

HttpClient相比传统JDK自带的URLConnection,增加了易用性和灵活性,使客户顿发送http请求变得容易。HttpClient是Apache Jakarta Common下的子项目,用来提供高效的、最新的、功能丰富的支持HTTP协议的客户端编程工具包,并且它支持HTTP协议最新的版本和建议。

2.2. 使用HttpClient发送get请求

//拼接get请求路径String url = "http://192.168.14.79/Web2/servlet/LoginServlet?name=" + URLEncoder.encode(name) + "&pass=" + pass;//创建HttpClient客户端对象HttpClient client = new DefaultHttpClient();//创建Get请求对象,参数传入请求地址HttpGet get = new HttpGet(url);try {    //调用客户端的execute()方法执行请求,获取HttpResponse响应对象。Response对象中有服务器返回的信息        HttpResponse response = client.execute(get);        //获取响应中的状态行,根据状态行可以判断请求成功失败等信息        StatusLine line = response.getStatusLine();        if(line.getStatusCode() == 200){            //获去请求实体            HttpEntity entity = response.getEntity();            //从实体中获取输入流,也就是服务器返回给客户端的流信息            InputStream is = entity.getContent();            //获取流信息            String text = Tools.getTextFromStream(is);            Message msg = handler.obtainMessage();            msg.obj = text;            handler.sendMessage(msg);        }    } catch (Exception e) {        // TODO Auto-generated catch block        e.printStackTrace();    }}

运行效果: 

这里写图片描述

2.3. 使用HttpClient发送post请求

//封装请求提交的参数,创建NameValuePair对象用于封装要提交的参数String url = "http://192.168.14.79/Web2/servlet/LoginServlet";HttpClient client = new DefaultHttpClient();HttpPost post = new HttpPost(url);BasicNameValuePair bnvp1 = new BasicNameValuePair("name", name);BasicNameValuePair bnvp2 = new BasicNameValuePair("pass", pass);//创建集合用来存放封装后的提交参数List
parameters = new ArrayList
();//将封装后的提交参数存入到集合中parameters.add(bnvp1);parameters.add(bnvp2);try { //创建表单实体UrlEncodedFormEntity,参数1代表需要提交数据的集合,参数2代表编码格式 UrlEncodedFormEntity entity = new UrlEncodedFormEntity(parameters,"utf-8"); //给post对象设置请求实体,post.setEntity(entity) post.setEntity(entity); //调用client的execute(post)方法获取响应对象 HttpResponse response = client.execute(post); if(response.getStatusLine().getStatusCode() == 200){ //从响应中获取响应体然后获取其中的内容,也就是我们的输入流。 InputStream is = response.getEntity().getContent(); String text = Tools.getTextFromStream(is); Message msg = handler.obtainMessage(); msg.obj = text; handler.sendMessage(msg); }} catch (Exception e) { e.printStackTrace();}

效果如下: 

这里写图片描述

3. AsyncHttpClient

3.1. 简介以及框架下载

AsyncHttpClient是一个开源的网络请求框架,它是专门针对Android在Apache的HttpClient基础上构建的异步的callback based http client。所有的请求全在UI线程之外,而callback发生在创建它的线程中,应用了Android的Handler发送消息机制。 

下载可以在github上面下载,使用的时候直接在Android工程中导入AsyncHttpClient的jar包或者源码。

3.2. GET方式向服务器提交数据

EditText et_name = (EditText) findViewById(R.id.et_name);EditText et_pass = (EditText) findViewById(R.id.et_pass);final String name = et_name.getText().toString();final String pass = et_pass.getText().toString();String url = "http://192.168.14.79/Web2/servlet/LoginServlet";//创建AsyncHttpClient对象AsyncHttpClient client = new AsyncHttpClient();//创建RequestParams对象,封装需要提交的数据RequestParams params = new RequestParams();params.put("name", name);params.put("pass", pass);//调用AsyncHttpClient对象的get方法来提交get请求,参数1是请求Url,参数2是提交的参数,参数3是请求回调client.get(url, params, new MyHandler());

请求回调,继承自AsyncHttpResponseHandler

class MyHandler extends AsyncHttpResponseHandler{    //onSuccess()当请求成功时回调    @Override    public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {        super.onSuccess(statusCode, headers, responseBody);        Toast.makeText(MainActivity.this, new String(responseBody), 0).show();    }    //onFailuure()当请求失败时回调    @Override    public void onFailure(int statusCode, Header[] headers,byte[] responseBody, Throwable error) {        super.onFailure(statusCode, headers, responseBody, error);        Toast.makeText(MainActivity.this, "请求失败", 0).show();    }}

3.3. POST方式向服务器提交数据

//获取数据EditText et_name = (EditText) findViewById(R.id.et_name);EditText et_pass = (EditText) findViewById(R.id.et_pass);final String name = et_name.getText().toString();final String pass = et_pass.getText().toString();String url = "http://192.168.14.79/Web2/servlet/LoginServlet";AsyncHttpClient client = new AsyncHttpClient();RequestParams params = new RequestParams();params.put("name", name);params.put("pass", pass);//调用AsyncHttpClient的post方法来提交post请求,其中第一个参数是访问Url,第二个参数是提交的参数,第三个参数是请求响应回调client.post(url, params, new MyHandler());

创建请求处理类,继承自AsyncHttpResponseHandler:

class MyHandler extends AsyncHttpResponseHandler{    //当请求成功会回调onSuccess()方法    @Override    public void onSuccess(int statusCode, Header[] headers,byte[] responseBody) {        super.onSuccess(statusCode, headers, responseBody);        Toast.makeText(MainActivity.this, new String(responseBody),  0).show();    }    //当请求失败会回调onFailure()方法    @Override    public void onFailure(int statusCode, Header[] headers,byte[] responseBody, Throwable error) {        super.onFailure(statusCode, headers,responseBody, error);        Toast.makeText(MainActivity.this, "请求失败", 0).show();    }}

4. 文件上传

分析服务器端Web工程UploadFileServlet接收上传文件的代码: 

首先判断上传的数据是表单数据还是一个带文件的数据,如果是带文件的数据,拿到Servlet真实路径,创建目录,如果目录不存在则创建目录,然后利用ServletFileUpload进行上传文件。

@WebServlet("/UploadServlet")public class UploadServlet extends HttpServlet {    private static final long serialVersionUID = 1L;    public UploadServlet() {        super();    }    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {    }    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {        boolean isMultipart = ServletFileUpload.isMultipartContent(request);        //判断上传的数据是表单数据还是一个带文件的数据        if (isMultipart) {            //获取Servlet真实路径,在路径后面加上/files            String realpath =  request.getSession().getServletContext().getRealPath("/files");            //创建File对象,判断是否存在目录,如果不存在,则创建目录            File dir = new File(realpath);            if (!dir.exists())                dir.mkdirs();             FileItemFactory factory = new DiskFileItemFactory();            //创建文件上传类,设置头的编码格式为utf-8            ServletFileUpload upload = new ServletFileUpload(factory);            upload.setHeaderEncoding("UTF-8");            try {                //调用ServletFileUpLoad类的parserRequest()方法,解析请求,返回文件项的集合                List
items = upload.parseRequest(request); //遍历集合,判断FileItem的类型,如果是表单数据(26-30),我们就获取请求参数,如果是文件类型(30-35行),我们就调用FileItem的write方法,将文件上传到指定目录。 for (FileItem item : items) { if (item.isFormField()) { String name1 = item.getFieldName(); String value = item.getString("UTF-8"); System.out.println(name1 + "=" + value); } else { item.write(new File(dir,System.currentTimeMillis() + item.getName().substring(item.getName().lastIndexOf(".")))); } } } catch (Exception e) { e.printStackTrace(); } } }}

以上是服务器的代码,那手机客户端如何上传文件呢?利用httpwatch查看上传文件时请求信息,如果是自己实现文件上传比较复杂。

4.1. 利用AsyncHttpClient上传文件

这里写图片描述 

创建布局:

上传逻辑代码:

//获取EditText中输入的路径,创建File对象String path = et_path.getText().toString().trim();File file = new File(path);//判断file是否是存在并且file的长度不为0if (file.exists() && file.length() > 0) {    String uploadUrl = "http://192.168.19.28:8080/upload/UploadServlet";    //创建AsyncHttpClient对象    AsyncHttpClient client = new AsyncHttpClient();    //创建请求参数类,加入请求参数    RequestParams params = new RequestParams();    params.put("username", "james");    params.put("password", "123456");    try {        //传入file,就是我们需要上传的文件对象        params.put("profile_picture", file);    } catch (FileNotFoundException e) {        e.printStackTrace();    }    client.post(uploadUrl, params, new AsyncHttpResponseHandler() {        @Override        public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {            System.out.println("请求成功");        }        @Override        public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {        }    });}}

运行结果: 

这里写图片描述

服务器控制台输出: 

这里写图片描述

5. 多线程加速下载的原理

司马光砸缸的例子:砸的口子越多,流出的水越快。 

小细节:并不是说开的线程越多下载速度越快,如手机迅雷(简称手雷)推荐线程3-5;此外下载速度还受到真实带宽的影响。 
在单位时间内,服务器更多的cpu资源给了你,速度越快。 
那么我们如何实现多线程下载呢?下面这张图表示客户端开启3个线程去服务器端下载相对应部分的文件: 
这里写图片描述 
多线程下载步骤: 
客户端: 
(a)创建一个文件,大小和服务器文件的大小一样; 
(b)开启多个线程去下载服务器上对应部分的资源。 
这时候我们需要考虑以下几个问题: 
(c)如何等分服务器的资源? 
(d)如何创建一个大小和服务器一模一样的文件? 
(e)如何开启多个线程? 
(f)如何知道每个线程都下载完毕了?

6. JavaSE多线程下载实现

步骤: 

1. 部署服务端; 
2. 获取服务器的资源; 
3. 获取文件的大小,利用conn.getContentLength(); 
4. 创建随机文件(RandomAccessFile),大小和服务器文件大小一致; 
5. 计算出每个线程下载的大小,然后算出每个线程下载的开始位置以及结束位置; 
6. 开启多个线程去下载。

6.1. 部署服务端

运行tomcat服务器,在tomcat安装路径的ROOT目录下放置下载资源(参考:当前计算机的C:\软件\apache-tomcat-7.0.68\webapps\ROOT),如下图所示: 

这里写图片描述 
我们需要下载的资源是feiq.exe,启动tomcat,在浏览器中访问该下载资源(路径为:),效果如下图所示: 
这里写图片描述 
从上图可以看出,服务端部署完毕。

6.2. 编写下载核心代码

在Eclipse中创建Java工程,工程名为“多线程下载”,实现多线程下载文件feiq.exe。 

1. 创建工程 
【File】->【new】->【Java Project】命名为:多线程下载 
2. 使用默认包名,创建一个类MultiDownload,工程目录结构如下图所示: 
这里写图片描述 
3. 在MultiDownload类中编写下载方法。具体实现步骤如下: 
(a)联网获取网络资源,获取文件长度。

public class MutilDownLoad {    //定义服务器端资源访问路径    private static String path = "http://192.168.19.28:8080/feiq.exe";    //定义下载线程数为3    private static int threadCount = 3;     public static void main(String[] args) {        try {            //网络请求数据            URL url = new URL(path);            HttpURLConnection conn = (HttpURLConnection)url.openConnection();            conn.setRequestMethod("GET");            conn.setConnectTimeout(5000);            int code = conn.getResponseCode();            if (code == 200) {                 //通过getContentLenght()方法可以获取服务器文件的大小                int length = conn.getContentLength();                System.out.println(length);            }        } catch (Exception e) {            e.printStackTrace();        }    }}

运行结果如下图: 

这里写图片描述 
我们查看服务器上的文件详情,可以看到大小和我们网络获取得到的大小一样,如下图: 
这里写图片描述 
(b)在本地创建一个文件,大小和服务器文件大小一样。

//创建RandomAccessFile对象RandomAccessFile ras = new RandomAccessFile("temp.exe","rw");//setLength()方法设置文件的大小,传入的大小就是之前从服务器获取的文件的大小ras.setLength(length);

运行程序,可以看到工程目录下多了一个文件temp.exe,查看文件大小和我们服务器的文件大小一致,如下图: 

这里写图片描述

这里写图片描述

我们使用编辑器打开temp.exe,可以看到里面没有数据,全部为null,这样证明了我们创建的文件是一个大小和服务器一样的空文件。如下图: 

这里写图片描述

(c)定义下载线程的个数,根据文件总大小计算每个线程下载的开始位置和结束位置。 

根据推理,我们可以得出以下公式: 
这里写图片描述

获取每个线程下载的开始位置和结束位置:

public class MutilDownLoad {    private static String path = "http://192.168.19.28:8080/feiq.exe";    //定义下载的线程数为3    private static int threadCount = 3;     public static void main(String[] args) {        try {              //联网获取服务器资源              ... ...              if (code == 200) {               int length = conn.getContentLength();               //计算每个线程需要下载的长度               int blockSize = length / threadCount;               for (int i = 0; i < threadCount; i++) {                    //根据公式计算出每个线程的开始位置                    int startIndex = i * blockSize;                     //根据公式计算出每个线程的结束位置(除去最后一个线程)                    int endIndex = (i + 1) * blockSize - 1;                    //如果是最后一个线程,根据公式,该线程的结束位置为(文件总长度-1)                    if (i == threadCount - 1) {                        endIndex = length - 1;                    }                       System.out.println("线程id:"+id+"下载位置:"+startindex+"~"+endindex);                }            }        } catch (Exception e) {            e.printStackTrace();        }    }

运行结果如下图: 

这里写图片描述

(d)定义下载子线程,下载对应区域的文件,写入到创建的本地文件中。该类由于是一个子线程,所以我们需要继承Thread类。

private static class DownLoadThread extends Thread {    //定义下载路径    private String path;    //定义当前线程下载的开始位置和结束位置    private int startIndex;    private int endIndex;    //定义threadId表示当前是哪一个线程    private int threadId;    //定义构造函数    public DownLoadThread(String path, int startIndex, int endIndex, int threadId) {        this.path = path;        this.startIndex = startIndex;        this.endIndex = endIndex;        this.threadId = threadId;    }    //开启子线程下载文件    @Override    public void run() {        try {            URL url = new URL(path);            HttpURLConnection conn = (HttpURLConnection) url.openConnection();            conn.setRequestMethod("GET");            conn.setConnectTimeout(5000);            //设置Range头信息,告诉服务器每个线程下载的开始位置和结束位置;第24行,判断请求码是否是206,代表请求服务器部分资源成功,200代表请求服务器全部资源成功            conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);            int code = conn.getResponseCode();            if (code == 206) {                InputStream in = conn.getInputStream();                //创建RandomAccessFile对象,和我们之前创建的文件对接                RandomAccessFile ras = new RandomAccessFile("temp.exe", "rw");                //seek()方法设置文件写入的初始位置,由于每个线程开始下载的位置不一样,所以我们需要调用该方法指定线程下载数据开始存放的位置                ras.seek(startIndex);                int len = -1;                byte buffer[] = new byte[1024];                while ((len = in.read(buffer)) != -1) {                    ras.write(buffer, 0, len);                }                ras.close();                System.out.println("线程id" + threadId + "下载完毕了");            }        } catch (Exception e) {            e.printStackTrace();        }        super.run();    }}

(e)在我们计算每个线程下载初始位置和结束位置时,调用下载线程下载对应区域的文件数据。

for (int i = 0; i < threadCount; i++) {    int startIndex = i * blockSize;     int endIndex = (i + 1) * blockSize - 1;    if (i == threadCount - 1) {        endIndex = length - 1;    }    //创建下载线程,参数1表示下载路径,参数2表示下载文件开始位置,参数3表示下载文件结束位置,参数4表示当前线程的标识    DownLoadThread downLoadThread = new DownLoadThread(path, startIndex, endIndex, i);    //开启线程    downLoadThread.start();}

测试结果: 

这里写图片描述 
我们查看工程目录下的文件,点击可以使用,说明下载成功。如下图: 
这里写图片描述

7. 多线程下载断点续传

原理:把每次下载的位置给存起来,下次从这个位置继续下载。 

本案例和上一节多线程下载有很多相似之处,不同点在于如何断点续传,所以这里将对断点续传代码做详细解释。 
断点续传最关键的是下载的时候,要知道上次下载的位置。那么我们可以定一个变量total来记录当前线程已经下载的文件大小,每次往本地文件中写如lenth长度数据后,total也需要加上这个lenth长度变成新的total长度。此外,还需要定义一个变量来记录当前线程下载的位置currentThreadPosition,当前线程下载的位置currentThreadPosition=startIndex(初始位置)+total(已经下载的大小),将当前下载位置保存到文件中。

(a)保存当前线程下载的位置

//total表示当前下载的总大小int total = 0; while ((len = in.read(buffer)) != -1) {    ras.write(buffer, 0, len);    //每次向本地文件写入数据时,total要加上写入数据的长度len    total += len;    //计算出当前线程下载的位置    int currentThreadPosition = startIndex + total;    //创建保存当前线程下载位置的文件,getFileName(path)方法获取文件名称(见下方)    RandomAccessFile rass = new RandomAccessFile(getFileName(path) + threadId + ".txt", "rwd");      rass.write(String.valueOf(currentThreadPosition).getBytes());    rass.close();}

获取文件名称,将路径截取最后一个/获取后面的文件名:

public static String getFileName(String path) {    int start = path.lastIndexOf("/") + 1;    return path.substring(start);}

我们这边模拟下载过程中断掉下载,查看下载目录: 

这里写图片描述 
这里写图片描述 
从上图可以看出,我们第0个线程下载到的位置在4186000。那么我们下次下载的时候就需要读取文件中的这个位置,从这个位置继续下载。

(b)读取文件中下载的位置,以这个位置为开始位置,以计算的每个线程的结束位置进行第n次下载。

private static class DownLoadThread extends Thread {    @Override    public void run() {        try {            //联网获取网络数据            ......            //创建我们保存线程读取数据大小的文件对象            File file = new File(getFileName(path) + threadId + ".txt");            //如过保存线程下载位置的文件存在并且大小大于0,就说明是断点续传            if (file.exists() && file.length() > 0) {                //读取线程上次写入数据的大小                FileInputStream fis = new FileInputStream(file);                BufferedReader bfr = new BufferedReader(new InputStreamReader(fis));                String lastPosition = bfr.readLine();                 //设置当前线程下载的开始和结束位置                conn.setRequestProperty("Range", "bytes=" + lastPosition + "-" + endIndex);                startIndex = Integer.parseInt(lastPosition);                fis.close();                System.out.println("线程id真实的-" + threadId + ":" + startIndex + "----" + endIndex);            } else {                //如果是第一次开始下载,那么就从我们之前计算的开始位置和结束位置进行下载                conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);            }         //下载逻辑        ......        } catch (Exception e) {            e.printStackTrace();        }        super.run();    }}

我们测试一下,当第一次下载时,我们断一下下载,我们查看控制台输出: 

这里写图片描述

我们接下来继续下载,查看控制台输出:

这里写图片描述 

从上面可以看出,第二次下载,线程的起始位置发生了改变。

(c)下载完毕删除记录当前线程下载位置的文件。 

由于是多线程下载,我们需要知道是哪一个线程下载完成了。我们定义一个变量runningThread来记录运行的线程个数。runningThread初始化的值就是我们线程的数量,当下载完成之后runningThread自减,当runningThread小于等于0就表示全部下载完成,这时候删除记录文件。

synchronized (DownLoadThread.class) {    runningThread--;    if (runningThread <= 0) {         for (int i = 0; i < threadCount; i++) {            File deleteFile = new File(getFileName(path) + i + ".txt");            deleteFile.delete();        }    }}

由于是多线程,所以runningThread–;会有线程安全问题,所以我们需要将这句话加上同步语句。

8. Android上多线程下载

Android工程中多线程下载的逻辑和JavaSE中多线程下载逻辑类似。这里把JavaSE中已实现的功能代码移植到Android工程中,并且添加添加下载的进度条来实时查看线程下载进度。 

这里写图片描述 
这里写图片描述

界面布局:

MainActivity逻辑,EditText中输入下载线程个数,点击下载按钮开始下载,并且根据线程个数创建对应的ProgressBar。

public class MainActivity extends Activity {    //定义成员变量    ......    //线性布局用来添加ProgressBar    private LinearLayout ll_layout;    //用来存放ProgressBar集合    private List
pbs; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //初始化控件 ...... pbs = new ArrayList
(); } public void click(View v) { ...... ll_layout.removeAllViews(); pbs.clear(); //根据下载线程数量来创建ProgressBar添加到线性布局,并且添加到ProgressBar的集合 for (int i = 0; i < threadCount; i++) { ProgressBar pbBar = (ProgressBar) View.inflate(getApplicationContext(), R.layout.item, null); ll_layout.addView(pbBar); pbs.add(pbBar); } new Thread() { public void run() { try { //联网获取数据 ...... if (code == 200) { int length = conn.getContentLength(); RandomAccessFile ras = new RandomAccessFile(Environment.getExternalStorageDirectory().getPath() + "/" + getFileName(path), "rw"); ras.setLength(length); runningThread = threadCount; int blockSize = length / threadCount; for (int i = 0; i < threadCount; i++) { int startIndex = i * blockSize; int endIndex = (i + 1) * blockSize - 1; if (i == threadCount - 1) { endIndex = length - 1; } DownLoadThread downLoadThread = new DownLoadThread(path, startIndex, endIndex, i); // 开启线程 downLoadThread.start(); } } } catch (Exception e) { e.printStackTrace(); } }; }.start(); } private class DownLoadThread extends Thread { //成员变量 ...... private int pbmaxSize; private int pbCurrentSize; //构造函数 ...... @Override public void run() { try { pbmaxSize = endIndex - startIndex; //连接网络获取资源 ...... File file = new File(Environment.getExternalStorageDirectory().getPath() + "/" + getFileName(path) + threadId + ".txt"); if (file.exists() && file.length() > 0) { FileInputStream fis = new FileInputStream(file); BufferedReader bfr = new BufferedReader(new InputStreamReader(fis)); String lastPosition = bfr.readLine(); conn.setRequestProperty("Range", "bytes=" + lastPosition + "-" + endIndex); pbCurrentSize = Integer.parseInt(lastPosition) - startIndex; startIndex = Integer.parseInt(lastPosition); fis.close(); } else { conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex); } int code = conn.getResponseCode(); if (code == 206) { InputStream in = conn.getInputStream(); RandomAccessFile ras = new RandomAccessFile(Environment.getExternalStorageDirectory().getPath() + "/" + getFileName(path), "rw"); ras.seek(startIndex); int len = -1; byte buffer[] = new byte[1024 * 1024]; // 1kb int total = 0; while ((len = in.read(buffer)) != -1) { ras.write(buffer, 0, len); total += len; int currentThreadPosition = startIndex + total; RandomAccessFile rass = new RandomAccessFile(Environment.getExternalStorageDirectory().getPath() + "/" + getFileName(path) + threadId + ".txt", "rwd"); rass.write(String.valueOf(currentThreadPosition).getBytes()); rass.close(); pbs.get(threadId).setMax(pbmaxSize); pbs.get(threadId).setProgress(pbCurrentSize + total); } ras.close(); ...... //删除保存线程下载位置文件 } } catch (Exception e) { e.printStackTrace(); } super.run(); } }}

9. 使用xUtils实现多线程下载

9.1. xUtils开源项目简介及框架下载

(1)xUtils包含了很多实用的android工具。

(2)xUtils最初源于Afinal框架,进行了大量重构,使得xUtils支持大文件上传,拥有更加灵活的ORM,更多的事件注解支持且不受混淆影响。

(3)xUitls最低兼容android 2.2 (api level 8)

(4)xUtils主要有以下四大模块: 

DbUtils模块、ViewUtils模块、HttpUtils模块、BitmapUtils模块。

(5)xUtils可以在github上搜索进行下载: 

这里写图片描述 
这里写图片描述

9.2. 使用xUtils下载

界面布局:点击下载进行下载,下方progressbar显示下载进度。

下载逻辑:

public class MainActivity extends Activity {    private ProgressBar pb;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        //更新下载的进度        pb = (ProgressBar) findViewById(R.id.progressBar1);    }    public void click(View v){        //创建HttpUtils对象        HttpUtils http = new HttpUtils();        String url = "http://192.168.19.28:8080/feiq.exe";        //调用HttpUtils的download方法下载文件,第一个参数是下载的路径,第二个参数是是否支持断点续传,第三个参数是下载回调        http.download(url, "/mnt/sdcard/fei.exe", true, new RequestCallBack
() { //下载成功的回调 @Override public void onSuccess(ResponseInfo
responseInfo) { Toast.makeText(getApplicationContext(), "sucess",0).show(); } //下载失败 @Override public void onFailure(HttpException error, String msg) { } //更新当前的进度 @Override public void onLoading(long total, long current, boolean isUploading) { pb.setMax((int) total); pb.setProgress((int) current); super.onLoading(total, current, isUploading); } }); }}

运行结果: 

这里写图片描述

10. ProgressDialog的使用

点击按钮弹出ProgressDialog对话框。 

这里写图片描述

public class MainActivity extends Activity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);    }    public void click(View v) {        //创建ProgressDialog实例        final ProgressDialog dialog = new ProgressDialog(MainActivity.this);        //设置标题        dialog.setTitle("正在玩命加载ing");        //设置ProgressDialog的样式是横向的样式        dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);        new Thread() {            public void run() {                //设置ProgressDialog的最大进度                dialog.setMax(100);                for (int i = 0; i <= 100; i++) {                    try {                        Thread.sleep(50);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                    //设置ProgressDialog当前的进度                    dialog.setProgress(i);                }                // 关闭对话框                dialog.dismiss();            };        }.start();        dialog.show();    }}

转载于:https://my.oschina.net/u/2884845/blog/743395

你可能感兴趣的文章
[工具]infolite-chrome插件css插件
查看>>
javascript 深拷贝
查看>>
SwitchHosts—hosts管理利器
查看>>
【代码小记】无
查看>>
【知识点】Java机密
查看>>
如何在 Java 中正确使用 wait, notify 和 notifyAll?
查看>>
BarTender 2016表单中的“秤显示”控件
查看>>
仓库盘:动态盘点
查看>>
全面理解javascript的caller,callee,call,apply概念[转载]
查看>>
C#中使用Monitor类、Lock和Mutex类来同步多线程的执行
查看>>
Jquery 下拉框取值
查看>>
POJ 1287 Networking 【最小生成树Kruskal】
查看>>
IDEA中使用Maven创建Java Web项目
查看>>
2017.12.25
查看>>
react--1.创建项目
查看>>
C++ 与OpenCV 学习笔记
查看>>
【CV学习7】FAST算法详解
查看>>
11月20日学习内容整理:jquery插件
查看>>
预科班第四次考核总结
查看>>
【js】再谈移动端的模态框实现
查看>>