如果你還想了解Http的基礎知識、通過url獲取網絡數據以及GET、POST方式上傳數據到服務器,請查看我前面幾篇博文。Android中的Http通信(一)之Http協議基本知識、Android中的Http通信(二)之根據Url讀取網絡數據、Android中的Http通信(三)之get、post傳遞參數到服務器。
本文主要介紹通過多線程的方式從服務器下載數據,這和前兩篇博文相比較,增加了線程池以及本地文件讀寫。如果內心看完前面的幾篇博文,這一篇文章,就顯得很容易啦。廢話不多說,直接開幹。
老規矩,先來一個簡單的佈局,這個佈局裏面只有一個button和imageview,這裏不做任何解釋:
<RelativeLayout 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" >
<Button
android:id="@+id/download_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="下載" />
<ImageView
android:id="@+id/img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/download_btn"
android:layout_centerHorizontal="true"
android:background="@drawable/ic_launcher"
android:scaleType="centerCrop" />
</RelativeLayout>
再來看一下Activity中的代碼:
package com.example.http;
import java.io.File;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;
/**
* 文件下載
*
* @author xiaoyf
*
*/
public class MainActivity extends Activity {
private String fileName;
private ImageView img;
private Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
if (msg.what == 1) {
Bitmap bitmap = BitmapFactory.decodeFile(fileName);
img.setImageBitmap(bitmap);
}
};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
File parent = Environment.getExternalStorageDirectory();
File file = new File(parent, System.currentTimeMillis() + ".jpg");
fileName = file.getAbsolutePath();
initViews();
}
/**
* 初始化控件
*/
private void initViews() {
img = (ImageView) findViewById(R.id.img);
findViewById(R.id.download_btn).setOnClickListener(
new OnClickListener() {
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
new Thread() {
@Override
public void run() {
DownLoadFile downLoadFile = new DownLoadFile(
"http://192.168.197.2:8080/WebProject/girl.jpg",
handler, fileName);
downLoadFile.downLoad();
}
}.start();
}
});
}
}
同樣是簡單,不做任何解釋。
下面是核心代碼,下載操作都在這裏面執行:
package com.example.http;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import android.os.Handler;
/**
* 文件下載
*
* @author xiaoyf
*
*/
public class DownLoadFile {
/**
* 線程池中線程的數量
*/
private static final int THREAD_NUM = 3;
/**
* 連接超時時間、讀取超時時間
*/
private static final int TIME_OUT = 5000;
/**
* 完成的線程的數量
*/
private int complete_num;
/**
* 線程池
*/
private Executor executorThread = Executors.newFixedThreadPool(THREAD_NUM);
private String url;
private Handler handler;
private String fileName;
public DownLoadFile() {
super();
}
public DownLoadFile(String url, Handler handler, String fileName) {
super();
this.url = url;
this.handler = handler;
this.fileName = fileName;
}
public void downLoad() {
try {
URL httpUrl = new URL(url);
HttpURLConnection connection = (HttpURLConnection) httpUrl
.openConnection();
connection.setReadTimeout(TIME_OUT);
connection.setConnectTimeout(TIME_OUT);
connection.setRequestMethod("GET");
connection.setDoInput(true);
long count = connection.getContentLength();
long block = count / THREAD_NUM;
// 計算每個線程下載的起始位置和結束位置;
// 如:待下載的內容有10字節:第一個線程:0-2;第二個線程:3-5;第三個線程:6-10
for (int i = 0; i < THREAD_NUM; i++) {
long start = i * block;
long end = (i + 1) * block - 1;
if (i == THREAD_NUM - 1) {
end = count;
}
DownLoadRunnable runnable = new DownLoadRunnable(start, end);
executorThread.execute(runnable);
}
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
class DownLoadRunnable implements Runnable {
private long start;
private long end;
public DownLoadRunnable() {
}
public DownLoadRunnable(long start, long end) {
this.start = start;
this.end = end;
}
public void run() {
try {
URL httpUrl = new URL(url);
HttpURLConnection connection = (HttpURLConnection) httpUrl
.openConnection();
connection.setReadTimeout(TIME_OUT);
connection.setConnectTimeout(TIME_OUT);
connection.setRequestMethod("GET");
connection.setDoInput(true);
// 通過http的Range字段,設置從服務器請求數據的開始位置和結束位置
connection.setRequestProperty("Range", "bytes=" + start + "-"
+ end);
// 創建RandomAccessFile對象,"rwd"代表可以對文件進行讀寫和操作
RandomAccessFile accessFile = new RandomAccessFile(new File(
fileName), "rwd");
// 通過RandomAccessFile對象,設置文件開始寫入的位置
accessFile.seek(start);
InputStream in = connection.getInputStream();
//定義一個緩衝數組,大小爲2K
byte[] buffer = new byte[1024 * 2];
int len = 0;
while ((len = in.read(buffer)) != -1) {
accessFile.write(buffer, 0, len);
}
if (in != null) {
in.close();
}
if (accessFile != null) {
accessFile.close();
}
complete_num++;
if (complete_num == THREAD_NUM) {
handler.sendEmptyMessage(1);
}
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
註釋的也是相當詳細,不做過多解釋。文章最後結束的時候,會做一個流程的總結。OK,我們來看一下運行的效果如何。
看着是不是爽歪歪啊
從美女身上回來,我們最後進行一下流程總結:
1、通過給定的url獲得URL以及HttpURLConnection對象,並對HttpURLConnection對象進行基本的設置
2、根據線程池中線程的數量,計算每個線程下載的起始位置和結束位置
3、對線程進行必要的設置,其中包括通過給定的url獲得URL以及HttpURLConnection對象,並對HttpURLConnection對象進行基本的設置(特別是通過http的Range字段,設置從服務器請求數據的開始位置和結束位置)、創建RandomAccessFile對象,通過RandomAccessFile對象,設置文件開始寫入的位置、
4、啓動線程池中各個線程,進行獨立下載。
5、記錄完成的線程的數量,當完成的線程的數量和線程池中線程的數量相等時,文件下載完畢
項目下載:http://download.csdn.net/detail/u014544193/9415691