网络编程2
--------此笔记根据黑马程序员的授课视频所记录
Activity
网络编程1
网络编程2
广播接收者
服务
内容提供者
多媒体
1.使用httpurlconnection方式把数据提交到服务器
基于http协议
【1】get和post提交的区别
路径不同
get方式提交:组拼URL地址把数据组拼到URL上 大小限制1kb(浏览器规定) 4kb(http协议规定)
post方式提交:post比get方式提交安全 数据没有大小限制 通过请求体的方式把数据写给服务器(也是以流的形式)
【2】get方式提交数据 [联网记住要加权限]
//【2.1】定义get方式要提交的路径
String path = "http://***.***.***.***:8080?username="+name+"&pwd="+pwd;
//【2.2】获取httpurlconnection
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
int code = conn.getResponseCode();
if (code == 200) {
//请求成功后的逻辑
//获取服务器返回的数据 以流的形式返回
InputStream in = conn.getInputStream();
//通过工具类将流转换成字符串 InputStream in ➡ String content;
//把服务器返回的数据展示到Toast上 (因为是网络请求所以此处就是子线程)
runOnUiThread(new Runable(){
public void run(){
Toast.makeText(getApplicationContext(),content,1).show();
}
});
}
【3】post方式提交数据
//【3.1】定义post方式要提交的路径
String path = "http://***.***.***.***:8080";//此处与get不同**********
//获取请求体
String data = "username="+name+"&pwd="+pwd;
//【2.2】获取httpurlconnection
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("POST");//此处与get不同***
//比get多设置两个请求头信息******
conn.setRequestProperty("Content-Type","application/x-www-form=urlencoded");//里面参数是一个key:value的类型
conn.setRequestProperty("Content-Lenght",data.length()+"");
//把组拼好的数据提交给服务器 以流的形式*******
conn.setDoOutput(true);//设置一个标记允许输出
conn.getOutStream().write(data.getBytes());
int code = conn.getResponseCode();
if (code == 200) {
//请求成功后的逻辑
//获取服务器返回的数据 以流的形式返回
InputStream in = conn.getInputStream();
//通过工具类将流转换成字符串 InputStream in ➡ String content;
//把服务器返回的数据展示到Toast上 (因为是网络请求所以此处就是子线程)
runOnUiThread(new Runable(){
public void run(){
Toast.makeText(getApplicationContext(),content,1).show();
}
});
}
2.乱码问题解决
——————实际开发时不需要考虑,会有架包搞定
因为客户端与服务端之间传输数据时是通过二进制传输的,所以解决乱码问题就是让服务端和服务端的编码格式相同就好了,哪个不一样改哪个,tomcat的默认编码格式是iso-8859-1,然而发送时的默认编码格式是gbk,在安卓当中编码格式默认都是utf-8;
【1】解决服务端发送到客户端的乱码问题
【1.1】服务端解决乱码问题
/*例*/
response.getOutputStream().write("返回服务器的信息".getBytes("utf-8"));
【1.2】客户端解决乱码问题
/*例*/
String content = new String (baos.toByteArray(),"gbk");//此处是将从服务端拿过来的流信息转换为字符串时用gbk方式解码
【2】解决客户端发送到服务端的乱码问题
/*例*/
//原理就是数据接收到后先以iso-8859-1的编码格式进行解码,然后再以utf-8的形式进行转码
new String(name.getBytes("iso-8859-1"),"utf-8");//其中name就是从客户端获取到的数据
//但是如果只这样进行转码的话再客户端还为乱码,但是再网页访问可以,因为网页给数据进行了一个url编码所以在Android中如果要进行转码需要将数据进行url的编码。此时有一个java类可以直接实现
URLEncoder.encode("要编码的内容","utf-8");
3.以httpclient方式把数据提交到服务器
开源项目,阿帕奇的开源项目,谷歌拿过来封装好了 就是对http请求的封装
谷歌一般以 base defalut simple 进行命名
【1】get方式提交数据
//获取httpclient实例
DefaultHttpClient client = new DefaultHttpClient();
//准备一个get请求 定义一个Httpget实现
HttpGet get = new HttpGet(path);//path为服务器地址
//执行一个get请求
HttpResponse response = client.execute(get);
//获取服务器返回的状态码
int code = response.getStatusLine().getStatusCode();//返回getStatusLine()状态行,返回getStatusCode()状态码
if(code == 200){
//获取服务器返回的数据 以流的形式返回
InputStream inputStream = response.getEntity().getContent(); //getEntity()获取一个实体,getContent()获取一个流
//把流通过工具类转换成一个字符串 InputStream inputStream ➡ String content
//打印土司(记住因为现在是访问网络所以是在子线程中,打印土司用个runOnUiThread();)
}else{
}
【2】post方式提交数据
String path = "http://***.***.***.***:8080";
//获取httpclient实例
DefaultHttpClient client = new DefaultHttpClient();
//准备post请求
HttpPost post = new HttpPost(path);
//准备parameters
List<NameValuePair> parameters = new ArrayList<NameValuePair>();
//准备NameValuePair 实际上就是我们要提交的用户名密码 第一个参数key是服务器的key
BasicNameValuePair nameValuePair = new BasicNameValuePair("username",name);
BasicNameValuePair pwdValuePair = new BasicNameValuePair("password",pwd);
//将值加入到parameters集合中
parameters.add(nameValuePair);
parameters.add(pwdValuePair);
//准备entity
UrlEncodeFormEntity entity = new UrlEncodeFormEntity(parameters);
//以实体的形式准备post提交的正文信息(键值对的形式)
post.setEntity(entity);
//执行一个post请求
HttpResponse response = client.execute(post);
//获取服务器返回的状态码
int code = response.getStatusLine().getStatusCode();//返回getStatusLine()状态行,返回getStatusCode()状态码
if(code == 200){
//获取服务器返回的数据 以流的形式返回
InputStream inputStream = response.getEntity().getContent(); //getEntity()获取一个实体,getContent()获取一个流
//把流通过工具类转换成一个字符串 InputStream inputStream ➡ String content
//打印土司(记住因为现在是访问网络所以是在子线程中,打印土司用个runOnUiThread();)
}else{
}
4.开源项目方式把数据提交到服务器
5.javase多线程下载
设置请求服务器文件的位置
conn.setRequestProperty("Range","bytes="+startIndex+"-"+endIndex);//此头信息是告诉服务器此线程访问的数据开始位置和结束位置
【1】介绍
【1.1】多线程加速下载,但是不是说线程越多下载越快,手机迅雷这款软件它建议是开3-4个线程
【1.2】受服务器带宽的影响,受自己本身网络限制
【1.3】多线程是拿了更多的cpu资源
【2】步骤
【2.1】获取文件大小
【2.2】在客户端创建一个大小和服务器一摸一样的文件 提前申请好空间(此步可以省略)
【2.3】计算好每一个下载的开始位置和结束位置
【2.4】开多个线程取下载文件
【2.5】知道每个线程什么时候下载完毕了
/*
*实例代码
*/
private static String path = "http://***.***.***.***:8080/xxx文件.exe";//定义下载路径
private static final int threadCount = 3; //假设开的线程数为3
private static final int runningThread;//表示正在运行的线程数量
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
int code = conn.getResponseCode();
if (code == 200) {
//[1]获取服务器文件大小
int length = conn.getContentLength();
//把线程的数量赋值给正在运行的线程数
runningThread = threadCount;
//[2]创建出一个大小和服务器上的一摸一样的文件 目的是提前把空间申请出来
RandomAccessFile rafAccessFile = new RandomAccessFile("文件名","rm");//第一个参数是生成的文件名,第二个参数是生成文件的模式。用此方法创建的文件是可以随机的访问读取和写入
//设置生成文件的长度
rafAccessFile.setLength(length);
//[3]计算开始结束位置
int blockSize = lenght/threadCount;
for(int i=0;i<threadCount;i++){
int startIndex = i*blockSize //计算出每个线程下载的开始位置
int endIndex = (i+1)*blockSize-1;//计算出每个线程下载的结束位置
//特殊情况最后一个线程
if(i == treadCount-1){
endIndex = length-1;
}
}
//[4]开启线程去下载文件
DownLoadThread downLoadThread = new DownLoadThread(startIndex,endIndex,threadId);
downLoadThread.start();
}else{
}
//定义一个线程去服务器下载文件
public static class DownLoadThread extends Thread{
//通过构造方法把每个线程下载的开始位置和结束位置传进来
private int startIndex;
private int endIndex;
private int threadId;
public DownLoadThread(int startIndex,int endIndex,int threadId){
this.startIndex = int startIndex;
this.endIndex = int endIndex;
this.threadId = int threadId;
}
public void run(){
//实现去服务器下载文件的逻辑
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
conn.setRequestProperty("Range","bytes="+startIndex+"-"+endIndex);//此头信息是告诉服务器此线程访问的数据开始位置和结束位置
int code = conn.getResponseCode();//206代表请求部分资源成功 200是请求全部资源成功
if (code == 206) {
//创建随机读写对象
RandomAccessFile raf = new RandomAccessFile("文件名","rm");
//每个线程从自己的位置开始写
raf.seek(startIndex);
InputStream in = conn.getInputStream();//现在这个流就是下载返回的数据
//把数据写到文件中
int len = -1;
byte[] buffer = new byte[1024];
while((len == in.read(buffer))!=-1){
raf.write(buffer,0,len);
}
raf.close();//关闭流释放资源
}
}
6.断点续传实现
原理:存一个文件记录上次下载到的位置,再次下载的时候检测此文件是否存在,若文件存在则提取里面记录的位置信息,将startIndex的位置变成此次记录的位置,然后再开始下载
所以说要实现断点续传只需要更改去服务下载文件的这个类,下面代码为更改上一步的代码
//定义一个线程去服务器下载文件(添加了断点续传的功能版)
public static class DownLoadThread extends Thread{
//通过构造方法把每个线程下载的开始位置和结束位置传进来
private int startIndex;
private int endIndex;
private int threadId;
public DownLoadThread(int startIndex,int endIndex,int threadId){
this.startIndex = int startIndex;
this.endIndex = int endIndex;
this.threadId = int threadId;
}
public void run(){
//实现去服务器下载文件的逻辑
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
//***********
//如果中间断过 继续上次的位置下载 继续下载 从文件里读取上次下载的位置
File file = new File(threadId+".txt");
if (file.exists()&&file.length()>0){
FileInputStream fis = new FileInputStream(file);
BufferedReader bufr = new BufferedReader(new InputStreamReader(fis));
String SlastPosition = bufr.readLine();//此处读取出来的就是上一次下载的内容
int lastPosition = Integer.parseInt(SlastPosition);
startIndex = lastPosition;
fis.close();
}
//***********
conn.setRequestProperty("Range","bytes="+startIndex+"-"+endIndex);//此头信息是告诉服务器此线程访问的数据开始位置和结束位置
int code = conn.getResponseCode();//206代表请求部分资源成功 200是请求全部资源成功
if (code == 206) {
//创建随机读写对象
RandomAccessFile raf = new RandomAccessFile("文件名","rm");
//每个线程从自己的位置开始写
raf.seek(startIndex);
InputStream in = conn.getInputStream();//现在这个流就是下载返回的数据
//把数据写到文件中
int len = -1;
byte[] buffer = new byte[1024*1024];//1mb
int total = 0; //代表当前线程下载的大小**********
while((len == in.read(buffer))!=-1){
raf.write(buffer,0,len);
//**********
total+=len;
//实现断点续传 就是把当前线程下载的位置给存起来下次再下载的时候 就是按照上次下载的位置继续下载就可以了
int currentThreadPosition = startIndex+total//当前线程下载的大小加上起始位置就是当前下载的位置,然后存入一个txt文本中去
//接下开始将当前位置信息存入到当前线程的文本中去
RandomAccessFile raff = new RandomAccessFile(threadId+".txt","rwd");//存入的名称为当前线程的id.txt,模式为rwd
raff.write(String.valueOf(currentThreadPosition).getBytes());
raff.close();
//**********
}
raf.close();//关闭流释放资源
//**********
//下载完成后删除记录位置的文件
synchronized (DownLoadThread.class){//加一个线程锁避免冲突
runningThread--
if(runningThread == 0){//当runningThread等于0了,就代表所有的线程都下载完毕了,就可以开始删除文件了
for(int i=0;i<threadCount;i++){
File deleteFile = new File(i+".txt");
deleteFile.delete();
}
}
}
//**********
}
}