您的位置:首页 > 理论基础 > 计算机网络

Android中的网络编程

2016-05-02 22:17 260 查看
一、访问网络资源

1、使用统一资源定位符获取网络资源的路径url

URL url = new URL(path);



2、通过url打开网络连接(连接方式有http连接,ftp连接,https连接,rtsp连接)

此处获取网络连接用到的是http连接,所以使打开一个HttpURLConnection
HttpURLConnection openConnection = (HttpConnection)url.openConnection();
openConnection.setRequestMethod("GET");//设置请求方式
openConnection.setConnectTimeout(5000);//设置请求超时时间




3、打开连接后就可以获取网络返回的数据(例)
String contentType = openConnection.getContentType();//获取返回的数据类型
Object content = openConnection.getContent();//获取放回的数据
int code = openConnection.getResponseCode();//获取响应码,200--返回正确,404--资源没找到,503--服务器内部错误
......


4、获取输入流,保存返回的数据
InputStream inputStream = openConnection.getInputStream();


5、根据资源类型将网络资源转换成相应的资源,如是图片资源,则如下转换
Bitmap decodeStream = BitmapFactory.decodeStream(inputStream);


6、访问网络需要添加网络权限

7、一个常用的参数:User-Agent,我们可以通过这个参数来获取这个资源的来源信息,如某个网站,iphone6 plus等

例:
openConnection.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64;Trident/4.0;SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET4.0C; InfoPath.2");


8、网络路径中不能包含中文,如果在路径中需要涉及到中文,需要使用将中文使用URLEncoder对中文进行编码
URLEncoder.encode("中文",utf-8);


二、使用smartImageView加载网络图片

smartImageView继承自ImageView,具有ImageView的所有功能,同时扩展了ImageVIew的功能,如常用的方法setImageUrl(URL url),这个方法可以通过直接传递图片的URL地址来给smartImageView控件设置图片。

smartIamgeView使用异步加载的方式从网络获取网络图片,加载过程在子线程中执行,在使用时只需要传递图片的路径即可,不需要在子线程访问网络和加载的问题。

public class MainActivity extends Activity {
String path = "http://f.hiphotos.baidu.com/image/pic/item/a8014c086e061d9507500dd67ff40ad163d9cacd.jpg";
private SmartImageView smiv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button bt = (Button) findViewById(R.id.bt);
smiv = (SmartImageView) findViewById(R.id.smiv);
}

public void click(View v){
smiv.setImageUrl(path);
}
}


三、使用Get请求方式请求网络

模拟QQ登录

1、服务器端代码

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//01010101
String qq = request.getParameter("qq");//采用的编码是iso-8859-1
String pwd = request.getParameter("pwd");
System.out.println("qq:"+new String(qq.getBytes("iso-8859-1"),"utf-8"));
System.out.println("pwd:"+new String(pwd.getBytes("iso-8859-1"),"utf-8"));
//查询数据库 看qq和密码是否存在
if("10000".equals(qq)&&"abcde".equals(pwd)){
//tomcat容器如果发现字符串不识别就默认采用本地码表
response.getOutputStream().write("登陆成功".getBytes("utf-8"));
}else{
response.getOutputStream().write("登陆失败".getBytes("utf-8"));
}
}


服务器端会通过HttpServletRequest request getParameter()方法获取到用户请求网络的参数,如用户名和密码。用户发送请求时,输入的请求参数都会展示在地址栏,即地址栏的所有的参数就是服务器端要获取到的所有参数。在发送请求时,只需要在请求地址后面加上相应的参数就能发送get请求,如www.qq.com/web/LogingServlet?qq=10000&pwd=abcde

2、客户端在发送get请求时,需要将请求的参数传递到URL中,然后在发送请求。如:

