Android基础第六篇(下)

本文详述了Android中处理乱码问题,包括GET和POST提交数据时的解决策略。介绍了HttpClient和AsyncHttpClient的使用,包括发送GET、POST请求。讲解了文件上传的实现,多线程下载的原理与JavaSE实现,以及Android环境下多线程下载的逻辑。最后提到了xUtils框架在多线程下载中的应用和ProgressDialog的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转载请标明出处:
http://blog.youkuaiyun.com/gj782128729/article/details/52398040
本文出自:【高境的博客】

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<NameValuePair> parameters = new ArrayList<NameValuePair>();
//将封装后的提交参数存入到集合中
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<FileItem> 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上传文件

这里写图片描述
创建布局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity" >

    <EditText
        android:id="@+id/et_path"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入上传文件的路径" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="click"
        android:text="上传" />

</LinearLayout>

上传逻辑代码:

//获取EditText中输入的路径,创建File对象
String path = et_path.getText().toString().trim();
File file = new File(path);
//判断file是否是存在并且file的长度不为0
if (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,在浏览器中访问该下载资源(路径为:http://localhost:8080/feiq.exe),效果如下图所示:
这里写图片描述
从上图可以看出,服务端部署完毕。

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工程中,并且添加添加下载的进度条来实时查看线程下载进度。
这里写图片描述
这里写图片描述

界面布局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity" >

    <EditText
        android:id="@+id/et_path"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入上传文件的路径" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="click"
        android:text="上传" />
</LinearLayout>

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

public class MainActivity extends Activity {
    //定义成员变量
    ......
    //线性布局用来添加ProgressBar
    private LinearLayout ll_layout;
    //用来存放ProgressBar集合
    private List<ProgressBar> pbs;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
         //初始化控件
         ......
        pbs = new ArrayList<ProgressBar>();
    }
    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显示下载进度。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity" >

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="click"
        android:text="下载" />

    <ProgressBar
        android:id="@+id/progressBar1"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</LinearLayout>

下载逻辑:

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<File>() {
            //下载成功的回调
            @Override
            public void onSuccess(ResponseInfo<File> 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();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值