多线程下载android,Android多线程下载示例详解

本文详细介绍了如何在Android中实现多线程文件下载。从服务端准备到Android客户端的布局设计,再到自定义下载线程、下载管理器的实现,以及MainActivity中的下载逻辑,每个步骤都配有具体的代码示例。最后,文章提到了需要添加的Android权限。通过这个实例,读者可以学习到如何在Android应用中实现文件下载并更新UI进度。

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

一、概述

说到Android中的文件下载,Android API中明确要求将耗时的操作放到一个子线程中执行,文件的下载无疑是需要耗费时间的,所以要将文件的下载放到子线程中执行。下面,我们一起来实现一个Android中利用多线程下载文件的小例子。

二、服务端准备

在这个小例子中我以下载有道词典为例,在网上下载有道词典的安装包,在eclipse中新建项目web,将下载的有道词典安装包放置在WebContent目录下,并将项目发布到Tomcat中,具体如下图所示

36600dd7b0a281a56a43f07158924a7c.png

三、Android实现

1、布局

界面上自上而下放置一个TextView,用来提示文本框中输入的信息,一个文本框用来输入网络中下载文件的路径,一个Button按钮,点击下载文件,一个ProgressBar显示下载进度,一个TextView显示下载的百分比。具体布局内容如下:

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:paddingBottom="@dimen/activity_vertical_margin"

android:paddingLeft="@dimen/activity_horizontal_margin"

android:paddingRight="@dimen/activity_horizontal_margin"

android:paddingTop="@dimen/activity_vertical_margin"

android:orientation="vertical"

tools:context=".MainActivity" >

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:text="下载路径" />

android:id="@+id/ed_path"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:text="http://192.168.0.170:8080/web/youdao.exe"/>

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="下载"

android:onClick="download"/>

android:id="@+id/pb"

android:layout_width="match_parent"

android:layout_height="wrap_content"

style="@android:style/Widget.ProgressBar.Horizontal"/>

android:id="@+id/tv_info"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:gravity="center"

android:text="下载:0%"/>

2、自定义ProgressBarListener监听器接口

新建自定义ProgressBarListener监听器接口,这个接口中定义两个方法,void getMax(int length)用来获取下载文件的长度,void getDownload(int length);用来获取每次下载的长度,这个方法中主要是在多线程中调用,子线程中获取到的数据传递到这两个接口方法中,然后在这两个接口方法中通过Handler将相应的长度信息传递到主线程,更新界面显示信息,具体代码实现如下:

package com.example.inter;

/**

* 自定义进度条监听器

* @author liuyazhuang

*

*/

public interface ProgressBarListener {

/**

* 获取文件的长度

* @param length

*/

void getMax(int length);

/**

* 获取每次下载的长度

* @param length

*/

void getDownload(int length);

}

3、自定义线程类DownloadThread

这里通过继承Thread的方式来实现自定义线程操作,在这个类中主要是实现文件的下载操作,在这个类中,定义了一系列与下载有关的实例变量来控制下载的数据,同时通过自定义监听器ProgressBarListener中的void getDownload(int length)方法来跟新界面显示的进度信息。

具体实现如下:

package com.example.download;

import java.io.File;

import java.io.FileOutputStream;

import java.io.InputStream;

import java.io.RandomAccessFile;

import java.net.HttpURLConnection;

import java.net.URL;

import com.example.inter.ProgressBarListener;

/**

* 自定义线程类

* @author liuyazhuang

*

*/

public class DownloadThread extends Thread {

//下载的线程id

private int threadId;

//下载的文件路径

private String path;

//保存的文件

private File file;

//下载的进度条更新的监听器

private ProgressBarListener listener;

//每条线程下载的数据量

private int block;

//下载的开始位置

private int startPosition;

//下载的结束位置

private int endPosition;

public DownloadThread(int threadId, String path, File file, ProgressBarListener listener, int block) {

this.threadId = threadId;

this.path = path;

this.file = file;

this.listener = listener;

this.block = block;

this.startPosition = threadId * block;

this.endPosition = (threadId + 1) * block - 1;

}

@Override

public void run() {

super.run();

try {

//创建RandomAccessFile对象

RandomAccessFile accessFile = new RandomAccessFile(file, "rwd");

//跳转到开始位置

accessFile.seek(startPosition);

URL url = new URL(path);

//打开http链接

HttpURLConnection conn = (HttpURLConnection) url.openConnection();

//设置超时时间

conn.setConnectTimeout(5000);

//指定请求方式为GET方式

conn.setRequestMethod("GET");

//指定下载的位置

conn.setRequestProperty("Range", "bytes="+startPosition + "-" + endPosition);

//不用再去判断状态码是否为200

InputStream in = conn.getInputStream();

byte[] buffer = new byte[1024];

int len = 0;

while((len = in.read(buffer)) != -1){

accessFile.write(buffer, 0, len);

//更新下载进度

listener.getDownload(len);

}

accessFile.close();

in.close();

} catch (Exception e) {

// TODO: handle exception

e.printStackTrace();

}

}

}

4、新建DownloadManager类

这个类主要是对下载过程的管理,包括下载设置下载后文件要保存的位置,计算多线程中每个线程的数据下载量等等。

具体实现如下:

package com.example.download;

import java.io.File;

import java.io.RandomAccessFile;

import java.net.HttpURLConnection;

import java.net.URL;

import android.os.Environment;

import com.example.inter.ProgressBarListener;

/**

* 文件下载管理器

* @author liuyazhuang

*

*/