//获取请求的参数
final String qq = et_qq.getText().toString().trim();
final String pwd = et_pwd.getText().toString().trim();
//将参数添加到请求地址中
String path = "http://192.168.1.103:8080/web/LoginServlet?qq="+URLEncoder.encode(qq, "utf-8")+"&pwd="+URLEncoder.encode(pwd, "utf-8");


3、get请求的优缺点

优点:使用方便,只需要的地址后面组拼请求参数即可发送请求

缺点:(1)请求参数在地址栏都能展示出来,不安全;

(2)数据的长度有限制

四、使用post请求方式发送网络请求

1、post请求方式的优缺点

优点:安全,数据不是在url后面组拼,而是通过流的形式写给服务器;数据的长度没有限制

缺点:编写麻烦

2、post请求和get请求的区别

(1)get请求需要在URL后面组拼提交的数据,post请求不需要组拼任何数据

(2)post请求必须要指定请求提交的数据长度(如下图,post请求比get请求多了Content-Type请求头)


(3)post请求是以流的方式吧数据写给服务器,所有的http请求头必须要告诉服务器写多长的数据(如下图,post请求比get请求多了Content-Length请求头)

3、服务端代码示例

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("post过来的请求");
doGet(req, resp);
}


4、示例代码

URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//1.设置请求方式为POST
conn.setRequestMethod("POST"); //注意单词必须大写.
conn.setConnectTimeout(5000);
//2.设置http请求数据的类型为表单类型
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
//3.设置给服务器写的数据的长度
//qq=10000&pwd=abcde
String data = "qq="+URLEncoder.encode(qq, "utf-8")+"&pwd="+URLEncoder.encode(pwd, "utf-8");
conn.setRequestProperty("Content-Length", String.valueOf(data.length()));
//4.记得指定要给服务器写数据
conn.setDoOutput(true);
//5.开始向服务器写数据
conn.getOutputStream().write(data.getBytes());


五、使用HTTPClient发送网络请求

1、发送get请求

String path = "http://192.168.1.103:8080/web/LoginServlet?qq="+URLEncoder.encode(qq, "utf-8")+"&pwd="+URLEncoder.encode(pwd, "utf-8");
HttpClient client = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(path);
HttpResponse response = client.execute(httpGet);
//获取状态码
int code = response.getStatusLine().getStatusCode();

if(code == 200){
InputStream is = response.getEntity().getContent();
String result = StreamTools.readStream(is);
Message msg = Message.obtain();
msg.what = SUCCESS;
msg.obj = result;
handler.sendMessage(msg);
}else{
Message msg = Message.obtain();
msg.what = ERROR;
handler.sendMessage(msg);
}


2、发送post请求

String path = "http://192.168.1.103:8080/web/LoginServlet";
HttpClient client = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(path);
List<NameValuePair> parameters = new ArrayList<NameValuePair>();
parameters.add(new BasicNameValuePair("qq", qq));
parameters.add(new BasicNameValuePair("pwd", pwd));
httpPost.setEntity(new UrlEncodedFormEntity(parameters, "utf-8"));
HttpResponse response = client.execute(httpPost);
//获取状态码
int code = response.getStatusLine().getStatusCode();

if(code == 200){
InputStream is = response.getEntity().getContent();
String result = StreamTools.readStream(is);
Message msg = Message.obtain();
msg.what = SUCCESS;
msg.obj = result;
handler.sendMessage(msg);
}else{
Message msg = Message.obtain();
msg.what = ERROR;
handler.sendMessage(msg);
}


StreamTools.readStream()

public class StreamTools {
/**
* 工具方法
* @param is 输入流
* @return 文本字符串
* @throws Exception
*/
public static String readStream(InputStream is) throws Exception{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = -1;
while((len = is.read(buffer))!=-1){
baos.write(buffer, 0, len);
}
is.close();
String temp =  baos.toString();
return temp;
}
}


六、使用过异步网络加载框架AsyncHttpClient请求网络

