HttpClient
发送get请求
示例代码,表示的是以get方式发送请求,获得server返回的数据响应。这里表示的是
把示例代码写到button中,这些网络请求的都在子线程中执行
Thread t = new Thread(){
@Override
public void run() {
String path = "http://192.168.13.13/Web/servlet/CheckLogin?name=" + URLEncoder.encode(name) + "&pass=" + pass;
//使用httpClient框架做get方式提交
//1.创建HttpClient对象
HttpClient hc = new DefaultHttpClient();
//2.创建httpGet对象,构造方法的参数就是网址
HttpGet hg = new HttpGet(path);
//3.使用客户端对象,把get请求对象发送出去
try {
HttpResponse hr = hc.execute(hg);
//拿到响应头中的状态行
StatusLine sl = hr.getStatusLine();
if(sl.getStatusCode() == 200){
//拿到响应头的实体
HttpEntity he = hr.getEntity();
//拿到实体中的内容,其实就是服务器返回的输入流
InputStream is = he.getContent();
String text = Utils.getTextFromStream(is);//引用了下面工具类中的方法
//发送消息,让主线程刷新ui显示text
Message msg = handler.obtainMessage();
msg.obj = text;
handler.sendMessage(msg);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
- 一个通用的工具类,用来读取流中的数据并写入输出流中显示
public class Utils {
public static String getTextFromStream(InputStream is){
byte[] b = new byte[1024];
int len = 0;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
while((len = is.read(b)) != -1){
bos.write(b, 0, len);
}
String text = new String(bos.toByteArray());
bos.close();
return text;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
发送post请求
示例代码
与get方式相差不大,但post方式中,path不携带参数进行传递,因此要使用BasicNameValuePair 来传递携带
的参数数据,并把这些键值对存储在一个集合中。这些都要写在子线程中进行,如果相要把server上的一些数
据返回到客户端显示,还有用到消息队列和Handler来处理
public void post(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();
Thread t = new Thread(){
@Override
public void run() {
String path = "http://192.168.13.13/Web/servlet/CheckLogin";
//1.创建客户端对象
HttpClient hc = new DefaultHttpClient();
//2.创建post请求对象
HttpPost hp = new HttpPost(path);
//封装form表单提交的数据
BasicNameValuePair bnvp = new BasicNameValuePair("name", name);
BasicNameValuePair bnvp2 = new BasicNameValuePair("pass", pass);
List<BasicNameValuePair> parameters = new ArrayList<BasicNameValuePair>();
//把BasicNameValuePair放入集合中
parameters.add(bnvp);
parameters.add(bnvp2);
try {
//要提交的数据都已经在集合中了,把集合传给实体对象
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(parameters, "utf-8");
//设置post请求对象的实体,其实就是把要提交的数据封装至post请求的输出流中
hp.setEntity(entity);
//3.使用客户端发送post请求
HttpResponse hr = hc.execute(hp);
if(hr.getStatusLine().getStatusCode() == 200){
InputStream is = hr.getEntity().getContent();
String text = Utils.getTextFromStream(is);
//发送消息,让主线程刷新ui显示text
Message msg = handler.obtainMessage();
msg.obj = text;
handler.sendMessage(msg);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
t.start();
}
异步HTTPClient
原理:是使用了一些成型的开源框架来完成操作,目录如下,把开源框架中的http包
引入即可。
- 操作代码如下
这里相当于把在HTTPClient中操作的代码都封装起来了,所以显示的看起来很简洁,这里要重写了
AsyncHttpResponseHandler类中的两个方法,分别表示请求server成功和请求失败时所调用的方法,
这里为了简洁直接toast。
public void get(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();
String url = "http://192.168.0.103/Web/servlet/CheckLogin?name=" + URLEncoder.encode(name) + "&pass=" + pass;
//创建异步httpclient
AsyncHttpClient ahc = new AsyncHttpClient();
//发送get请求提交数据
ahc.get(url, new MyResponseHandler());
}
public void post(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();
String url = "http://192.168.0.103/Web/servlet/CheckLogin";
//创建异步httpclient
AsyncHttpClient ahc = new AsyncHttpClient();
//发送post请求提交数据
//把要提交的数据封装至RequestParams对象
RequestParams params = new RequestParams();
params.add("name", name);
params.add("pass", pass);
ahc.post(url, params, new MyResponseHandler());
}
class MyResponseHandler extends AsyncHttpResponseHandler{
//请求服务器成功时,此方法调用
@Override
public void onSuccess(int statusCode, Header[] headers,
byte[] responseBody) {
Toast.makeText(MainActivity.this, new String(responseBody), 0).show();
}
//请求失败此方法调用
@Override
public void onFailure(int statusCode, Header[] headers,
byte[] responseBody, Throwable error) {
Toast.makeText(MainActivity.this, "请求失败", 0).show();
}
}
多线程下载和断点续传的实现
这个示例是用的javase的知识实现
public class MultiDownload {
static int ThreadCount = 3;//定义系统线程数量
static int finishedThread = 0;
//确定下载地址
static String path = "http://192.168.0.103:8080/WebApp/1234.pdf";
public static void main(String[] args) {
//发送get请求,请求这个地址的资源
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
if(conn.getResponseCode() == 200){
//拿到所请求资源文件的长度
int length = conn.getContentLength();
File file = new File("1234.pdf");
//生成临时文件
RandomAccessFile raf = new RandomAccessFile(file, "rwd");
//设置临时文件的大小
raf.setLength(length);
raf.close();
//计算出每个线程应该下载多少字节
int size = length / ThreadCount;
for (int i = 0; i < ThreadCount; i++) {
//计算线程下载的开始位置和结束位置
int startIndex = i * size;
int endIndex = (i + 1) * size - 1;
//如果是最后一个线程,那么结束位置写死
if(i == ThreadCount - 1){
endIndex = length - 1;
}
// System.out.println("线程" + i + "的下载区间是:" + startIndex + "---" + endIndex);
new DownLoadThread(startIndex, endIndex, i).start();
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
这个类中定义了三个参数,分别表示每个线程的起始位置、结束位置和线程id。通过判断以线程id为编号的txt
文件是否存在,来判断是断点续传还是刚开始下载。
class DownLoadThread extends Thread{
int startIndex;
int endIndex;
int threadId;
public DownLoadThread(int startIndex, int endIndex, int threadId) {
super();
this.startIndex = startIndex;
this.endIndex = endIndex;
this.threadId = threadId;
}
@Override
public void run() {
//再次发送http请求,下载原文件
try {
File progressFile = new File(threadId + ".txt");
//判断进度临时文件是否存在,断点续传
if(progressFile.exists()){
FileInputStream fis = new FileInputStream(progressFile);
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
//从进度临时文件中读取出上一次下载的总进度,然后与原本的开始位置相加,得到新的开始位置
startIndex += Integer.parseInt(br.readLine());
fis.close();
}
System.out.println("线程" + threadId + "的下载区间是:" + startIndex + "---" + endIndex);
HttpURLConnection conn;
URL url = new URL(MultiDownload.path);
conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
//设置本次http请求所请求的数据的区间
conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);
//请求部分数据,相应码是206
if(conn.getResponseCode() == 206){
//流里此时只有1/3原文件的数据
InputStream is = conn.getInputStream();
byte[] b = new byte[1024];
int len = 0;
int total = 0;
//拿到临时文件的输出流
File file = new File("1234.pdf");
//这里使用RandomAccessFile是为了方便对文件进行操作,和同步更新
RandomAccessFile raf = new RandomAccessFile(file, "rwd");
//把文件的写入位置移动至startIndex
raf.seek(startIndex);
while((len = is.read(b)) != -1){
//每次读取流里数据之后,同步把数据写入临时文件
raf.write(b, 0, len);
total += len;
// System.out.println("线程" + threadId + "下载了" + total);
//生成一个专门用来记录下载进度的临时文件
RandomAccessFile progressRaf = new RandomAccessFile(progressFile, "rwd");
//每次读取流里数据之后,同步把当前线程下载的总进度写入进度临时文件中,有可能出现下载
//完毕却没有把进度写入临时文件的情况
progressRaf.write((total + "").getBytes());
progressRaf.close();
}
System.out.println("线程" + threadId + "下载完毕-------------------小志参上!");
raf.close();
//不能直接这样删除,因为多个线程的情况中,如果一个线程先下载完毕,那么
//会直接把对应线程的临时文件删除掉,当下次再下载时就会出现重新下载的情况
//progressFile.delete();
//这里如果三个线程都走到下面的一句代码时,满足条件,进行for循环,会删除9次,
//所以,这里加了同步语句块,并且删除完毕后要把finishedThread重置为零。
MultiDownload.finishedThread++;
synchronized (MultiDownload.path) {
if(MultiDownload.finishedThread == MultiDownload.ThreadCount){
for (int i = 0; i < MultiDownload.ThreadCount; i++) {
File f = new File(i + ".txt");
f.delete();
}
MultiDownload.finishedThread = 0;
}
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Android上的多线程下载和断点续传
方法和原理一样,基本把以上的代码移植到安卓中,其他的再修改一些就可以使用了
示例代码:这里在界面中添加一个button、一个ProgressBar和一个TextView,在界面上
显示的效果为当点击button时,ProgressBar会自动加载并且TextView中的文本框文字会自动的增加
直到下载完毕。在未下载完毕的某个时候,可以停止线程,再重新启动,会发现又会从停止的点进行
继续下载。这里要注意在清单文件中配置权限android.permission.INTERNET和
android.permission.WRITE_EXTERNAL_STORAGE
static int ThreadCount=3;
static int finishedThread=0;
String fileName="1.mp3";
String path="http://192.168.0.103:8080/WebApp/"+fileName;
ProgressBar pb;
int currentProgress=0;
TextView tv;
Handler handler=new Handler(){
public void handleMessage(android.os.Message msg) {
tv.setText((long)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);
}
button的点击事件
public void getClick(View v){
Thread thread=new Thread(){
@Override
public void run() {
try {
URL url=new URL(path);
HttpURLConnection connection=(HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
if(connection.getResponseCode()==200){
int len=connection.getContentLength();
pb.setMax(len);//取得进度条的最大值
File file=new File(Environment.getExternalStorageDirectory(),fileName);
RandomAccessFile raf=new RandomAccessFile(file, "rwd");
raf.setLength(len);
raf.close();
int size=len/ThreadCount;
for(int i=0;i<ThreadCount;i++){
int start=i*size;
int end=(i+1)*size-1;
if(i==ThreadCount){
end=len-1;
}
//System.out.println(i+":"+start+"---"+end);
new DownLoadThread(start, end, i).start();
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
thread.start();
}
- 这里在进行更新进度条和文本内容时,注意,按常规更新UI要用到主线程,需要用到消息处理器来操作
- 但ProgressBar不用,直接在子线程操作就好。而TextView必须更新到主线程中,因此用Handler来处理
class DownLoadThread extends Thread{
int startIndex;
int endIndex;
int threadId;
public DownLoadThread(int startIndex, int endIndex, int threadId) {
super();
this.startIndex = startIndex;
this.endIndex = endIndex;
this.threadId = threadId;
}
@Override
public void run() {
File progressFile=new File(Environment.getExternalStorageDirectory(),threadId+".txt");
HttpURLConnection connection;
try {
//这里表示下载了部分数据,未下载完全
if(progressFile.exists()){
FileInputStream fis=new FileInputStream(progressFile);
BufferedReader br=new BufferedReader(new InputStreamReader(fis));
int lastProgress=Integer.parseInt(br.readLine());
startIndex+=lastProgress;
currentProgress+=lastProgress;
pb.setProgress(currentProgress);//把上次下载的进度条显示至进度条
handler.sendEmptyMessage(1);
fis.close();
}
System.out.println("线程"+threadId+"区间"+startIndex+"=-----"+endIndex);
URL url=new URL(path);
connection=(HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
//设置下载部分
connection.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex);
//表示部分数据下载成功
if(connection.getResponseCode()==206){
InputStream is=connection.getInputStream();
byte[] b=new byte[1024];
int len=0;
int total=0;
File file=new File(Environment.getExternalStorageDirectory(),fileName);
RandomAccessFile raf=new RandomAccessFile(file, "rwd");
raf.seek(startIndex);
while((len=is.read(b))!=-1){
raf.write(b, 0, len);
total+=len;
//System.out.println("线程"+threadId+"下载了"+total);
currentProgress+=len;
pb.setProgress(currentProgress);
handler.sendEmptyMessage(1);
RandomAccessFile progressraf=new RandomAccessFile(progressFile, "rwd");
progressraf.write((total+"").getBytes());
progressraf.close();
}
System.out.println("线程" + threadId + "下载完毕-------------------小志参上!");
raf.close();
finishedThread++;
synchronized (path) {
if(finishedThread==ThreadCount){
for(int i=0;i<ThreadCount;i++){
File f=new File(Environment.getExternalStorageDirectory(),i+".txt");
f.delete();
finishedThread=0;
}
}
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
用开源框架的代码(xUtils)直接实现
- 添加jar包,来操作断点续传
示例代码
可以看到简单很多,只需要调用jar包中的一个方法,然后重写该方法参数中的方法即可。
private TextView tv_failure;
private TextView tv_progress;
private ProgressBar pb;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv_failure = (TextView) findViewById(R.id.tv_failure);
tv_progress = (TextView) findViewById(R.id.tv_progress);
pb = (ProgressBar) findViewById(R.id.pb);
}
public void click(View v){
HttpUtils utils = new HttpUtils();
String fileName = "1.mp3";
//确定下载地址
String path = "http://192.168.0.103:8080/" + fileName;
utils.download(path, //下载地址
"sdcard/QQPlayer.exe", //文件保存路径
true,//是否支持断点续传
true, new RequestCallBack<File>() {
//下载成功后调用
@Override
public void onSuccess(ResponseInfo<File> arg0) {
Toast.makeText(MainActivity.this, arg0.result.getPath(), 0).show();
}
//下载失败调用
@Override
public void onFailure(HttpException arg0, String arg1) {
// TODO Auto-generated method stub
tv_failure.setText(arg1);
}
@Override
public void onLoading(long total, long current,
boolean isUploading) {
// TODO Auto-generated method stub
super.onLoading(total, current, isUploading);
pb.setMax((int)total);
pb.setProgress((int)current);
tv_progress.setText(current * 100 / total + "%");
}
});
}
总结
这上面就是学习的文件下载和断点续传的流程,总之,和网络提交数据不可分割。对get和post提交数据的方
一定要掌握;还有一部分对文件读取的操作;多个线程操作文件时,怎么划分每个线程要操作的具体大小;
RandomAccessFile类的用法,动态显示文本和进度条的实现。