android 多线程断点下载实现
原理:
在发出请求协议的时候加上参数Range,可以定位下载文件的开始位置和结束位置,下载数据的时候,记录每条线程已经下载的数据量和线程id,现在文件的url作为下载文件的一个标识,对下载信息做持久化,可以存入数据库,文件,网络等,本例一sqlite数据库做的,当开始下载文件的时候,检查数据库没有没这个下载地址的下载信息,没有的话,就开始下载,如果有这个下载地址的信息,就计算他的线程数,如果之前下载的线程数与当前下载任务的线程数一致的话.重新开始下载.如果有下载信息,并且线程数一样的话,发送请求的时候可以使用上面的Range参数,他的对应值就是开始下载的位置和结束下载的位置,如:conn.setRequestProperty("Range", "bytes=" + startPosition + "-" + endPosition);如果想尽快的读懂下面的代码,最好知道实现本例的原理,下面代码实现
主页面activity,注意主线程(ui线程)的画面数据有子线程提供的实现
[java]
package com.itcast.activity;
import java.io.File;
import java.io.IOException;
import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.itcast.download.DownloadPropressListener;
import com.itcast.download.FileDownLoader;
import com.itcast.download.R;
public class MainActivity extends Activity {
private EditText downloadPath;
private ProgressBar progressBar;
private TextView resultText;
private Button download;
private Button stop;
private FileDownLoader downLoader;
// 当handler被创建时,会关联到创建他的当前线程的消息队列,该类用以往消息队列发送消息,消息由当前的线程内部进行处理,
// 消息的运行代码是运行在ui线程里的,r如果想让handler在处理消息的时候执行自己想要的代码,可以重写它的handleMessage方法
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {// 判断消息id
case 1:// 消息代號
int downloadSize = msg.getData().getInt("downloadSize");// 获取当前所有线程下载的数据量
progressBar.setProgress(downloadSize);// 设置进度条的进度值
float num = (float) progressBar.getProgress() / (float) progressBar.getMax();// 下载数据量的百分比
int result = (int) (num * 100);
resultText.setText(result + "%");// 用TextView显示下载的百分比
if (progressBar.getProgress() == progressBar.getMax()) {// 如果现在完成,提示下载完成
Toast.makeText(MainActivity.this, R.string.success, Toast.LENGTH_SHORT).show();
download.setEnabled(true);// 下载完成后下载按钮处于活动妆太浓
stop.setEnabled(false);// 停止按钮不能被点击,变灰
}
break;
case -1:// 消息id-1
Toast.makeText(MainActivity.this, R.string.error, Toast.LENGTH_SHORT).show();// 如果下载过程中出现错误
break;
}
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
downloadPath = (EditText) findViewById(R.id.urlPath);
resultText = (TextView) findViewById(R.id.result);
progressBar = (ProgressBar) findViewById(R.id.progress);
download = (Button) findViewById(R.id.down);
stop = (Button) findViewById(R.id.stop);
ButtonClickListener buttonListener = new ButtonClickListener();// 点击事件的处理类
download.setOnClickListener(buttonListener);
stop.setOnClickListener(buttonListener);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
/**
* 主线程(ui线程) Responsive(应用程序响应)<br>
* <br>
* 1,在正常情况下,Android程序会在一条单线程里运行。如果Activity要处理一件比较耗时的工作,应该交给子线程完成,
* 否侧会因为主线程长时间等待被阻塞 , 后面的用户输入事件因没能在5秒内响应,导致应用出现ANR对话框(ANR(Application No
* Response) 对话框) <br>
* <br>
* 2,若果用了子线程,该方法编译没错,但是运行起来会有错,因为这个方法是执行的代码是另外开的子线程中,所有的任务都在子线程中完成 ,
* 这个代码只是走了一遍代码, 可能不到一秒就跑完了,但是真正的下载任务还在子线程中进行,根据java规范,一旦方法运行完了,那么他的属性也就消失了,
* 这样子线程中就没法在使用创建线程的方法传入的参数了,为了方便使用,应该把两个参数设置为final,这样传递的就是在是对象的引用了,而是直接传值<br>
* <br>
* 3,因为用了子线程,而主线程(ui线程)中的控件只能由主线程来控制,如实用
补充:移动开发 , Android ,