1、AsyncHttpClient进一步对HttpClient进行了封装,这个框架考虑到了子线程问题,我们在使用时就不用再创建子线程,直接使用估计可,同时框架中还使用了线程池,使加载效率更高。

2、发送get请求
String path = "http://192.168.1.103:8080/web/LoginServlet?qq="+URLEncoder.encode(qq)+"&pwd="+URLEncoder.encode(pwd);
AsyncHttpClient client = new AsyncHttpClient();
client.get(path, new AsyncHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, Header[] headers,
byte[] responseBody) {
tv_status.setText(new String(responseBody));
}
@Override
public void onFailure(int statusCode, Header[] headers,
byte[] responseBody, Throwable error) {
tv_status.setText("http请求失败"+new String(responseBody));
}
});


3、发送post请求
String path = "http://192.168.1.103:8080/web/LoginServlet";
AsyncHttpClient client = new AsyncHttpClient();
RequestParams params = new RequestParams();
params.put("qq", qq);
params.put("pwd", pwd);
client.post(path, params, new AsyncHttpResponseHandler(){
@Override
public void onSuccess(int statusCode, Header[] headers,
byte[] responseBody) {
tv_status.setText("登陆结果:"+new String(responseBody));
}
@Override
public void onFailure(int statusCode, Header[] headers,
byte[] responseBody, Throwable error) {
tv_status.setText("请求失败请检查网络");
}
});


AsyncHttpClient特性:

(1)采用异步http请求,并通过匿名内部类处理回调结果

(2)http请求独立在UI主线程之外

(3)采用线程池来处理并发请求

(4)采用RequestParams类创建GET/POST参数

(5)不需要第三方包即可支持Multipart file文件上传

(6)大小只有25kb

(7)自动为各种移动电话处理连接断开时请求重连

(8)超快的自动gzip响应解码支持

(9)使用BinaryHttpResponseHandler类下载二进制文件(如图片)

(10) 使用JsonHttpResponseHandler类可以自动将响应结果解析为json格式

(11)持久化cookie存储,可以将cookie保存到你的应用程序的SharedPreferences中

使用方法

(1)到官网http://loopj.com/android-async-http/下载最新的android-async-http-1.4.4.jar,然后将此jar包添加进Android应用程序 libs文件夹

(2)通过import com.loopj.android.http.*;引入相关类

(3)创建异步请求

[java] view plaincopy

AsyncHttpClient client = new AsyncHttpClient();

client.get("http://www.google.com", new AsyncHttpResponseHandler() {

@Override

public void onSuccess(String response) {

System.out.println(response);

}

});

建议使用静态的Http Client对象

在下面这个例子,我们创建了静态的http client对象,使其很容易连接到Twitter的API

[java] view plaincopy

import com.loopj.android.http.*;

public class TwitterRestClient {

private static final String BASE_URL = "http://api.twitter.com/1/";

private static AsyncHttpClient client = new AsyncHttpClient();

public static void get(String url, RequestParams params, AsyncHttpResponseHandler responseHandler) {

client.get(getAbsoluteUrl(url), params, responseHandler);

}

public static void post(String url, RequestParams params, AsyncHttpResponseHandler responseHandler) {

client.post(getAbsoluteUrl(url), params, responseHandler);

}

private static String getAbsoluteUrl(String relativeUrl) {

return BASE_URL + relativeUrl;

}

}

然后我们可以很容易的在代码中操作Twitter的API

[java] view plaincopy

import org.json.*;

import com.loopj.android.http.*;

class TwitterRestClientUsage {

public void getPublicTimeline() throws JSONException {

TwitterRestClient.get("statuses/public_timeline.json", null, new JsonHttpResponseHandler() {

@Override

public void onSuccess(JSONArray timeline) {

// Pull out the first event on the public timeline

JSONObject firstEvent = timeline.get(0);

String tweetText = firstEvent.getString("text");

// Do something with the response

System.out.println(tweetText);

}

});

}

}