public class DownloadManager {

//下载线程的数量

private static final int TREAD_SIZE = 3;

private File file;

/**

* 下载文件的方法

* @param path:下载文件的路径

* @param listener:自定义的下载文件监听接口

* @throws Exception

*/

public void download(String path, ProgressBarListener listener) throws Exception{

URL url = new URL(path);

HttpURLConnection conn = (HttpURLConnection) url.openConnection();

conn.setConnectTimeout(5000);

conn.setRequestMethod("GET");

if(conn.getResponseCode() == 200){

int filesize = conn.getContentLength();

//设置进度条的最大长度

listener.getMax(filesize);

//创建一个和服务器大小一样的文件

file = new File(Environment.getExternalStorageDirectory(), this.getFileName(path));

RandomAccessFile accessFile = new RandomAccessFile(file, "rwd");

accessFile.setLength(filesize);

//要关闭RandomAccessFile对象

accessFile.close();

//计算出每条线程下载的数据量

int block = filesize % TREAD_SIZE == 0 ? (filesize / TREAD_SIZE) : (filesize / TREAD_SIZE +1 );

//开启线程下载

for(int i = 0; i < TREAD_SIZE; i++){

new DownloadThread(i, path, file, listener, block).start();

}

}

}

/**

* 截取路径中的文件名称

* @param path:要截取文件名称的路径

* @return:截取到的文件名称

*/

private String getFileName(String path){

return path.substring(path.lastIndexOf("/") + 1);

}

}

5、完善MainActivity

在这个类中首先,找到页面中的各个控件,实现Button按钮的onClick事件,在onClick事件中开启一个线程进行下载操作,同时子线程中获取到的数据,通过handler与Message机制传递到主线程,更新界面显示。

具体实现如下:

package com.example.multi;

import android.app.Activity;

import android.os.Bundle;

import android.os.Handler;

import android.os.Message;

import android.view.Menu;

import android.view.View;

import android.widget.EditText;

import android.widget.ProgressBar;

import android.widget.TextView;

import android.widget.Toast;

import com.example.download.DownloadManager;

import com.example.inter.ProgressBarListener;

/**

* MainActivity整个应用程序的入口

* @author liuyazhuang

*

*/

public class MainActivity extends Activity {

protected static final int ERROR_DOWNLOAD = 0;

protected static final int SET_PROGRESS_MAX = 1;

protected static final int UPDATE_PROGRESS = 2;

private EditText ed_path;

private ProgressBar pb;

private TextView tv_info;

private DownloadManager manager;

//handler操作

private Handler mHandler = new Handler(){

public void handleMessage(android.os.Message msg) {

switch (msg.what) {

case ERROR_DOWNLOAD:

//提示用户下载失败

Toast.makeText(MainActivity.this, "下载失败", Toast.LENGTH_SHORT).show();

break;

case SET_PROGRESS_MAX:

//得到最大值

int max = (Integer) msg.obj;

//设置进度条的最大值

pb.setMax(max);

break;

case UPDATE_PROGRESS:

//获取当前下载的长度

int currentprogress = pb.getProgress();

//获取新下载的长度

int len = (Integer) msg.obj;

//计算当前总下载长度

int crrrentTotalProgress = currentprogress + len;

pb.setProgress(crrrentTotalProgress);

//获取总大小

int maxProgress = pb.getMax();

//计算百分比

float value = (float)currentprogress / (float)maxProgress;

int percent = (int) (value * 100);

//显示下载的百分比

tv_info.setText("下载:"+percent+"%");

break;

default:

break;

}

};

};

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

this.ed_path = (EditText) super.findViewById(R.id.ed_path);

this.pb = (ProgressBar) super.findViewById(R.id.pb);

this.tv_info = (TextView) super.findViewById(R.id.tv_info);

this.manager = new DownloadManager();

}

@Override

public boolean onCreateOptionsMenu(Menu menu) {

// Inflate the menu; this adds items to the action bar if it is present.

getMenuInflater().inflate(R.menu.main, menu);

return true;

}

public void download(View v){

final String path = ed_path.getText().toString();

//下载

new Thread(new Runnable() {

@Override

public void run() {

// TODO Auto-generated method stub

try {

manager.download(path, new ProgressBarListener() {

@Override

public void getMax(int length) {

// TODO Auto-generated method stub

Message message = new Message();

message.what = SET_PROGRESS_MAX;

message.obj = length;

mHandler.sendMessage(message);

}

@Override

public void getDownload(int length) {

// TODO Auto-generated method stub

Message message = new Message();

message.what = UPDATE_PROGRESS;

message.obj = length;

mHandler.sendMessage(message);

}

});

} catch (Exception e) {

// TODO: handle exception

e.printStackTrace();

Message message = new Message();

message.what = ERROR_DOWNLOAD;

mHandler.sendMessage(message);

}

}

}).start();

}

}

6、增加权限

最后,别忘了给应用授权,这里要用到Android联网授权和向SD卡中写入文件的权限。

具体实现如下:

package="com.example.multi"

android:versionCode="1"

android:versionName="1.0" >

android:minSdkVersion="8"

android:targetSdkVersion="18" />

android:allowBackup="true"

android:icon="@drawable/ic_launcher"

android:label="@string/app_name"

android:theme="@style/AppTheme" >

android:name="com.example.multi.MainActivity"

android:label="@string/app_name" >

四、运行效果

2ef76494d586dfe0ce4839dd3b0d8ce9.png

90e7028f3bf5f1ffd02b1abc744f3a75.png

e9043e664ea789fa526fed0f11c8e996.png

提醒:大家可以到这个链接来获取完整的代码示例。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值