网络编程
- 在Android中开发中,处处都需要去请求网络,处理网络请求的方式有很多种,有Android中自带的HTTPURLConnection,还有一些开源框架,如Volley,OkHttp,Xutils等
- 请求网络,就要想到断点续传和断点下载,这其中的原理又是什么呢?看下代码:
//联网获取文件的长度 算出每个线程下载的位置 开子线程
new Thread() {
public void run() {
//[一]获取服务器文件的大小 因为一会要开多个线程 方便计算下载的位置
try {
//[1.1]创建URL对象指定我们要访问的 网络(路径)
URL url = new URL(path);
//[1.2]拿到httpurlconnection对象 用于发送或者接收数据
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//[1.3]设置请求方式
conn.setRequestMethod("GET");//get要求大写 默认就是get请求
//[1.4]设置请求超时时间
conn.setConnectTimeout(5000);
//[1.5]获取服务器返回的状态码
int code = conn.getResponseCode();
//[1.6]如果code == 200 说明请求成功
if (code == 200) {
runningThred = threadCount;
//[1.7]获取服务器文件的大小
int length = conn.getContentLength();
System.out.println("length:"+length);
//[二]在客户端创建一个大小和服务器一模一样的文件
RandomAccessFile raf =new RandomAccessFile(getFileName(path), "rw");
raf.setLength(length); //文件创建出来 但是没有内容
//[三开启多个线程去下载 计算出每个线程下载的开始位置和结束位置]
int blockSize = length/threadCount; //每个线程下载的大小
for (int i = 0; i < threadCount; i++) {
int startIndex = i*blockSize;
int endIndex = (i+1)*blockSize-1;
//有特殊情况 就是最后一个线程 的结束位置 是文件长度-1
if (i==threadCount-1) {
endIndex = length-1;
}
System.out.println("当前线程下载的id:"+i+"理论开始位置和结束位置:"+startIndex+"~~~"+endIndex);
//四开启多个线程去下载我们指定路径的文件
DownLoadThread downLoadThread = new DownLoadThread(startIndex, endIndex, i);
downLoadThread.start();
}
}
} catch (Exception e) {
}
};
}.start();
- 通过查看代码,我们从网络下载数据的时候,创建一个线程,首先获取路径,创建URL,HTTPURLConnection对象,设置网络请求,获取网络中文件的大小,在本地创建一个和网络中大小相同的文件,记录每个线程下载的位置,开启线程去下载文件
private class DownLoadThread extends Thread{
private int startIndex;
private int endIndex;
private int threadId;
private int pbMax; //当前线程进度条的最大值
private int pbLastPositin; //代表进度条上次显示的位置
public DownLoadThread(int startIndex,int endIndex,int threadId){
this.startIndex= startIndex;
this.endIndex = endIndex;
this.threadId= threadId;
}
@Override
public void run() {
try {
//[1.0]算出当前线程进度条的最大值
pbMax = endIndex - startIndex;
//[1.1]创建URL对象指定我们要访问的 网络(路径)
URL url = new URL(path);
//[1.2]拿到httpurlconnection对象 用于发送或者接收数据
HttpURLConnection conn = (HttpURLConnection) url
.openConnection();
//[1.3]设置请求方式
conn.setRequestMethod("GET");//get要求大写 默认就是get请求
//[1.4]设置请求超时时间
conn.setConnectTimeout(5000);
File file = new File(getFileName(path)+threadId+".txt");
if (file.exists()&&file.length()>0) {
//说明下载中断过 继续上次的那个位置继续下 读取上次下载的位置
FileInputStream fis = new FileInputStream(file);
BufferedReader bufr = new BufferedReader(new InputStreamReader(fis));
String lastDownLoadPositionn = bufr.readLine(); //就是上次下载的位置
//改变一下当前下载的位置
startIndex = Integer.parseInt(lastDownLoadPositionn);
//把startIndex 赋值给pbLastPositin
pbLastPositin = startIndex;
fis.close();
System.out.println("当前线程下载的id:"+threadId+"真实下载的开始位置和结束位置:"+startIndex+"~~~"+endIndex);
}
//[1.4.1]设置Range 头 告诉服务器每个线程下载的开始位置和结束位置
conn.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex);
//[1.5]获取服务器返回的状态码
int code = conn.getResponseCode();
//[1.6]如果code == 200 说明请求成功 206请求部分资源成功
if (code == 206) {
//[1.7]获取服务器返回的数据 是以流的形式返回的
InputStream in = conn.getInputStream(); //这流里面的数据就是网络中的数据
RandomAccessFile raf =new RandomAccessFile(getFileName(path), "rw");
raf.seek(startIndex);//设置每个线程写入的位置
int len = 0;
int total=0; //代表当前线程下载的大小
byte[] buffer = new byte[1024*1024];//1Mb
while((len=in.read(buffer))!=-1){
total+=len;
//[1.8]实现断点续传的逻辑 就是把当前线程下载的位置给存起来 存到一个txt文本文件中
int currentThreadPosition = startIndex+total;
//[1.9]把currentThreadPostition位置给存起来
RandomAccessFile randomAccessFile =new RandomAccessFile(getFileName(path)+threadId+".txt", "rwd");
randomAccessFile.write(String.valueOf(currentThreadPosition).getBytes());
randomAccessFile.close();
raf.write(buffer, 0, len);
//[2.0]更新进度条的进度
pbs.get(threadId).setMax(pbMax); //设置进度条的最大值
pbs.get(threadId).setProgress(pbLastPositin+total);
}
raf.close();//关闭流
synchronized (DownLoadThread.class) {
//当所有的线程都下载完毕了 把.txt文件删除
runningThred--;
if (runningThred==0) {
for (int i = 0; i < threadCount; i++) {
File deleteFile = new File(getFileName(path)+i+".txt");
deleteFile.delete();
}
}
}
System.out.println("线程id:"+threadId+"下载完毕了");
}
} catch (Exception e) {
}
}
}
在这个方法里面进行网络下载,每次下载的时候判断之前是否下载过,文件是否存在,如果之前下载过,那么就从之前下载的位置下载,这就是断点下载的原理。
Volley
在Volley内部封装了请求网络的细节,我们只需简单的调用它的方法就可以去实现网络请求数据,Volley可是说是把AsyncHttpClient和Universal-Image-Loader的优点集于了一身,既可以像AsyncHttpClient一样非常简单地进行HTTP通信,也可以像Universal-Image-Loader一样轻松加载网络上的图片。对于大数据量的操作,Volley就表现的不如人意了。
Volley里面有非常方便的方法
- StringRequest 的使用
- 先创建一个请求队列
RequestQueue mQueue = Volley.newRequestQueue(context);
- 创建一个StringRequest对象,发送HTTP请求
StringRequest stringRequest = new StringRequest("http://www.baidu.com",
//传入一个响应成功回调的监听 和一个响应失败的监听
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.d("TAG", response);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e("TAG", error.getMessage(), error);
}
});
- 最后一步是将StringRequest对象添加到请求队列中
mQueue.add(stringRequest);
- 以上这是GET 请求,怎么发送post请求呢?其实跟GET请求类似,只不过在StringQuest的构造方法中添加请求的方式,默认是get
StringRequest stringRequest = new StringRequest(Method.POST, url, listener, errorListener) {
@Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String, String> map = new HashMap<String, String>();
map.put("params1", "value1");
map.put("params2", "value2");
return map;
}
};
- JsonRequest的使用
- JsonRequest也是继承Request类,不过JsonRequest是一个抽象类,它的子类有JsonObjectRequest和JsonArrayRequest,这里可以看出来,一个是用于请求一段JSON数据的,一个是用于请求一段JSON数组的。它们的使用方式和StringRequest方式是一样的,按照三步走。
- ImageRequest的使用
- 其实请求的图片的方式也是跟上面一样,就是new对象的时候参数多了一些
ImageRequest imageRequest = new ImageRequest(
"http://developer.android.com/images/home/aw_dac.png",
new Response.Listener<Bitmap>() {
@Override
public void onResponse(Bitmap response) {
imageView.setImageBitmap(response);
}
}, 0, 0, Config.RGB_565, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
imageView.setImageResource(R.drawable.default_image);
}
});
- 第二个和第三个参数是图片的宽高,如果设置为0,不会对图片进行压缩
- Volley中还有ImageLoader,它要比ImageQuest更加高效,因为它不仅可以帮我们队图片进行缓存,还可以过滤掉重复的连接,避免重复的发送请求,它内部也是使用ImageQuest来实现的
- 创建一个RequestQueue对象。
RequestQueue mQueue = Volley.newRequestQueue(context);
- 创建一个ImageLoader对象。
//第一个参数是请求队列,第二个是缓存对象
ImageLoader imageLoader = new ImageLoader(mQueue, new ImageCache() {
@Override
public void putBitmap(String url, Bitmap bitmap) {
}
@Override
public Bitmap getBitmap(String url) {
return null;
}
});
- 获取一个ImageListener对象。
/*第一个参数是ImageView,第二个参数是加载过程中需要显示的图片,第三个参数是加载失败的图片*/
ImageListener listener = ImageLoader.getImageListener(imageView,
R.drawable.default_image, R.drawable.failed_image);
- 调用ImageLoader的get()方法加载网络上的图片。
imageLoader.get(url, listener);
/*如果想要对图片的大小进行限制,可以使用这个方法*/
imageLoader.get(url,
listener, 宽, 高);
- 当创建请求队列对象的时候,它的内部默认会有五个线程在后台运行,不断等待网络请求的到来,默认情况下,每条请求都是可以缓存的,也可以调用Request的setShouldCache(false)方法来改变这一默认行为。
- 接下来开篇Okhttp