AsyncHttpClient, RequestParams ,AsyncHttpResponseHandler三个类使用方法

(1)AsyncHttpClient

public class AsyncHttpClient extends java.lang.Object

该类通常用在android应用程序中创建异步GET, POST, PUT和DELETE HTTP请求,请求参数通过RequestParams实例创建,响应通过重写匿名内部类 ResponseHandlerInterface的方法处理。

例子:

[java] view plaincopy

AsyncHttpClient client = new AsyncHttpClient();

client.get("http://www.google.com", new ResponseHandlerInterface() {

@Override

public void onSuccess(String response) {

System.out.println(response);

}

});

(2)RequestParams

public class RequestParams extends java.lang.Object

用于创建AsyncHttpClient实例中的请求参数(包括字符串或者文件)的集合

例子:

[java] view plaincopy

RequestParams params = new RequestParams();

params.put("username", "james");

params.put("password", "123456");

params.put("email", "my@email.com");

params.put("profile_picture", new File("pic.jpg")); // Upload a File

params.put("profile_picture2", someInputStream); // Upload an InputStream

params.put("profile_picture3", new ByteArrayInputStream(someBytes)); // Upload some bytes

Map<String, String> map = new HashMap<String, String>();

map.put("first_name", "James");

map.put("last_name", "Smith");

params.put("user", map); // url params: "user[first_name]=James&user[last_name]=Smith"

Set<String> set = new HashSet<String>(); // unordered collection

set.add("music");

set.add("art");

params.put("like", set); // url params: "like=music&like=art"

List<String> list = new ArrayList<String>(); // Ordered collection

list.add("Java");

list.add("C");

params.put("languages", list); // url params: "languages[]=Java&languages[]=C"

String[] colors = { "blue", "yellow" }; // Ordered collection

params.put("colors", colors); // url params: "colors[]=blue&colors[]=yellow"

List<Map<String, String>> listOfMaps = new ArrayList<Map<String, String>>();

Map<String, String> user1 = new HashMap<String, String>();

user1.put("age", "30");

user1.put("gender", "male");

Map<String, String> user2 = new HashMap<String, String>();

user2.put("age", "25");

user2.put("gender", "female");

listOfMaps.add(user1);

listOfMaps.add(user2);

params.put("users", listOfMaps); // url params: "users[][age]=30&users[][gender]=male&users[][age]=25&users[][gender]=female"

AsyncHttpClient client = new AsyncHttpClient();

client.post("http://myendpoint.com", params, responseHandler);

(3)public class AsyncHttpResponseHandler extends java.lang.Object implements ResponseHandlerInterface

用于拦截和处理由AsyncHttpClient创建的请求。在匿名类AsyncHttpResponseHandler中的重写 onSuccess(int, org.apache.http.Header[], byte[])方法用于处理响应成功的请求。此外,你也可以重写 onFailure(int, org.apache.http.Header[], byte[], Throwable),
onStart(), onFinish(), onRetry() 和onProgress(int, int)方法

例子:

[java] view plaincopy

AsyncHttpClient client = new AsyncHttpClient();

client.get("http://www.google.com", new AsyncHttpResponseHandler() {

@Override

public void onStart() {

// Initiated the request

}

@Override

public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {

// Successfully got a response

}

@Override

public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error)

{

// Response failed :(

}

@Override

public void onRetry() {

// Request was retried

}

@Override

public void onProgress(int bytesWritten, int totalSize) {

// Progress notification

}

@Override

public void onFinish() {

// Completed the request (either success or failure)

}

});

利用PersistentCookieStore持久化存储cookie

PersistentCookieStore类用于实现Apache HttpClient的CookieStore接口,可以自动的将cookie保存到Android设备的SharedPreferences中,如果你打算使用cookie来管理验证会话,这个非常有用,因为用户可以保持登录状态,不管关闭还是重新打开你的app

(1)首先创建 AsyncHttpClient实例对象

[java] view plaincopy

AsyncHttpClient myClient = new AsyncHttpClient();

(2)将客户端的cookie保存到PersistentCookieStore实例对象,带有activity或者应用程序context的构造方法

[java] view plaincopy

PersistentCookieStore myCookieStore = new PersistentCookieStore(this);

myClient.setCookieStore(myCookieStore);

(3)任何从服务器端获取的cookie都会持久化存储到myCookieStore中,添加一个cookie到存储中,只需要构造一个新的cookie对象,并且调用addCookie方法

[java] view plaincopy

BasicClientCookie newCookie = new BasicClientCookie("cookiesare", "awesome");

newCookie.setVersion(1);

newCookie.setDomain("mydomain.com");

newCookie.setPath("/");

myCookieStore.addCookie(newCookie);

7.利用RequestParams上传文件

类RequestParams支持multipart file 文件上传

(1)在RequestParams 对象中添加InputStream用于上传

[java] view plaincopy

InputStream myInputStream = blah;

RequestParams params = new RequestParams();

params.put("secret_passwords", myInputStream, "passwords.txt");

(2)添加文件对象用于上传

[java] view plaincopy

File myFile = new File("/path/to/file.png");

RequestParams params = new RequestParams();

try {

params.put("profile_picture", myFile);

} catch(FileNotFoundException e) {}

(3)添加字节数组用于上传

[java] view plaincopy

byte[] myByteArray = blah;

RequestParams params = new RequestParams();

params.put("soundtrack", new ByteArrayInputStream(myByteArray), "she-wolf.mp3");

用BinaryHttpResponseHandler下载二进制数据

BinaryHttpResponseHandler用于获取二进制数据如图片和其他文件
AsyncHttpClient client = new AsyncHttpClient();
String[] allowedContentTypes = new String[] { "image/png", "image/jpeg" };
client.get("http://example.com/file.png", new BinaryHttpResponseHandler(allowedContentTypes) {
@Override
public void onSuccess(byte[] fileData) {
// Do something with the file
}
});


android-async-http 开源框架可以使我们轻松地获取网络数据或者向服务器发送数据,最关键的是,它是异步框架,在底层使用线程池处理并发请求,效率很高,使用又特别简单。

以往我们在安卓上做项目,比如要下载很多图片、网页或者其他的资源,多数开发者会选择一个线程一个下载任务这种模型,因为安卓自带的 AndroidHttpClient 或者 java 带的 java.net.URL ,默认都是阻塞式操作。这种模型效率不高,对并发要求高的 APP 来讲,并不适用。有的人会选择使用 nio 自己实现,代码复杂度又很高。

AsyncHttpClient 作为 android-async-http 框架的一个核心应用类,使用简单,可以处理文本、二进制等各种格式的 web 资源。下面提供一些代码来看如何使用:
public class Downloader {
public static AsyncHttpClient mHttpc = new AsyncHttpClient();
public static String TAG = "Downloader";

public void downloadText(String uri){
mHttpc.get(uri, null, new AsyncHttpResponseHandler(){
@Override
public void onSuccess(String data){
Log.i(TAG, "downloaded, thread id " + Thread.currentThread().getId());
// TODO: do something on
}
@Override
public void onFailure(Throwable e, String data){
Log.i(TAG, "download failed.");
// TODO: error proceed
}
});
}

public void downloadImage(String uri, String savePath){
mHttpc.get(uri, new ImageResponseHandler(savePath));
}

public class ImageResponseHandler extends BinaryHttpResponseHandler{
private String mSavePath;

public ImageResponseHandler(String savePath){
super();
mSavePath = savePath;
}
@Override
public void onSuccess(byte[] data){
Log.i(TAG, "download image, file length " + data.length);
// TODO: save image , do something on image
}
@Override
public void onFailure(Throwable e, String data){
Log.i(TAG, "download failed");
// TODO : error proceed
}
}
};


七、使用post请求上传文件

使用异步网络请求框架上传文件时,只需要将请求的参数设置为要上传的文件即可
public void upload(View view){
String path = et_path.getText().toString().trim();
File file = new File(path);
if(file.exists()&&file.length()>0){
//上传
AsyncHttpClient client = new AsyncHttpClient();
RequestParams params = new RequestParams();
try {
params.put("file", file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
client.post("http://192.168.1.103:8080/web/UploadServlet", params, new AsyncHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
Toast.makeText(MainActivity.this, "上传成功", 0).show();
}

@Override
public void onFailure(int statusCode, Header[] headers,
byte[] responseBody, Throwable error) {
Toast.makeText(MainActivity.this, "上传失败", 0).show();
}
});

}else{
Toast.makeText(this, "请检查文件是否存在", 0).show();
}
}


八、使用HttpUtils加载网络

1、发送get请求

HttpUtils http = new HttpUtils();
http.send(HttpRequest.HttpMethod.GET,
"http://www.lidroid.com",
new RequestCallBack<String>(){
@Override
public void onLoading(long total, long current, boolean isUploading) {
testTextView.setText(current + "/" + total);
}
@Override
public void onSuccess(ResponseInfo<String> responseInfo) {
textView.setText(responseInfo.result);
}
@Override
public void onStart() {
}
@Override
public void onFailure(HttpException error, String msg) {
}
});


2、下载

支持断点续传,随时停止下载任务,开始任务

HttpUtils http = new HttpUtils();
HttpHandler handler = http.download("http://apache.dataguru.cn/httpcomponents/httpclient/source/httpcomponents-client-4.2.5-src.zip",
"/sdcard/httpcomponents-client-4.2.5-src.zip",
true, // 如果目标文件存在,接着未完成的部分继续下载。服务器不支持RANGE时将从新下载。
true, // 如果从请求返回信息中获取到文件名,下载完成后自动重命名。
new RequestCallBack<File>() {
@Override
public void onStart() {
testTextView.setText("conn...");
}
@Override
public void onLoading(long total, long current, boolean isUploading) {
testTextView.setText(current + "/" + total);
}
@Override
public void onSuccess(ResponseInfo<File> responseInfo) {
testTextView.setText("downloaded:" + responseInfo.result.getPath());
}
@Override
public void onFailure(HttpException error, String msg) {
testTextView.setText(msg);
}
});


九、多线程下载
1、多线程下载的步骤

(1)在磁盘上创建一个与网络文件相同大小的空白文件

(2)将文件划分为若干块,开启多线程,下载文件,每个线程分别下载不同的块

(3)当所有的线程都将自己的块下载完成后,文件下载完成

2、具体实现步骤

(1)连接网络,使用getContentLength()方法获取网络文件的大小
(2)使用RandomAccessFile在本地创建一个与网络文件相同大小的空文件
(3)设置开启多少个线程来下载
(4)为每个线程设置需要下载的文件大小(每个线程下载的大小=总文件大小/线程数),设置每个线程下载的区域由于不不能被整除,所以最后一个线程多下载一点,特殊处理
(5)创建线程下载文件
(6)线程连接网络
(7)高告诉服务器只需要下载一部分内容 conn.setRequestProperty("Range", "bytes="+currentPosition+"-"+endIndex);

Range表示范围,后面的字符串为范围值

(8)使用随机文件访问流的seek(int index)方法设置每个线程下载文件后在本地文件中写入的起始位置
(9)将获取到的数据写入到本地文件中

public void download(View view) {
path = et_path.getText().toString().trim();
if (TextUtils.isEmpty(path) || (!path.startsWith("http://"))) {
Toast.makeText(this, "对不起路径不合法", 0).show();
return;
}
new Thread(){
public void run() {
try {
//1、连接网络
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
int code = conn.getResponseCode();
if (code == 200) {
//2、使用getContentLength()方法获取网络文件的大小
int length = conn.getContentLength();
System.out.println("服务器文件的长度为:" + length);
//3、使用RandomAccessFile在本地创建一个空文件
RandomAccessFile raf = new RandomAccessFile(Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+getFileName(path), "rw");
//4、将空文件的大小设置为与网络文件大小相同
raf.setLength(length);
raf.close();
//5、设置将文件划分的块数,threadCount--线程数
int blocksize = length / threadCount;
runningThreadCount = threadCount;
//6、设置每个线程下载的文件区域
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(threadId, startIndex, endIndex).start();
}
}
} catch (Exception e) {
e.printStackTrace();
}
};
}.start();
}


//创建线程下载文件
private class DownloadThread extends Thread {
/**
* 线程id
*/
private int threadId;
/**
* 线程下载的理论开始位置
*/
private int startIndex;
/**
* 线程下载的结束位置
*/
private int endIndex;
/**
* 当前线程下载到文件的那一个位置了.
*/
private int currentPosition;
public DownloadThread(int threadId, int startIndex, int endIndex) {
this.threadId = threadId;
this.startIndex = startIndex;
this.endIndex = endIndex;
System.out.println(threadId + "号线程下载的范围为:" + startIndex
+ "   ~~   " + endIndex);
currentPosition = startIndex;
}
@Override
public void run() {
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//检查当前线程是否已经下载过一部分的数据了
File info = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+threadId+".position");
RandomAccessFile raf = new RandomAccessFile(Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+getFileName(path), "rw");
if(info.exists()&&info.length()>0){
FileInputStream fis = new FileInputStream(info);
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
currentPosition = Integer.valueOf(br.readLine());
conn.setRequestProperty("Range", "bytes="+currentPosition+"-"+endIndex);
System.out.println("原来有下载进度,从上一次终止的位置继续下载"+"bytes="+currentPosition+"-"+endIndex);
fis.close();
raf.seek(currentPosition);//每个线程写文件的开始位置都是不一样的.
}else{
//告诉服务器 只想下载资源的一部分
conn.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex);
System.out.println("原来没有有下载进度,新的下载"+ "bytes="+startIndex+"-"+endIndex);
raf.seek(startIndex);//每个线程写文件的开始位置都是不一样的.
}
InputStream is = conn.getInputStream();
byte[] buffer = new byte[1024];
int len = -1;
while((len = is.read(buffer))!=-1){
//把每个线程下载的数据放在自己的空间里面.
//                System.out.println("线程:"+threadId+"正在下载:"+new String(buffer));
raf.write(buffer,0, len);
currentPosition+=len;
File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+threadId+".position");
RandomAccessFile fos = new RandomAccessFile(file,"rwd");
//System.out.println("线程:"+threadId+"写到了"+currentPosition);
fos.write(String.valueOf(currentPosition).getBytes());
fos.close();//fileoutstream数据是不一定被写入到底层设备里面的,有可能是存储在缓存里面.
//raf 的 rwd模式,数据是立刻被存储到底层硬盘设备里面.
int max = endIndex - startIndex;
int progress = currentPosition - startIndex;
if(threadId==0){
pb0.setMax(max);
pb0.setProgress(progress);
}else if(threadId==1){
pb1.setMax(max);
pb1.setProgress(progress);
}else if(threadId==2){
pb2.setMax(max);
pb2.setProgress(progress);
}
}
raf.close();
is.close();
System.out.println("线程:"+threadId+"下载完毕了...");
File f = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+threadId+".position");
f.renameTo(new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+threadId+".position.finish"));
synchronized (MainActivity.class) {
runningThreadCount--;
if(runningThreadCount<=0){
for(int i=0;i<threadCount;i++){
File ft = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+i+".position.finish");
ft.delete();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}


/**
* 获取一个文件名称
* @param path
* @return
*/
public String getFileName(String path){
int start = path.lastIndexOf("/")+1;
return path.substring(start);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: