網(wǎng)絡編程
- Java基礎:網(wǎng)絡編程
- Uri瞒大、URL劝贸、UriMatcher碍论、ContentUris詳解
- Android應用開發(fā):網(wǎng)絡編程1
- Android應用開發(fā):網(wǎng)絡編程2
1. 使用HttpClient發(fā)送get請求
HttpClient是Apache開發(fā)的第三方框架,Google把它封裝到了Android API中赐劣,用于發(fā)送HTTP請求嫉拐。
在Android.jar包中,可以看到有很多java的API魁兼,這些都是被Android改寫的API婉徘,也可以看到Android封裝了大量的Apache API。
示例:res\layout\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"
tools:context=".MainActivity"
android:orientation="vertical">
<EditText
android:id="@+id/et_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<EditText
android:id="@+id/et_pass"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="get登陸"
android:onClick="click1"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="post登陸"
android:onClick="click2"
/>
</LinearLayout>
src/cn.itcast.getmethod/MainActivity.java
package cn.itcast.getmethod;
import java.io.InputStream;
import java.net.URLEncoder;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import cn.itcast.getmethod.tool.Tools;
public class MainActivity extends Activity {
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
Toast.makeText(MainActivity.this, (String)msg.obj, 0).show();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void click1(View v){
EditText et_name = (EditText)findViewById(R.id.et_name);
EditText et_pass = (EditText)findViewById(R.id.et_pass);
String name = et_name.getText().toString();
String pass = et_pass.getText().toString();
final String path = "http://192.168.1.100:8080/Web/servlet/Login?name=" + URLEncoder.encode(name) + "&pass=" + pass;
Thread t = new Thread(){
@Override
public void run() {
//1. 創(chuàng)建客戶端對象
//HttpClient是一個接口咐汞,不要new一個HttpClient對象盖呼,否則要實現(xiàn)很多的方法
HttpClient client = new DefaultHttpClient();
//2. 創(chuàng)建Http GET請求對象
HttpGet get = new HttpGet(path);
try {
//3. 使用客戶端發(fā)送get請求
HttpResponse response = client.execute(get);
//獲取狀態(tài)行
StatusLine line = response.getStatusLine();
//從狀態(tài)行中拿到狀態(tài)碼
if(line.getStatusCode() == 200){
//獲取實體,實體里存放的是服務器返回的數(shù)據(jù)的相關信息
HttpEntity entity = response.getEntity();
//獲取服務器返回的輸入流
InputStream is = entity.getContent();
String text = Tools.getTextFromStream(is);
//發(fā)送消息化撕,讓主線程刷新UI
Message msg = handler.obtainMessage();
msg.obj = text;
handler.sendMessage(msg);
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
t.start();
}
}
添加權限:
點擊get登陸按鈕几晤,運行結果:
2. 使用HttpClient發(fā)送post請求
src/cn.itcast.postmethod/MainActivity.java
package cn.itcast.postmethod;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import cn.itcast.getmethod.R;
import cn.itcast.getmethod.tool.Tools;
public class MainActivity extends Activity {
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
Toast.makeText(MainActivity.this, (String)msg.obj, 0).show();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void click2(View v){
EditText et_name = (EditText)findViewById(R.id.et_name);
EditText et_pass = (EditText)findViewById(R.id.et_pass);
final String name = et_name.getText().toString();
final String pass = et_pass.getText().toString();
final String path = "http://:8080/Web/servlet/Login";
Thread t = new Thread(){
@Override
public void run() {
//1. 創(chuàng)建客戶端對象
HttpClient client = new DefaultHttpClient();
//2. 創(chuàng)建Http POST請求對象
HttpPost post = new HttpPost(path);
try{
//通過此集合封裝要提交的數(shù)據(jù)
List<NameValuePair> parameters = new ArrayList<NameValuePair>();
//集合的泛型是BasicNameValuePair類型,那么由此可以推算出植阴,要提交的數(shù)據(jù)是封裝在BasicNameValuePair對象中
BasicNameValuePair bvp1 = new BasicNameValuePair("name", name);
BasicNameValuePair bvp2 = new BasicNameValuePair("pass", pass);
parameters.add(bvp1);
parameters.add(bvp2);
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(parameters,"utf-8");
//把實體類封裝至post請求中蟹瘾,提交post請求時,實體中的數(shù)據(jù)就會用輸出流寫給服務器
post.setEntity(entity);
//客戶端發(fā)送post請求
HttpResponse response = client.execute(post);
//獲取狀態(tài)行
StatusLine line = response.getStatusLine();
//從狀態(tài)行中拿到狀態(tài)碼
if(line.getStatusCode() == 200){
//獲取實體掠手,實體里存放的是服務器返回的數(shù)據(jù)的相關信息
HttpEntity et = response.getEntity();
//獲取服務器返回的輸入流
InputStream is = et.getContent();
String text = Tools.getTextFromStream(is);
//發(fā)送消息憾朴,讓主線程刷新UI
Message msg = handler.obtainMessage();
msg.obj = text;
handler.sendMessage(msg);
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
t.start();
}
}
點擊post登陸按鈕,運行結果:
3. 異步HttpClient框架
從github上下載android-async-http-master開源jar包喷鸽,拷貝library/src/main/java目錄下的內容到我們自己的項目中众雷。
拷貝后,發(fā)現(xiàn)有錯誤魁衙,這是由于Base64.java中的BuildConfig類導包問題报腔,Ctrl+Shift+O自動導包即可修復株搔。
使用異步HttpClient框架實現(xiàn)上面示例中的功能剖淀,activity.xml與上面的示例相同,修改MainActivity.java代碼纤房。
src/cn.itcast.asynchttpclient/MainActivity.java
package cn.itcast.asynchttpclient;
import org.apache.http.Header;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.AsyncHttpResponseHandler;
import com.loopj.android.http.RequestParams;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void click1(View v){
EditText et_name = (EditText)findViewById(R.id.et_name);
EditText et_pass = (EditText)findViewById(R.id.et_pass);
final String name = et_name.getText().toString();
final String pass = et_pass.getText().toString();
final String path = "http://192.168.1.100:8080/Web/servlet/Login";
//使用異步HttpClient發(fā)送get請求
AsyncHttpClient client = new AsyncHttpClient();
//定義一個請求參數(shù)對象纵隔,封裝要提交的數(shù)據(jù)
RequestParams rp = new RequestParams();
rp.add("name", name);
rp.add("pass", pass);
//發(fā)送get請求
client.get(path, rp, new MyResponseHandler());
}
public void click2(View v){
EditText et_name = (EditText)findViewById(R.id.et_name);
EditText et_pass = (EditText)findViewById(R.id.et_pass);
final String name = et_name.getText().toString();
final String pass = et_pass.getText().toString();
final String path = "http://192.168.1.100:8080/Web/servlet/Login";
AsyncHttpClient client = new AsyncHttpClient();
RequestParams rp = new RequestParams();
rp.add("name", name);
rp.add("pass", pass);
//發(fā)送post請求
client.post(path, rp, new MyResponseHandler());
}
class MyResponseHandler extends AsyncHttpResponseHandler{
//請求成功時(響應碼為200開頭),此方法調用
//登陸成功或者登錄失敗,只要請求成功捌刮,都會調用onSuccess方法
@Override
public void onSuccess(int statusCode, Header[] headers,
byte[] responseBody) {
Toast.makeText(MainActivity.this, new String(responseBody), 0).show();
}
//請求失敗時(響應碼非200開頭)調用
@Override
public void onFailure(int statusCode, Header[] headers,
byte[] responseBody, Throwable error) {
//請求不成功碰煌,也顯示登錄失敗
Toast.makeText(MainActivity.this, "登陸失敗", 0).show();
}
}
}
添加權限:
運行結果:分別點擊“get登陸”和“post登陸”按鈕。
4. 多線程下載的原理和過程
斷點續(xù)傳:上次下載到哪绅作,這次就從哪開始下芦圾。
多線程:下載速度更快。
原理:搶占服務器資源俄认。例如:帶寬為20M/s个少,3個人去下載同一部電影,那么每人分別占6.66M/s帶寬眯杏。如果有一人A開了3個線程同時下載夜焦,那么5個線程,各占4M/s帶寬岂贩,那么A所占帶寬就是4*3=12M/s茫经,其他兩人各占4M/s帶寬。也就是說A搶占了更多的服務器資源萎津。
多線程下載示例說明:
例如有一個10KB的文件卸伞,分成010,3個線程去下載,第0個線程下載02择镇,也就是3KB數(shù)據(jù)夹囚,第1個線程下載35,也就是3KB數(shù)據(jù)弃酌,余下的69,4KB的數(shù)據(jù)由最后一個線程下載儡炼。
總結出公式就是:
每個線程下載的數(shù)據(jù)開始點:threadId*size妓湘,結束點:(threadId + 1) * size -1。
最后一個線程除外乌询,下載結束點:length - 1榜贴。
計算每條線程的下載區(qū)間
多線程斷點續(xù)傳的API全部都是Java API,Java項目測試比較容易妹田,所以唬党,我們先創(chuàng)建一個Java項目。
將待下載的資源放入Tomcat服務器中鬼佣。
src/cn.itcast.MultiDownLoad/Main.java
package cn.itcast.MultiDownLoad;
import java.net.HttpURLConnection;
import java.net.URL;
public class Main {
static int threadCount = 3;
static String path = "http://localhost:8080/QQPlayer.exe";
public static void main(String[] args) {
URL url;
try {
url = new URL(path);
//打開連接對象驶拱,做初始化設置
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setConnectTimeout(8000);
conn.setReadTimeout(8000);
if(conn.getResponseCode() == 200){
//獲取要下載的目標文件的總長度
int length = conn.getContentLength();
//計算每條線程要下載的長度
int size = length / threadCount;
System.out.println("size:" + size);
//計算每條線程下載的開始位置和結束位置
for(int threadId = 0; threadId < threadCount; threadId++){
int startIndex = threadId * size;
int endIndex = (threadId + 1) * size - 1;
//如果是最后一條線程,那么需要把余數(shù)也一塊下載
if(threadId == threadCount - 1){
endIndex = length - 1;
}
System.out.println("線程" + threadId + "晶衷,下載區(qū)間為:" + startIndex + "-" + endIndex);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
運行結果:
創(chuàng)建臨時文件
src/cn.itcast.MultiDownLoad/Main.java
package cn.itcast.MultiDownLoad;
import java.io.File;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
public class Main {
static int threadCount = 3;
static String path = "http://localhost:8080/QQPlayer.exe";
public static void main(String[] args) {
URL url;
try {
url = new URL(path);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setConnectTimeout(8000);
conn.setReadTimeout(8000);
if(conn.getResponseCode() == 200){
int length = conn.getContentLength();
int size = length / threadCount;
System.out.println("size:" + size);
//創(chuàng)建一個與目標文件大小一致的臨時文件
File file = new File(getFileNameFromPath(path));
//打開文件的訪問模式設置為rwd蓝纲,表示除了讀取和寫入阴孟,還要求對文件內容的每個更新都同步寫入到底層存儲設備。
//設計到下載的程序税迷,文件訪問模式一定要使用rwd永丝,不經過緩沖區(qū),直接寫入硬盤箭养。
//如果下載到的數(shù)據(jù)讓寫入到緩沖區(qū)慕嚷,一旦斷電,緩沖區(qū)數(shù)據(jù)丟失毕泌,并且下次服務器斷點續(xù)傳也不會再傳輸這部分數(shù)據(jù)闯冷,那么下載的文件就不能用了
RandomAccessFile raf = new RandomAccessFile(file, "rwd");
//設置臨時文件的大小
raf.setLength(length);
for(int threadId = 0; threadId < threadCount; threadId++){
int startIndex = threadId * size;
int endIndex = (threadId + 1) * size - 1;
if(threadId == threadCount - 1){
endIndex = length - 1;
}
System.out.println("線程" + threadId + ",下載區(qū)間為:" + startIndex + "-" + endIndex);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static String getFileNameFromPath(String path){
int index = path.lastIndexOf("/");
return path.substring(index + 1);
}
}
開啟多個線程下載文件
src/cn.itcast.MultiDownLoad/Main.java
package cn.itcast.MultiDownLoad;
import java.io.File;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
public class Main {
static int threadCount = 3;
static String path = "http://localhost:8080/QQPlayer.exe";
public static void main(String[] args) {
URL url;
try {
url = new URL(path);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setConnectTimeout(8000);
conn.setReadTimeout(8000);
if(conn.getResponseCode() == 200){
int length = conn.getContentLength();
int size = length / threadCount;
System.out.println("size:" + size);
for(int threadId = 0; threadId < threadCount; threadId++){
int startIndex = threadId * size;
int endIndex = (threadId + 1) * size - 1;
if(threadId == threadCount - 1){
endIndex = length - 1;
}
System.out.println("線程" + threadId + "懈词,下載區(qū)間為:" + startIndex + "-" + endIndex);
DownLoadThread dt = new DownLoadThread(threadId, startIndex, endIndex);
dt.start();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static String getFileNameFromPath(String path){
int index = path.lastIndexOf("/");
return path.substring(index + 1);
}
}
class DownLoadThread extends Thread{
int threadId;
int startIndex;
int endIndex;
public DownLoadThread(int threadId, int startIndex, int endIndex) {
super();
this.threadId = threadId;
this.startIndex = startIndex;
this.endIndex = endIndex;
}
public void run(){
URL url;
try{
url = new URL(Main.path);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setConnectTimeout(8000);
conn.setReadTimeout(8000);
//Range表示指定請求的數(shù)據(jù)區(qū)間
conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);
//請求部分數(shù)據(jù)蛇耀,返回的是206
if(conn.getResponseCode() == 206){
InputStream is = conn.getInputStream();
//打開臨時文件的IO流
File file = new File(Main.getFileNameFromPath(Main.path));
RandomAccessFile raf = new RandomAccessFile(file, "rwd");
//修改寫入臨時文件的開始位置
raf.seek(startIndex);
byte[] b = new byte[1024];
int len = 0;
//當前線程下載的總進度
int total = 0;
while((len = is.read(b)) != -1){
//把讀取到的字節(jié)寫入臨時文件中
raf.write(b, 0, len);
total += len;
System.out.println("線程" + threadId + "下載的進度為:" + total);
}
raf.close();
}
System.out.println("線程" + threadId + "下載完畢---------------------");
}catch(Exception e){
e.printStackTrace();
}
}
}
運行結果:刷新,即可看到文件已經下載好了
創(chuàng)建進度臨時文件保存下載進度
src/cn.itcast.MultiDownLoad/Main.java
package cn.itcast.MultiDownLoad;
import java.io.File;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
public class Main {
static int threadCount = 3;
static String path = "http://localhost:8080/QQPlayer.exe";
public static void main(String[] args) {
URL url;
try {
url = new URL(path);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setConnectTimeout(8000);
conn.setReadTimeout(8000);
if(conn.getResponseCode() == 200){
int length = conn.getContentLength();
int size = length / threadCount;
System.out.println("size:" + size);
for(int threadId = 0; threadId < threadCount; threadId++){
int startIndex = threadId * size;
int endIndex = (threadId + 1) * size - 1;
if(threadId == threadCount - 1){
endIndex = length - 1;
}
System.out.println("線程" + threadId + "坎弯,下載區(qū)間為:" + startIndex + "-" + endIndex);
DownLoadThread dt = new DownLoadThread(threadId, startIndex, endIndex);
dt.start();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static String getFileNameFromPath(String path){
int index = path.lastIndexOf("/");
return path.substring(index + 1);
}
}
class DownLoadThread extends Thread{
int threadId;
int startIndex;
int endIndex;
public DownLoadThread(int threadId, int startIndex, int endIndex) {
super();
this.threadId = threadId;
this.startIndex = startIndex;
this.endIndex = endIndex;
}
public void run(){
URL url;
try{
url = new URL(Main.path);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setConnectTimeout(8000);
conn.setReadTimeout(8000);
conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);
if(conn.getResponseCode() == 206){
InputStream is = conn.getInputStream();
File file = new File(Main.getFileNameFromPath(Main.path));
RandomAccessFile raf = new RandomAccessFile(file, "rwd");
raf.seek(startIndex);
byte[] b = new byte[1024];
int len = 0;
int total = 0;
while((len = is.read(b)) != -1){
raf.write(b, 0, len);
total += len;
System.out.println("線程" + threadId + "下載的進度為:" + total);
//創(chuàng)建一個進度臨時文件纺涤,保存下載進度
File fileProgress = new File(threadId + ".txt");
RandomAccessFile rafProgress = new RandomAccessFile(fileProgress, "rwd");
rafProgress.write((total + "").getBytes());
rafProgress.close();
}
raf.close();
}
System.out.println("線程" + threadId + "下載完畢---------------------");
}catch(Exception e){
e.printStackTrace();
}
}
}
運行結果:執(zhí)行程序,然后抠忘,在沒下載完成時撩炊,就點擊右上角的停止按鈕。
刷新崎脉,可以看到記錄文件已經產生拧咳。
完成斷點續(xù)傳下載
src/cn.itcast.MultiDownLoad/Main.java
package cn.itcast.MultiDownLoad;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
public class Main {
static int threadCount = 3;
static String path = "http://localhost:8080/QQPlayer.exe";
public static void main(String[] args) {
URL url;
try {
url = new URL(path);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setConnectTimeout(8000);
conn.setReadTimeout(8000);
if(conn.getResponseCode() == 200){
int length = conn.getContentLength();
int size = length / threadCount;
System.out.println("size:" + size);
for(int threadId = 0; threadId < threadCount; threadId++){
int startIndex = threadId * size;
int endIndex = (threadId + 1) * size - 1;
if(threadId == threadCount - 1){
endIndex = length - 1;
}
DownLoadThread dt = new DownLoadThread(threadId, startIndex, endIndex);
dt.start();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static String getFileNameFromPath(String path){
int index = path.lastIndexOf("/");
return path.substring(index + 1);
}
}
class DownLoadThread extends Thread{
int threadId;
int startIndex;
int endIndex;
public DownLoadThread(int threadId, int startIndex, int endIndex) {
super();
this.threadId = threadId;
this.startIndex = startIndex;
this.endIndex = endIndex;
}
public void run(){
URL url;
try{
int lastProgress = 0;
//下載之前,先判斷進度臨時文件是否存在
File fileProgress1 = new File(threadId + ".txt");
if(fileProgress1.exists()){
FileInputStream fis = new FileInputStream(fileProgress1);
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
//讀取進度臨時文件中的值
lastProgress = Integer.parseInt(br.readLine());
//把上一次下載的進度加到下載開始位置
startIndex += lastProgress;
fis.close();
}
System.out.println("線程" + threadId + "囚灼,下載區(qū)間為:" + startIndex + "-" + endIndex);
url = new URL(Main.path);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setConnectTimeout(8000);
conn.setReadTimeout(8000);
conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);
if(conn.getResponseCode() == 206){
InputStream is = conn.getInputStream();
File file = new File(Main.getFileNameFromPath(Main.path));
RandomAccessFile raf = new RandomAccessFile(file, "rwd");
raf.seek(startIndex);
byte[] b = new byte[1024];
int len = 0;
//從之前下載的地方開始下載
int total = lastProgress;
while((len = is.read(b)) != -1){
raf.write(b, 0, len);
total += len;
System.out.println("線程" + threadId + "下載的進度為:" + total);
File fileProgress = new File(threadId + ".txt");
RandomAccessFile rafProgress = new RandomAccessFile(fileProgress, "rwd");
rafProgress.write((total + "").getBytes());
rafProgress.close();
}
raf.close();
System.out.println("線程" + threadId + "下載完畢---------------------");
}
}catch(Exception e){
e.printStackTrace();
}
}
}
運行結果:
執(zhí)行Main.java程序骆膝,然后,在還沒有下載完灶体,停止阅签。然后,再次執(zhí)行Main.java蝎抽,可以看到如下顯示政钟,也就是實現(xiàn)了斷點續(xù)傳。
下載后刪除進度臨時文件
src/cn.itcast.MultiDownLoad/Main.java
package cn.itcast.MultiDownLoad;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
public class Main {
//記錄當前已經下載完成的線程的數(shù)量
static int finishedThreadCount = 0;
static int threadCount = 3;
static String path = "http://localhost:8080/QQPlayer.exe";
public static void main(String[] args) {
URL url;
try {
url = new URL(path);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setConnectTimeout(8000);
conn.setReadTimeout(8000);
if(conn.getResponseCode() == 200){
int length = conn.getContentLength();
int size = length / threadCount;
System.out.println("size:" + size);
for(int threadId = 0; threadId < threadCount; threadId++){
int startIndex = threadId * size;
int endIndex = (threadId + 1) * size - 1;
if(threadId == threadCount - 1){
endIndex = length - 1;
}
DownLoadThread dt = new DownLoadThread(threadId, startIndex, endIndex);
dt.start();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static String getFileNameFromPath(String path){
int index = path.lastIndexOf("/");
return path.substring(index + 1);
}
}
class DownLoadThread extends Thread{
int threadId;
int startIndex;
int endIndex;
public DownLoadThread(int threadId, int startIndex, int endIndex) {
super();
this.threadId = threadId;
this.startIndex = startIndex;
this.endIndex = endIndex;
}
public void run(){
URL url;
try{
int lastProgress = 0;
File fileProgress1 = new File(threadId + ".txt");
if(fileProgress1.exists()){
FileInputStream fis = new FileInputStream(fileProgress1);
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
lastProgress = Integer.parseInt(br.readLine());
startIndex += lastProgress;
fis.close();
}
System.out.println("線程" + threadId + "樟结,下載區(qū)間為:" + startIndex + "-" + endIndex);
url = new URL(Main.path);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setConnectTimeout(8000);
conn.setReadTimeout(8000);
conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);
if(conn.getResponseCode() == 206){
InputStream is = conn.getInputStream();
File file = new File(Main.getFileNameFromPath(Main.path));
RandomAccessFile raf = new RandomAccessFile(file, "rwd");
raf.seek(startIndex);
byte[] b = new byte[1024];
int len = 0;
int total = lastProgress;
while((len = is.read(b)) != -1){
raf.write(b, 0, len);
total += len;
System.out.println("線程" + threadId + "下載的進度為:" + total);
File fileProgress = new File(threadId + ".txt");
RandomAccessFile rafProgress = new RandomAccessFile(fileProgress, "rwd");
rafProgress.write((total + "").getBytes());
rafProgress.close();
}
raf.close();
System.out.println("線程" + threadId + "下載完畢---------------------");
//在所有線程都下載完畢后养交,一起刪除所有進度臨時文件
//有一個線程完成下載,已經下載完成的線程的數(shù)量+1
Main.finishedThreadCount++;
synchronized(Main.path){
if(Main.finishedThreadCount == 3){
//刪除所有進度臨時文件
for(int i = 0; i < Main.finishedThreadCount; i++){
File f = new File(i + ".txt");
f.delete();
}
//為了防止所有線程都執(zhí)行到上面的Main.finishedThreadCount++;瓢宦,然后三個線程都執(zhí)行刪除所有臨時文件的代碼碎连。
//所以,一方面使用同步代碼塊刁笙,另一方面將Main.finishedThreadCount設置為0破花。
Main.finishedThreadCount = 0;
}
}
}
}catch(Exception e){
e.printStackTrace();
}
}
}
運行結果:運行Main.java,斷點續(xù)傳完成后疲吸,刷新座每。可以看到摘悴,臨時文件已經被刪除峭梳。
5. Android版多線程斷點續(xù)傳下載
res/layout/activity.xml
<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"
tools:context=".MainActivity" >
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="下載"
android:onClick="click" />
</RelativeLayout>
src/cn.itcast.androidmultidownload/MainActivity.xml
package cn.itcast.androidmultidownload;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
public class MainActivity extends Activity {
int finishedThreadCount = 0;
int threadCount = 3;
String path = "http://192.168.1.100:8080/QQPlayer.exe";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void click(View v){
Thread t = new Thread(){
@Override
public void run() {
URL url;
try {
url = new URL(path);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setConnectTimeout(8000);
conn.setReadTimeout(8000);
if(conn.getResponseCode() == 200){
int length = conn.getContentLength();
int size = length / threadCount;
System.out.println("size:" + size);
for(int threadId = 0; threadId < threadCount; threadId++){
int startIndex = threadId * size;
int endIndex = (threadId + 1) * size - 1;
if(threadId == threadCount - 1){
endIndex = length - 1;
}
DownLoadThread dt = new DownLoadThread(threadId, startIndex, endIndex);
dt.start();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
t.start();
}
public String getFileNameFromPath(String path){
int index = path.lastIndexOf("/");
return path.substring(index + 1);
}
class DownLoadThread extends Thread{
int threadId;
int startIndex;
int endIndex;
public DownLoadThread(int threadId, int startIndex, int endIndex) {
super();
this.threadId = threadId;
this.startIndex = startIndex;
this.endIndex = endIndex;
}
public void run(){
URL url;
try{
int lastProgress = 0;
//修改文件路徑,存在外部存儲器中
File fileProgress1 = new File(Environment.getExternalStorageDirectory(), threadId + ".txt");
if(fileProgress1.exists()){
FileInputStream fis = new FileInputStream(fileProgress1);
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
lastProgress = Integer.parseInt(br.readLine());
startIndex += lastProgress;
fis.close();
}
System.out.println("線程" + threadId + "蹂喻,下載區(qū)間為:" + startIndex + "-" + endIndex);
url = new URL(path);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setConnectTimeout(8000);
conn.setReadTimeout(8000);
conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);
if(conn.getResponseCode() == 206){
InputStream is = conn.getInputStream();
File file = new File(Environment.getExternalStorageDirectory(), getFileNameFromPath(path));
RandomAccessFile raf = new RandomAccessFile(file, "rwd");
raf.seek(startIndex);
byte[] b = new byte[1024];
int len = 0;
int total = lastProgress;
while((len = is.read(b)) != -1){
raf.write(b, 0, len);
total += len;
System.out.println("線程" + threadId + "下載的進度為:" + total);
File fileProgress = new File(Environment.getExternalStorageDirectory(), threadId + ".txt");
RandomAccessFile rafProgress = new RandomAccessFile(fileProgress, "rwd");
rafProgress.write((total + "").getBytes());
rafProgress.close();
}
raf.close();
System.out.println("線程" + threadId + "下載完畢---------------------");
finishedThreadCount++;
synchronized(path){
if(finishedThreadCount == 3){
for(int i = 0; i < finishedThreadCount; i++){
File f = new File(Environment.getExternalStorageDirectory(), i + ".txt");
f.delete();
}
finishedThreadCount = 0;
}
}
}
}catch(Exception e){
e.printStackTrace();
}
}
}
}
添加權限:
運行結果:點擊“下載”按鈕葱椭,在下載完成之前,殺死線程口四。
可以看到臨時文件生成孵运。
再次運行應用程序,點擊“下載”按鈕蔓彩,接著下載治笨,斷點續(xù)傳成功實現(xiàn)。
下載完成后赤嚼,可以看到臨時文件刪除成功旷赖。
添加進度條反應下載進度,res/layout/activity.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"
tools:context=".MainActivity"
android:orientation="vertical">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="下載"
android:onClick="click" />
<ProgressBar
android:id="@+id/pb"
style="@android:style/Widget.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
src/cn.itcast.androidmultidownload/MainActivity.xml
package cn.itcast.androidmultidownload;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.widget.ProgressBar;
public class MainActivity extends Activity {
int finishedThreadCount = 0;
int threadCount = 3;
String path = "http://192.168.1.100:8080/QQPlayer.exe";
private ProgressBar pb;
//記錄進度條的當前進度
int currentProgress = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//進度條
pb = (ProgressBar) findViewById(R.id.pb);
}
public void click(View v){
Thread t = new Thread(){
@Override
public void run() {
URL url;
try {
url = new URL(path);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setConnectTimeout(8000);
conn.setReadTimeout(8000);
if(conn.getResponseCode() == 200){
int length = conn.getContentLength();
//設定進度條的最大值
pb.setMax(length);
int size = length / threadCount;
System.out.println("size:" + size);
for(int threadId = 0; threadId < threadCount; threadId++){
int startIndex = threadId * size;
int endIndex = (threadId + 1) * size - 1;
if(threadId == threadCount - 1){
endIndex = length - 1;
}
DownLoadThread dt = new DownLoadThread(threadId, startIndex, endIndex);
dt.start();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
t.start();
}
public String getFileNameFromPath(String path){
int index = path.lastIndexOf("/");
return path.substring(index + 1);
}
class DownLoadThread extends Thread{
int threadId;
int startIndex;
int endIndex;
public DownLoadThread(int threadId, int startIndex, int endIndex) {
super();
this.threadId = threadId;
this.startIndex = startIndex;
this.endIndex = endIndex;
}
public void run(){
URL url;
try{
int lastProgress = 0;
File fileProgress1 = new File(Environment.getExternalStorageDirectory(), threadId + ".txt");
if(fileProgress1.exists()){
FileInputStream fis = new FileInputStream(fileProgress1);
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
lastProgress = Integer.parseInt(br.readLine());
startIndex += lastProgress;
//如果開始位置大于或等于endIndex更卒,說明上一次下載中等孵,此線程就已經下載完了
if(startIndex >= endIndex){
finishedThreadCount++;
}
//如果上一次下載過,把上次的進度加到當前進度中
currentProgress += lastProgress;
pb.setProgress(currentProgress);
fis.close();
}
System.out.println("線程" + threadId + "蹂空,下載區(qū)間為:" + startIndex + "-" + endIndex);
url = new URL(path);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setConnectTimeout(8000);
conn.setReadTimeout(8000);
conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);
if(conn.getResponseCode() == 206){
InputStream is = conn.getInputStream();
File file = new File(Environment.getExternalStorageDirectory(), getFileNameFromPath(path));
RandomAccessFile raf = new RandomAccessFile(file, "rwd");
raf.seek(startIndex);
byte[] b = new byte[1024];
int len = 0;
int total = lastProgress;
while((len = is.read(b)) != -1){
raf.write(b, 0, len);
total += len;
System.out.println("線程" + threadId + "下載的進度為:" + total);
File fileProgress = new File(Environment.getExternalStorageDirectory(), threadId + ".txt");
RandomAccessFile rafProgress = new RandomAccessFile(fileProgress, "rwd");
rafProgress.write((total + "").getBytes());
rafProgress.close();
//每一條線程下載的數(shù)據(jù)俯萌,都應該加到全局進度里
currentProgress += len;
//設置進度條當前進度
//進度條內部也是通過handler讓主線程刷新UI的
pb.setProgress(currentProgress);
}
raf.close();
System.out.println("線程" + threadId + "下載完畢---------------------");
finishedThreadCount++;
synchronized(path){
if(finishedThreadCount == 3){
for(int i = 0; i < finishedThreadCount; i++){
File f = new File(Environment.getExternalStorageDirectory(), i + ".txt");
f.delete();
}
finishedThreadCount = 0;
}
}
}
}catch(Exception e){
e.printStackTrace();
}
}
}
}
運行結果:點擊“下載”按鈕,可以通過進度條看到下載進度上枕。然后绳瘟,殺死進程,再重新運行應用程序姿骏,點擊“下載”按鈕糖声,可以看到進度條在原有的基礎上繼續(xù)向前移動,也就是實現(xiàn)了斷點續(xù)傳的進度條實現(xiàn)分瘦。
添加文本進度蘸泻,res/layout/activity.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"
tools:context=".MainActivity"
android:orientation="vertical">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="下載"
android:onClick="click" />
<ProgressBar
android:id="@+id/pb"
style="@android:style/Widget.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0%"
android:layout_gravity="right"
/>
</LinearLayout>
src/cn.itcast.androidmultidownload/MainActivity.xml
package cn.itcast.androidmultidownload;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
public class MainActivity extends Activity {
int finishedThreadCount = 0;
int threadCount = 3;
String path = "http://192.168.1.100:8080/QQPlayer.exe";
private ProgressBar pb;
int currentProgress = 0;
private TextView tv;
//刷新TextView
Handler handler = new Handler(){
public void handleMessage(android.os.Message msg) {
//當前進度除以最大進度,得到下載進度的百分比
tv.setText(pb.getProgress() * 100 /pb.getMax() + "%");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pb = (ProgressBar) findViewById(R.id.pb);
tv = (TextView)findViewById(R.id.tv);
}
public void click(View v){
Thread t = new Thread(){
@Override
public void run() {
URL url;
try {
url = new URL(path);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setConnectTimeout(8000);
conn.setReadTimeout(8000);
if(conn.getResponseCode() == 200){
int length = conn.getContentLength();
pb.setMax(length);
int size = length / threadCount;
System.out.println("size:" + size);
for(int threadId = 0; threadId < threadCount; threadId++){
int startIndex = threadId * size;
int endIndex = (threadId + 1) * size - 1;
if(threadId == threadCount - 1){
endIndex = length - 1;
}
DownLoadThread dt = new DownLoadThread(threadId, startIndex, endIndex);
dt.start();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
t.start();
}
public String getFileNameFromPath(String path){
int index = path.lastIndexOf("/");
return path.substring(index + 1);
}
class DownLoadThread extends Thread{
int threadId;
int startIndex;
int endIndex;
public DownLoadThread(int threadId, int startIndex, int endIndex) {
super();
this.threadId = threadId;
this.startIndex = startIndex;
this.endIndex = endIndex;
}
public void run(){
URL url;
try{
int lastProgress = 0;
File fileProgress1 = new File(Environment.getExternalStorageDirectory(), threadId + ".txt");
if(fileProgress1.exists()){
FileInputStream fis = new FileInputStream(fileProgress1);
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
lastProgress = Integer.parseInt(br.readLine());
startIndex += lastProgress;
if(startIndex >= endIndex){
finishedThreadCount++;
}
currentProgress += lastProgress;
pb.setProgress(currentProgress);
//發(fā)送消息嘲玫,讓主線程刷新文本進度
handler.sendEmptyMessage(1);
fis.close();
}
System.out.println("線程" + threadId + "悦施,下載區(qū)間為:" + startIndex + "-" + endIndex);
url = new URL(path);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setConnectTimeout(8000);
conn.setReadTimeout(8000);
conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);
if(conn.getResponseCode() == 206){
InputStream is = conn.getInputStream();
File file = new File(Environment.getExternalStorageDirectory(), getFileNameFromPath(path));
RandomAccessFile raf = new RandomAccessFile(file, "rwd");
raf.seek(startIndex);
byte[] b = new byte[1024];
int len = 0;
int total = lastProgress;
while((len = is.read(b)) != -1){
raf.write(b, 0, len);
total += len;
System.out.println("線程" + threadId + "下載的進度為:" + total);
File fileProgress = new File(Environment.getExternalStorageDirectory(), threadId + ".txt");
RandomAccessFile rafProgress = new RandomAccessFile(fileProgress, "rwd");
rafProgress.write((total + "").getBytes());
rafProgress.close();
currentProgress += len;
pb.setProgress(currentProgress);
//發(fā)送消息,讓主線程刷新文本進度
handler.sendEmptyMessage(1);
}
raf.close();
System.out.println("線程" + threadId + "下載完畢---------------------");
finishedThreadCount++;
synchronized(path){
if(finishedThreadCount == 3){
for(int i = 0; i < finishedThreadCount; i++){
File f = new File(Environment.getExternalStorageDirectory(), i + ".txt");
f.delete();
}
finishedThreadCount = 0;
}
}
}
}catch(Exception e){
e.printStackTrace();
}
}
}
}
運行結果:
文本進度計算的bug:當文件較大時去团,就會出現(xiàn)bug抡诞,文本進度計算數(shù)據(jù)變成了負數(shù)穷蛹。
這是因為文件大小超出了int所能表示的最大范圍。
只需要修改代碼如下即可昼汗。
如果最終顯示為99%肴熏,那么只需要在下載完成之后,直接在程序中寫死為100%即可顷窒。
6. xUtils多線程斷點續(xù)傳下載
從github上下載xUtils蛙吏,將xUtils的jar復制到libs目錄下。
如果無法關聯(lián)源碼鞋吉,可以通過在libs目錄下新建一個properties文件解決鸦做。
properties文件的內容為"src=源碼目錄",即可成功關聯(lián)源碼谓着。
res/layout/activity.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:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity"
android:orientation="vertical"
>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="下載"
android:onClick="click" />
<TextView
android:id="@+id/tv_success"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<TextView
android:id="@+id/tv_failure"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ff0000"
/>
<ProgressBar
android:id="@+id/tv_pb"
style="@android:style/Widget.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<TextView
android:id="@+id/tv_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
src/cn.itcast.androidmultidownload/MainActivity.xml
package cn.itcast.xutils;
import java.io.File;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.lidroid.xutils.HttpUtils;
import com.lidroid.xutils.exception.HttpException;
import com.lidroid.xutils.http.HttpHandler;
import com.lidroid.xutils.http.ResponseInfo;
import com.lidroid.xutils.http.callback.RequestCallBack;
public class MainActivity extends Activity {
String path = "http://192.168.1.100:8080/QQPlayer.exe";
private TextView tv;
private ProgressBar pb;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pb = (ProgressBar)findViewById(R.id.tv_pb);
tv = (TextView)findViewById(R.id.tv_progress);
}
public void click(View v){
HttpUtils utils = new HttpUtils();
HttpHandler handler = utils.download(path, //請求的網(wǎng)址
"sdcard/QQPlayer.exe", //文件保存的路徑及文件名
true, // 是否支持斷點續(xù)傳
true, // 如果相應頭中包含文件名泼诱,那么下載完畢后,自動以該名字重命名文件
new RequestCallBack<File>() {
//下載完成調用
@Override
public void onSuccess(ResponseInfo<File> responseInfo) {
TextView tv = (TextView)findViewById(R.id.tv_success);
tv.setText(responseInfo.result.getPath());
}
//下載失敗后調用
@Override
public void onFailure(HttpException error, String msg) {
TextView tv = (TextView)findViewById(R.id.tv_success);
tv.setText(msg);
}
//下載過程中不斷調用
@Override
public void onLoading(long total, long current,
boolean isUploading) {
pb.setMax((int)total);
pb.setProgress((int)current);
tv.setText((current * 100)/ total + "%");
}
});
}
}
添加權限:
運行結果: