使用HttpURLConnection下载
工具类
public class DownUtil {
//定义下载资源的路径
private String path ;
//指定所下载的文件的保存位置
private String targetFile ;
//定义需要使用多少线程下载资源
private int threadNum ;
//定义下载的线程对象
private DownThread[] threads ;
//定义下载的文件的总大小
private int fileSize ;
public DownUtil(String path , String targetFile , int threadNum){
this.path = path ;
this.threadNum = threadNum ;
threads = new DownThread[threadNum ];
this.targetFile = targetFile ;
}
public void download() throws Exception {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5 * 1000);
conn.setRequestMethod("GET");
conn.setRequestProperty(
"Accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg, "
+ "application/x-shockwave-flash, application/xaml+xml, "
+ "application/vnd.ms-xpsdocument, application/x-ms-xbap, "
+ "application/x-ms-application, application/vnd.ms-excel, "
+ "application/vnd.ms-powerpoint, application/msword, */*");
conn.setRequestProperty("Accept-Language", "zh-CN");
conn.setRequestProperty("Charset", "UTF-8");
conn.setRequestProperty("Connection", "Keep-Alive");
//得到文件大小
fileSize = conn.getContentLength();
conn.disconnect();
int currentPartSize = fileSize / threadNum + 1 ;
RandomAccessFile file = new RandomAccessFile(targetFile , "rw");
//设置本地文件的大小
file.setLength(fileSize);
file.close();
for (int i = 0 ; i < threadNum ; i++ ){
//计算每条线程的下载开始位置
int startPos = i * currentPartSize ;
//每个线程使用一个 RandomAccessFile 进行下载
RandomAccessFile currentPart = new RandomAccessFile(targetFile , "re");
//定位该线程的下载位置
currentPart.seek(startPos);
//创建下载线程
threads[i].start();
}
}
//获取下载的完成百分比
public double getCompleteRate(){
//统计多条线程已经下载的总大小
int sunSize = 0 ;
for (int i = 0 ; i <threadNum ; i++){
sunSize += threads[i].lenght ;
}
//返回已经完成的百分比
return sunSize*1.0/fileSize ;
}
private class DownThread extends Thread{
//当前线程下载位置
private int startPos ;
//定义当前线程负责下载文件的大小
private int currentPartSize ;
//当前线程需要下载的文件块
private RandomAccessFile currentPart ;
//定义该线程已经下载的字节数
int lenght ;
DownThread(int startPos , int currentPartSize , RandomAccessFile currentPart){
this.startPos = startPos ;
this.currentPartSize = currentPartSize ;
this.currentPart = currentPart ;
}
@Override
public void run() {
try{
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//设置建立连接的超时时间
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
//设置传递数据的超时时间
// conn.setReadTimeout(10000);
conn.setRequestProperty("Accept",
"image/gif, image/jpeg, image/pjpeg, image/pjpeg, "
+ "application/x-shockwave-flash, application/xaml+xml, "
+ "application/vnd.ms-xpsdocument, application/x-ms-xbap, "
+ "application/x-ms-application, application/vnd.ms-excel, "
+ "application/vnd.ms-powerpoint, application/msword, */*");
conn.setRequestProperty("Accept-Language", "zh-CN");
conn.setRequestProperty("Charset", "UTF-8");
InputStream inputStream = conn.getInputStream();
// 跳过startPos个字节,表明该线程只下载自己负责的那部分文件
inputStream.skip(this.startPos);
byte[] buffer = new byte[1024];
int hasRead = 0 ;
//读取网络数据,写入本地文件
while (lenght < currentPartSize &&(hasRead = inputStream.read(buffer)) > 0){
currentPart.write(buffer , 0 , hasRead);
lenght += hasRead ;
}
currentPart.close();
inputStream.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
// 定义一个为InputStream跳过bytes字节的方法
private static void skipFully(InputStream in , long bytes) throws IOException {
long remainning = bytes ;
long len = 0 ;
while (remainning > 0){
len = in.skip(remainning);
remainning -= len ;
}
}
}
代码部分
public class MainActivity extends AppCompatActivity {
private EditText url_edit , path_edit ;
private Button down_btn ;
private ProgressBar progressBar ;
private DownUtil downUtil ;
private MyHandler myHandler ;
@RequiresApi(api = Build.VERSION_CODES.M)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
myHandler = new MyHandler();
//动态添加权限
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},0x456);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == 0x456 && grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
Toast.makeText(this, "获取写入内存卡权限成功", Toast.LENGTH_SHORT).show();
}
}
private void init() {
progressBar = findViewById(R.id.progressBar) ;
url_edit = findViewById(R.id.url_edit) ;
path_edit = findViewById(R.id.path_edit) ;
down_btn = findViewById(R.id.down_btn) ;
down_btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
downUtil = new DownUtil(url_edit.getText().toString(),path_edit.getText().toString(),6);
new Thread(new Runnable() {
@Override
public void run() {
try {
//开始下载
downUtil.download();
} catch (Exception e) {
e.printStackTrace();
}
final Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
//获取下载任务的完成比例
double completeRate = downUtil.getCompleteRate();
Log.e("下载进度",completeRate+"");
Message message = new Message();
message.what = 101 ;
message.arg1 = (int) (completeRate*100);
//发送消息更新进度条 progressBar
myHandler.sendMessage(message);
//下载完成后取消进度任务
if(completeRate >= 1){
timer.cancel();
}
}
},0,100);
}
}).start();
}
});
}
class MyHandler extends Handler{
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what){
case 101 :
progressBar.setProgress(msg.arg1);
break;
}
}
}
}
网络权限以及向SD卡写入数据权限
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
为了让该APP使用 HTTP 协议执行下载,还要为 AndroidManifest.xml 的<application…/>元素增加以下属性:
<application
android:allowClearUserDataOnFailedRestore="true" />