Android(java)学习笔记159:多线程断点下载的原理(Android实现)
之前在Android(java)学习笔记215中,我们从JavaSE的角度去实现了多线程断点下载,下面从Android角度实现这个断点下载:
1. 新建一个Android工程:
(1)其中我们先实现布局文件activity_main.xml:
<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:text="http://192.168.1.100:8080/" /> <EditText
android:inputType="number"
android:id="@+id/et_count"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请设置下载线程的数量,默认3" /> <Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="download"
android:text="下载" /> <LinearLayout
android:paddingLeft="5dip"
android:paddingRight="5dip"
android:id="@+id/ll_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
</LinearLayout> </LinearLayout>
布局效果如下:
(2)其中的进度条样式pb.xml如下:
<?xml version="1.0" encoding="utf-8"?>
<ProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/progressBar1"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="fill_parent"
android:layout_marginLeft="5dip"
android:layout_marginRight="5dip"
android:layout_height="wrap_content" />
2. MainActivity.java:
package com.itheima.mutiledownloader; import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL; import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.text.TextUtils;
import android.view.View;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.Toast; public class MainActivity extends Activity {
private EditText et_path;
private EditText et_count;
private LinearLayout ll_container;
private int threadCount = 3;
private String path;
protected int runningThreadCount; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_count = (EditText) findViewById(R.id.et_count);
et_path = (EditText) findViewById(R.id.et_path);
ll_container = (LinearLayout) findViewById(R.id.ll_container);
} public void download(View view){
String str_count = et_count.getText().toString().trim();
path = et_path.getText().toString().trim();
if(TextUtils.isEmpty(path)&&!path.startsWith("http://")){
Toast.makeText(this, "下载路径不合法", 0).show();
return;
}
if(!TextUtils.isEmpty(str_count)){
threadCount = Integer.parseInt(str_count);
}
ll_container.removeAllViews();
for(int i=0;i<threadCount;i++){
ProgressBar pb = (ProgressBar) View.inflate(this, R.layout.pb,null);
ll_container.addView(pb);
}
new Thread(){
public void run() {
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
int code = conn.getResponseCode();
if(code == 200){
int length = conn.getContentLength();
System.out.println("服务器文件的大小为:"+length);
//创建一个空白文件文件的大小和服务器资源一样
RandomAccessFile raf = new RandomAccessFile(Environment.getExternalStorageDirectory().getPath()+"/"+getFileName(path), "rw");
raf.setLength(length);
raf.close();
//每个线程下载的平均区块大小
int blocksize = length / threadCount;
runningThreadCount = threadCount;
for(int threadId = 0;threadId<threadCount;threadId++){
int startIndex = threadId*blocksize;
int endIndex = (threadId+1)*blocksize-1;
if(threadId==(threadCount-1)){//最后一个线程的修正
endIndex = length - 1;
}
new DownloadThread(startIndex, endIndex, threadId).start();
}
}
} catch (Exception e) {
e.printStackTrace();
showToast("下载失败");
} };
}.start(); }
class DownloadThread extends Thread{
/**
* 理论上第一次开始的位置
*/
int firstStartIndex;
int startIndex;
int endIndex;
/**
* 当前线程下载到文件的位置
*/
int filePosition;
int threadId;
/**
* 当前线程需要下载的总文件大小
*/
int threadTotal;
ProgressBar pb;//当前线程对应的进度条
/**
*
* @param startIndex 开始位置
* @param endIndex 结束位置
* @param threadId 线程id
*/
public DownloadThread(int startIndex, int endIndex, int threadId) {
this.startIndex = startIndex;
this.firstStartIndex = startIndex;
this.endIndex = endIndex;
threadTotal = endIndex - startIndex;
this.threadId = threadId;
pb = (ProgressBar) ll_container.getChildAt(threadId);
pb.setMax(threadTotal);
filePosition = startIndex;
} @Override
public void run() {
//System.out.println("线程:"+threadId+"理论下载的位置:"+startIndex+"~~~"+endIndex);
//读取,看看有没有下载的历史数据,读下载的进度
try {
File file = new File(Environment.getExternalStorageDirectory().getPath()+"/"+threadId+getFileName(path)+".txt");//用一个文本记录当前线程下载的进程
if(file.exists()&&file.length()>0){
FileInputStream fis = new FileInputStream(file);
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
filePosition = Integer.parseInt(br.readLine());//上一次下载到文件的哪个位子。
startIndex = filePosition;
fis.close();
}
System.out.println("线程:"+threadId+"实际上下载的位置:"+startIndex+"~~~"+endIndex);
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex);
int code = conn.getResponseCode();//2XX 成功 3XX重定向 4XX资源找不到 5XX服务器异常
if(code == 206){
InputStream is = conn.getInputStream();
RandomAccessFile raf = new RandomAccessFile(Environment.getExternalStorageDirectory().getPath()+"/"+getFileName(path), "rwd");
//一定要记得定位文件写的位置
raf.seek(startIndex);
byte[] buffer = new byte[1024*4];//缓冲区的大小
int len = -1;
while((len = is.read(buffer))!=-1){
raf.write(buffer, 0, len);
filePosition+=len;//记录写入数据的进度
int process = filePosition - firstStartIndex;
pb.setProgress(process);
RandomAccessFile rafinfo = new RandomAccessFile(file, "rwd");
rafinfo.write(String.valueOf(filePosition).getBytes());
rafinfo.close();
}
raf.close();
is.close();
System.out.println("线程:"+threadId+"下载完毕了。");
synchronized (MainActivity.this) {//加锁,同步运行,保证下面一段代码在同一个时间片运行,原子性执行
runningThreadCount--;
if(runningThreadCount==0){
System.out.println("所有的线程都下载完毕了");
showToast("下载完毕了");
for(int i =0;i<threadCount;i++){
File f = new File(Environment.getExternalStorageDirectory().getPath()+"/"+i+getFileName(path)+".txt");
System.out.println(f.delete());
}
}
} }
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 获取路径对应的文件名
* @param path
* @return
*/
private static String getFileName(String path){
int beginIndex = path.lastIndexOf("/")+1;
return path.substring(beginIndex);
} private void showToast(final String text){
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, text, 0).show();
}
});
}
}
3. 其中AndroidMainfest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.itheima.mutiledownloader"
android:versionCode="1"
android:versionName="1.0" > <uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET"/> <application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.itheima.mutiledownloader.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application> </manifest>
Android(java)学习笔记159:多线程断点下载的原理(Android实现)的更多相关文章
- 我的Android进阶之旅------>Android基于HTTP协议的多线程断点下载器的实现
一.首先写这篇文章之前,要了解实现该Android多线程断点下载器的几个知识点 1.多线程下载的原理,如下图所示 注意:由于Android移动设备和PC机的处理器还是不能相比,所以开辟的子线程建议不要 ...
- Android(java)学习笔记216:多线程断点下载的原理(Android实现)
之前在Android(java)学习笔记215中,我们从JavaSE的角度去实现了多线程断点下载,下面从Android角度实现这个断点下载: 1.新建一个Android工程: (1)其中我们先实现布局 ...
- Android(java)学习笔记215:多线程断点下载的原理(JavaSE实现)
1. 为什么需要多线程下载? 服务器的资源有限,同时的平均地分配给每个客户端.开启的线程越多抢占的服务的资源就越多,下载的速度就越块. 2. 下载速度的限制条件? (1)你的电脑手机宽带的带宽 ...
- Android(java)学习笔记158:多线程断点下载的原理(JavaSE实现)
1. 为什么需要多线程下载? 服务器的资源有限,同时的平均地分配给每个客户端.开启的线程越多抢占的服务的资源就越多,下载的速度就越块. 2. 下载速度的限制条件? (1)你的电脑手机宽带的带宽 ...
- 【原】Java学习笔记032 - 多线程
package cn.temptation; public class Sample01 { public static void main(String[] args) { /* * [进程]:正在 ...
- Java学习笔记之——多线程
多线程编程 程序: 进程:一个程序运行就会产生一个进程 线程:进程的执行流程,一个进程至少有一个线程,称为主线程 如:QQ聊着天,同时在听音乐 一个进程可以有多个线程,多个线程共享同一个进程的资源 线 ...
- Java学习笔记:多线程(一)
Java中线程的五种状态: 新建状态(New) 就绪状态(Runnable) 运行状态(Running) 阻塞状态(Blocked) 凋亡状态(Dead) 其中阻塞状态(Blocked)又分为三种: ...
- java学习笔记(5)多线程
一.简介(过段时间再写,多线程难度有点大) --------------------------------------- 1.进程:运行时的概念,运行的应用程序 2.线程:应用程序内部并发执行的代码 ...
- Java 学习笔记(11)——多线程
Java内部提供了针对多线程的支持,线程是CPU执行的最小单位,在多核CPU中使用多线程,能够做到多个任务并行执行,提高效率. 使用多线程的方法 创建Thread类的子类,并重写run方法,在需要启动 ...
随机推荐
- HTTP 请求的组成 方法 已经 请求的状态码
HTTP请求是指从客户端到服务器端的请求消息. 包括:消息首行中,对资源的请求方法.资源的标识符及使用的协议.从客户端到服务器端的请求消息包括,消息首行中,对资源的请求方法.资源的标识符及使用的协议. ...
- Docker 概念及基本用法
Docker 概念及基本用法 一. 本节内容简介 本实验中我们初步接触Docker的概念和基本用法.需要依次完成下面几项任务: 理解Docker是什么 学习如何在Linux上安装Docker 学习如何 ...
- codeforces 126B
Asterix, Obelix and their temporary buddies Suffix and Prefix has finally found the Harmony temple. ...
- hadoop2 Ubuntu 下安装部署
搭建Hadoop环境( 我以hadoop 2.7.3 为例, 系统为 64bit Ubuntu14.04 ) hadoop 2.7.3 官网下载 , 选择自己要安装的版本.注意每个版本对应两个下载选项 ...
- 在 DotNetty 中实现同步请求
一.背景 DotNetty 本身是一个优秀的网络通讯框架,不过它是基于异步事件驱动来处理另一端的响应,需要在单独的 Handler 去处理相应的返回结果.而在我们的实际使用当中,尤其是 客户端程序 基 ...
- 3DMAX 9 角色建模3 uv展开
将角色删除一半,展好uv再镜像出来,节省一半工作量(前提是对称) 添加UVW展开编辑器(Unwrap UVW),选择面 打开UV编辑器 这里要注意映射问题,默认打开UV编辑器后所选择的面是映射到与选择 ...
- c# 组件无法下断点
1. c#组件 2.断点能下,且运行中断点本身显示正常(正常实心红点,不是白底红圈) 3.运行中无法进入组件 4.主程序中,组件的实例无法被观测到,调试显示:无法计算得到这个对象 , 但是程序能运行 ...
- 安全性测试入门 (三):CSRF 跨站请求伪造攻击和防御
本篇继续对于安全性测试话题,结合DVWA进行研习. CSRF(Cross-site request forgery):跨站请求伪造 1. 跨站请求伪造攻击 CSRF则通过伪装成受信任用户的请求来利用受 ...
- mybatis使用要点(2019.5.19)
接口入参 只有一个参数,叫啥都没问题 有两个参数以上,需使用@Param,否则名字依次为0.1.2和param1.param2.param3 一般用#,防sql注入:偶尔用$,比如需要动态表名等 接口 ...
- SpringAOP和Spring事物管理
Spring AOP : Pointcut表达式: designators-指示器 wildcards-通配符 operators-操作符 wildcards: * -- 匹配任意数量的字符 + -- ...