Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> [android ,11]11.android多線程斷點下載

[android ,11]11.android多線程斷點下載

編輯:關於Android編程

一、多線程下載原理:

1、在客戶端創建出來一個文件,該文件大小與服務器上的文件大小完全性相同。

①、首先要知道服務器上文件的大小,通過相應頭裡 的content-length得到文件大小。

②、使用RandomAccessFile類隨機創建一個文件 ,通過setLength方法設置文件大小。

2、開辟三個線程,進行下載,計算出每一個線程下載的數據塊大小。

3、當三個線程都運行完畢後,所有的數據都已經下載好了。

二、javase中的文件下載案例:支持斷點下載:

 

public classTestMutileDownload {

//服務器上要下載的資源的地址

public static final String path="http://192.168.1.247:8080/youdao.exe";

public static int threadcount;

 

public static void main(String[] args) throws Exception{

URL url = new URL(path);

//打開連接

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

conn.setRequestMethod("GET");

//設置請求頭,User-Agent:表示資源來自哪裡

conn.setRequestProperty("User-Agent","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");

conn.setConnectTimeout(5000);//得到服務器響應狀態碼的時間

// 獲取服務器文件的大小

int len =conn.getContentLength();

System.out.println("文件大小為"+len);

// 在本地創建出來一個大小跟服務器文件一樣大的資源

File file = newFile("youdao.exe");

//創建下載的文件對象

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

randomfile.setLength(len);

 

 

 

// 假設只開啟3個子線程

int blocksize = len/3;

for(int i =1;i<=3;i++){

threadcount = 0;

int startsize = (i-1)*blocksize;

int endsize = (i)*blocksize - 1;

 

if(i==3){

// if(endsize<=len)

endsize = len;

}

new Thread(new DownLoadTask(i, startsize, endsize)).start();

 

// 怎麼才能知道3個子線程都執行完畢了呢?

}

}

}

 

線程類:

class DownLoadTaskimplements Runnable{

//線程的id

private int id;

//要下載的文件的開始位置

private int startsize;

// 要下載的文件的結束位置

private int endsize;

//HttpURLConnection.setRequestProperty("Range","bytes=2097152-4194303");

 

public DownLoadTask(int id, int startsize, int endsize) {

this.id = id;

this.startsize =startsize;

this.endsize = endsize;

}

 

 

 

@Override

public void run() {

try {

// 每一個線程創建執行的時候 都創建一個id.txt的文件,這個文件用來記錄當前線程下載的進度

File idfile = new File(id+".txt");

// 判斷是否記錄的有下載的位置信息

if(idfile.exists()){

FileInputStream fis =new FileInputStream(idfile);

byte[] result =StreamTools.getBytes(fis);

String numberstr = newString(result);

if(numberstr!=null&&(!"".equals(numberstr))){

int startposition = Integer.parseInt( numberstr);

if(startposition>startsize){

startsize =startposition; // 重新指定下載的開始位置

}

 

}

}

 

URL url = new URL(TestMutileDownload.path);

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

conn.setRequestMethod("GET");

conn.setRequestProperty("User-Agent", "Mozilla/4.0(compatible; MSIE 6.0; Windows NT 5.1; SV1)");

conn.setConnectTimeout(5000); //得到服務器響應狀態碼的時間

// 指定當前線程從服務器上的哪個位置下載文件y

if(startsize>endsize){

startsize=endsize-1;

}

conn.setRequestProperty("Range","bytes="+startsize+"-"+endsize);

System.out.println("線程id="+id+"開始位置"+startsize+"結束位置"+endsize);

InputStream is = conn.getInputStream();

File file = new File("youdao.exe");

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

//指定指針開始的位置

randomfile.seek(startsize);

//randomfile.write(arg0, arg1, arg2);

byte[] buffer = new byte[1024];

int len = 0;

int total = 0;

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

randomfile.write(buffer,0, len);

// 記錄當前 線程下載的數據量 和對應的位置給記錄

total +=len;

FileOutputStream idfos =new FileOutputStream(idfile);

idfos.write((startsize+total+"").getBytes());// 記錄當前線程下載的位置信息

idfos.flush();

idfos.close();

}

randomfile.close();

is.close();

System.out.println("線程"+id+"下載完畢");

// //線程下載完畢後 擦屁股的操作

// if(idfile.exists()){

// idfile.delete();

// }

 

synchronized (TestMutileDownload.class) {

TestMutileDownload.threadcount++;

if(TestMutileDownload.threadcount>=3){

System.out.println("所有的線程都執行完畢了");

// 擦屁股的操作

for(int i=1;i<=3;i++){

File deletefile = newFile(i+".txt");

System.out.println(i+"刪除"+ deletefile.delete());

}

}

}

 

 

} catch (Exception e) {

e.printStackTrace();

}

 

}

}

三、android的多線程下載:

1、設置布局:

 

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:orientation="vertical">

 

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="多線程斷點下載器" />

 

android:id="@+id/et_path"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="http://192.168.1.247:8080/youdao.exe"

android:hint="請輸入下載文件的路徑" />

 

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:gravity="center_horizontal" //水平居中

android:orientation="horizontal" >

 

android:id="@+id/bt_download"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="下載" />

 

android:id="@+id/bt_stop"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="暫停" />

 

 

android:id="@+id/pb"

style="?android:attr/progressBarStyleHorizontal"//進度條的樣式.

android:layout_width="fill_parent"

android:layout_height="wrap_content" />

 

android:id="@+id/tv_progress"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="進度" />

 

 

 

2、業務代碼:

 

public class DemoActivityextends Activity implements OnClickListener {

protected static final int ERROR = 404;

public static final int DOWNLOAD_FINISH = 200;

private EditText et_path;

private Button bt_download;

private Button bt_stop;

private ProgressBar pb;

private TextView tv_progress;

public static int threadcount ;

public int total; // 當前下載的進度

public int totallen ; // 總的文件大小

 

private Handler handler = new Handler(){

 

@Override

public voidhandleMessage(Message msg) {

// TODO Auto-generated method stub

super.handleMessage(msg);

if(msg.what==ERROR){

Toast.makeText(getApplicationContext(),"獲取文件長度失敗", 0).show();

bt_download.setClickable(true);

bt_download.setEnabled(true);

return;

}

if(msg.what==DOWNLOAD_FINISH){

bt_download.setClickable(true);

bt_download.setEnabled(true);

return;

}

int process = total*100/totallen;

String strprocess = "當前進度"+process+"%";

tv_progress.setText(strprocess);

}

 

};

 

 

 

public boolean flag;

@Override

public void onCreate(BundlesavedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

et_path = (EditText)this.findViewById(R.id.et_path);

bt_download = (Button)this.findViewById(R.id.bt_download);

bt_stop = (Button)this.findViewById(R.id.bt_stop);

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

tv_progress = (TextView)this.findViewById(R.id.tv_progress);

 

//注冊按鈕的點擊事件

bt_download.setOnClickListener(this);

bt_stop.setOnClickListener(this);

 

}

@Override

public void onClick(View v) {

switch (v.getId()) {

case R.id.bt_stop:

flag = false;

bt_download.setClickable(true);

bt_download.setEnabled(true);

break;

case R.id.bt_download:

final String path = et_path.getText().toString().trim();

if("".equals(path)){

Toast.makeText(this,"路徑不能為空", 1).show();

return ;

}else{

// 開啟子線程 連接服務器 獲取文件的大小

bt_download.setClickable(false);

bt_download.setEnabled(false);

new Thread(){

public void run() {

try {

total = 0;

flag = true;

URL url = new URL(path);

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

conn.setRequestMethod("GET");

conn.setRequestProperty("User-Agent", "Mozilla/4.0(compatible; MSIE 6.0; Windows NT 5.1; SV1)");

conn.setConnectTimeout(5000); //得到服務器響應狀態碼的時間

// 獲取服務器文件的大小

totallen = conn.getContentLength();

//設置進度條的最大值

pb.setMax(totallen);

System.out.println("文件大小為"+totallen);

// 在本地創建出來一個大小跟服務器文件一樣大的資源

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

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

randomfile.setLength(totallen);

 

 

 

// 假設只開啟3個子線程

int blocksize = totallen/3;

for(int i = 1;i<=3;i++){

threadcount = 0;

int startsize =(i-1)*blocksize;

int endsize =(i)*blocksize - 1;

if(i==3){

endsize = totallen;

}

 

new Thread(newDownLoadTask(i, startsize, endsize,path)).start();

 

// 怎麼才能知道3個子線程都執行完畢了呢?

 

}

} catch (Exception e) {

e.printStackTrace();

// Toast.makeText(this, "下載出錯", 0).show();

Message msg = new Message();

msg.what = ERROR;

handler.sendMessage(msg);

}

};

}.start();

 

}

 

 

break;

}

 

}

 

class DownLoadTask implements Runnable{

//線程的id

private int id;

//要下載的文件的開始位置

private int startsize;

// 要下載的文件的結束位置

private int endsize;

//HttpURLConnection.setRequestProperty("Range","bytes=2097152-4194303");

private String path;

 

public DownLoadTask(intid, int startsize, int endsize,String path) {

this.id = id;

this.startsize = startsize;

this.endsize = endsize;

this.path = path;

}

 

 

 

@Override

public void run() {

try {

// 每一個線程創建執行的時候 都創建一個id.txt的文件,這個文件用來記錄當前線程下載的進度

File idfile = newFile("/mnt/sdcard/"+id+".txt");

// 判斷是否記錄的有下載的位置信息

if(idfile.exists()){

FileInputStream fis = new FileInputStream(idfile);

byte[] result = StreamTools.getBytes(fis);

String numberstr = new String(result);

if(numberstr!=null&&(!"".equals(numberstr))){

int startposition =Integer.parseInt( numberstr); // 從文件裡面獲取到的位置信息

if(startposition>startsize){

int currentposition = startposition - startsize; // 當前線程已經下載的數據的大小

setProgreebarProgress(currentposition);

 

handler.sendEmptyMessage(0);

// handler.post(r);

startsize = startposition; // 重新指定下載的開始位置

}

 

}

}

 

URL url = new URL(path);

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

conn.setRequestMethod("GET");

conn.setRequestProperty("User-Agent","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");

conn.setConnectTimeout(5000);//得到服務器響應狀態碼的時間

// 指定當前線程從服務器上的哪個位置下載文件y

if(startsize>endsize){

startsize=endsize-1;

}

conn.setRequestProperty("Range","bytes="+startsize+"-"+endsize);

System.out.println("線程id="+id+"開始位置"+startsize+"結束位置"+endsize);

InputStream is =conn.getInputStream();

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

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

randomfile.seek(startsize);

//randomfile.write(arg0,arg1, arg2);

byte[] buffer = newbyte[1024];

int len = 0;

int total = 0;

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

 

randomfile.write(buffer, 0, len);

// 記錄當前 線程下載的數據量 和對應的位置給記錄

total +=len;

setProgreebarProgress(len);

handler.sendEmptyMessage(0);

FileOutputStream idfos = new FileOutputStream(idfile);

idfos.write((startsize+total+"").getBytes()); // 記錄當前線程下載的位置信息

idfos.flush();

idfos.close();

if(!flag){

return ;

}

 

}

randomfile.close();

is.close();

System.out.println("線程"+id+"下載完畢");

// //線程下載完畢後 擦屁股的操作

// if(idfile.exists()){

// idfile.delete();

// }

 

synchronized (DemoActivity.this){

threadcount ++;

if(threadcount>=3){

System.out.println("所有的線程都執行完畢了");

// 擦屁股的操作

for(inti=1;i<=3;i++){

File deletefile = newFile("/mnt/sdcard/"+i+".txt");

System.out.println(i+"刪除"+ deletefile.delete());

}

//通知主線程 bt_download.setClickable(true);

//bt_download.setEnabled(true);

Message msg = newMessage();

msg.what =DOWNLOAD_FINISH;

handler.sendMessage(msg);

}

}

 

 

} catch (Exception e) {

e.printStackTrace();

}

 

}

}

 

private synchronized void setProgreebarProgress(int len){

total += len;

pb.setProgress(total);

// 第二種做法 就是每次得到下載的進度後 把數據存到文件裡面

}

 

 

public String getFileName(String path){

int start =path.lastIndexOf("/")+1;

returnpath.substring(start);

 

}

}

 

只有創建View對象的線程,才可以更新view對象裡的內容,

其實所有的view對象都是在主線程裡面創建的 ,線程的名字叫main。

所用的與ui相關的界面都是在主線程裡面創建的。

 

更新view顯示基本原理:

當子線程要更新view裡的內容時,就讓子線程發送一個消息給主線程,主線程再根據消息的內容進行操作。

主線程裡面有消息隊列(message queue),可以存放一組消息。並且還有一個輪詢器,定期的輪詢消息隊列,查看是否有消息。如果發現有消息,;輪詢器會把消息取出來。

在主線程中創建消息的處理者handler對象,用於處理在消息隊列中取出的消息。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved