第13.2.1讲 使用URL和URLConnection访问网络资源
1 URL
1.1 URL格式回顾
通常情况而言,URL可以由协议名、主机、端口和资源组成.
格式:protocol://host:[port]/path/[parameters] [?query], 例如:
补充:格式中:?query(查询):用于给动态网页传递参数,可有多个参数,用“&”符号隔开,每个参数的名和值用“=”符号隔开;
1.2 类URL的构造方法
(1.2.1)public URL(String spec);
(1.2.2)public URL(String protocol,String host,String file);
(1.2.4)public URL(String protocol,String host,int port,String file);
1.3 类URL的主要成员函数
(1.3.1)InputStream openStream(),打开与此URL的连接,并返回一个用于读取该RUL资源的InputStream;
InputStream is = url.openStream();
(1.3.2)URLConnection openConnection(),返回一个URLConnection对象,表示所引用的远程对象的连接;为后续URLConnection和HttpURLConnection服务;
URLConnection conn = url.openConnection();
1.4 直接通信的步骤和局限性
(1.4.1) 使用步骤就三步,第一步构建一个URL;第二步打开一个输入流,然后读取流;第三步关闭输入流;
(1.4.2)通过url.openStream()虽然能直接打开网络资源,由于没有个性化的网络超时等,除了演示和后台服务中使用外,在UI的子线程中很少使用;
1.5 案例应用代码片段
//功能演示1:从网络上下载一幅图片,显示Activity的ImageView上;
//功能演示2:从网络上下载一幅图片,保存到私有文件区下;
public class URLTestActivity extends Activity
{
//用于显示图片
ImageView mImageView;
//代表从网络下载得到的图片
Bitmap mBitmap;
//图片下载完成后通过mBitmapHandler通知URLTestActivity刷新界面
Handler mBitmapHandler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
if(msg.what == 0x123)
{
// 使用ImageView显示该图片
mImageView.setImageBitmap(mBitmap);
}
}
};
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mImageView = (ImageView) findViewById(R.id.urlImageView);
//启动一个匿名线程下载图片
new Thread()
{
public void run()
{
try
{
// 定义一个URL对象
URL url = new URL("
http://img5.imgtn.bdimg.com/"+
"it/u=1806774939,1961872736&fm=21&gp=0.jpg");
//---------功能演示1
// 打开该URL对应的资源的输入流
InputStream is = url.openStream();
// 从InputStream中解析出图片放到成员变量mBitmap中,
// 注意:此种方式应用比较普遍
mBitmap = BitmapFactory.decodeStream(is);
// 发送消息、通知UI组件显示该图片
handler.sendEmptyMessage(0x123);
is.close();
//---------功能演示2
// 再次打开URL对应的资源的输入流
is = url.openStream();
// 打开手机文件对应的输出流
OutputStream os = openFileOutput("demo.png"
, MODE_WORLD_READABLE);
byte[] buff = new byte[1024];
int hasRead = 0;
// 将URL对应的资源下载到本地
while((hasRead = is.read(buff)) > 0)
{
os.write(buff, 0 , hasRead);
}
is.close();
os.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}.start();
}
};
2 .URLConnection
2.1 构造
(2.1.1)URLConnection是一个抽象类,不能直接用new来实例化;
(2.1.2)URL的openConnection()方法将返回一个URLConnection对象,该对象表示应用程序和URL之间的通信连接;
2.2 通讯
URLConnection的通讯区分GET模式和POST两种模式,每种模式的使用步骤都不相同; 2.2 get模式的 使用步骤
(2.2.1)确定URL的地址,参数组,构建URL。get方式的网址特征为:网址+连接符?+参数组;不同参数之间用 & 连接;参数跟值之间用=连接;例如:
String uri = “
http://localhost/dangdang/login.jsp”;
String params = “userName=value1&loginPwd=value2& …”;
URL url = new URL(uri +"?"+ params);
(2.2.2)通过URL对象的openConnection()方法来创建URLConnection对象。
(2.2.3)设置URLConnection对象的常用的头字段内容和扩展头字段内容,例如超时、缓存、长连接等(get模式不需要设置流的启用);
(2.2.4)通过URLConnection对象connect()方法建立和远程资源之间的实际连接即可,即执行方法connect()时内部就以pos协议方式执行发送。
(2.2.5)然后进入接收阶段,通过URLConnection对象的getHeaderFields()函数读返回的头信息,按需解析流内部内容;
(2.2.6)通过URLConnection对象的getInputStream()函数获取其输入流,读取输入流的内容;按需解析;
2.3 post 使用步骤
(2.3.1)确定URL的地址,参数组,构建URL。post方式的网址特征为:网址是网址,参数组是参数组;不同参数之间用 & 连接;参数跟值之间用=连接;例如:
String uri = “
http://localhost/dangdang/login.jsp”;
String params = “userName=value1&loginPwd=value2& …”;
URL url = new URL(uri);
(2.3.2)通过URL对象的openConnection()方法来创建URLConnection对象。
(2.3.3)设置URLConnection对象的常用的头字段内容和扩展头字段内容,例如超时、缓存、长连接等。
(2.3.4)设置URLConnection的输入流和输出流为可用,只有设置启用后才能获取其对应的流。
//代码片段
//设置URLConnection对象在发送POST请求时,连接可读入输入信息。
conn.setDoInput(true)
//设置URLConnection对象在发送POST请求时,连接可以输出信息。
conn.setDoOutput(true);
(2.3.5通过getOutputStream()获得输出流对象,进而发送请求参数;(post模式改connect为写参数);
//生成一个字符打印流
os = new PrintWriter(conn.getOutputStream());
// 发送请求参数
os.print(parms);
// flush输出流的缓冲
os.flush();
(2.3.6)然后进入接收阶段,通过URLConnection对象的getHeaderFields()函数读返回的头信息,按需解析流内部内容;
(2.3.7)过URLConnection对象的getInputStream()函数获取其输入流,读取输入流的内容;按需解析;
2.4. GET和POS步骤比较
步骤 | GET | POST |
---|---|---|
URL连接串 | 主地址串+?+参数串 | 主地址串 |
打开连接 | openConnection | openConnection |
设置头域 | 设置主头域+扩展头域 | 设置主头域+扩展头域 |
检查头域 | -- | 必须启动输入流和输出流 |
输出参数 | -- | 通过输出流格式输出参数串 |
连接服务器 | connect() | -- |
解析头域 | getHeaderFields() | getHeaderFields() |
解析输入流 | getInputStream() | getInputStream() |
释放资源 | 释放流资源、网络连接资源 | 释放流资源、网络连接资源 |
2.5. 相关字节字符流函数的主要方法
(2.5.1.) InputStream 字节输入流;用于从字节流中读字节数组;例如:
...
// 打开该URL对应的资源的输入流
InputStream is = url.openStream();
byte[] buff = new byte[1024];
int hasRead = 0;
// 将URL对应的资源下载到本地
while((hasRead = is.read(buff)) > 0)
{
...
}
(2.5.2.) BufferedReader 字符流阅读器;用于从字符流中读字符串;例如:
...
URLConnection conn = url.openConnection();
...
//注意重点1 利用conn的输入流生成一个字符流阅读器,
BufferedReader in = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
//从字符阅读器中读字符串;
String line;
//注意重点2
while ((line = in.readLine()) != null){
result += "\n" + line;
}
...
(2.5.3.)PrintWriter向文本输出流打印对象的格式化表示形式;例如:
public testByPost(String urlStr,String... argStr) {
URL readUrl = new URL(urlStr);
// 打开和URL之间的连接
URLConnection conn = readUrl.openConnection();
// 设置通用的请求属性
...
//注意重点1 把输出流转化为格式文本打印流
PrintWriter out = new PrintWriter(conn.getOutputStream());
//重新组织参数串
String params="";
for(String item : argStr){
...
}
//处理完后的格式应该是这样的 String params = “userName=value1&loginPwd=value2& …”;
...
// 发送请求参数
out.print(params);
//注意重点2 flush输出流的缓冲
out.flush();
...
//处理返回数据 见上例代码
...
2.6. URLConnection常用的头字段内容
(2.6.1.)conn.setDoInput(boolean param):设置URLConnection对象在发送POST请求时,连接可读入输入信息。;
(2.6.2.)conn.setDoOutput(boolean param):设置URLConnection对象在发送POST请求时,连接可以输出信息。 如果设置为真,则认为是POST模式。
(2.6.3.)conn.setUseCaches(boolean param):设置使用缓存。
(2.6.4.)conn. setConnectTimeout(int msecond):设置连接网络超时时间,以毫秒为单位;
(2.6.5.)conn.setReadTimeout(10 * 1000); 设置流读的最长超时时间;
(2.6.6.)conn.setRequestMethod("POST"), 设置请求协议为POST或者GET;
2.7. URLConnection常用的扩展头字段内容
(2.7.1.)扩展头字段的函数原型: public void conn.setRequestProperty (String field,String newValue);
(2.7.2.) conn.setRequestProperty(“accept”,”*/*”); //设置请求资源的类型为全部;
(2.7.2.) conn.setRequestProperty(“connection”,”Keep-Alive”);//设置连接保持长连接。
(2.7.3.)conn.setRequestProperty("Charsert", "UTF-8");置使字符集。
(2.7.4.)
conn.setRequestProperty("user-agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");设置协议包头识别字,可以是用户自定义内容;目前经常被自定义为用于识别苹果手机、安卓手机等;
(2.7.5.)conn.setRequestProperty("Content-Type", MULTIPART_FROM_DATA
+ ";boundary=" + BOUNDARY) 内容类型,一般在文件上传时需要设置本属性,其它的都不设置;
2.8. 如何查看头字段都有哪些内容
(2.8.1.)终端盒服务器端通信时,双方都是根据头字段的内容对后续的正文进行解析,了解都有哪些头字段成为必要;
(2.8.2.) Map<String, List<String>> map = conn.getHeaderFields()获取头字段的HASHMAP,例子:
// 获取所有响应头字段
Map> map = conn.getHeaderFields();
//显示一下返回的头字段,大家可以学习一下都有哪些扩展属性
for (String key : map.keySet())
{
Log.d("GET", key + "--->" + map.get(key));
}
//如果返回类型是"text/html",进入对应的解析
List type=map.get("Content-Type");
if(type != null && type.contains("text/html")){
...
}
(2.8.2.) URLConnection还提供了另外一种快捷存取的方法函数,常见的有:
(2.8.3.1) conn.getContentEncodeing:获取content-encoding相应头字段的值;
(2.8.3.2)
conn.getContentLength:获取content-length相应头的长度;
(2.8.3.3)
conn.getContentType:获取content-type相应的值;
3. 案例实训
演示(Fragment版)
3.1 需求
需求1
从百度图片上找到4副图片,把对应的完整网址记录到字符串数组中;
需求2
在MainActivity内部署3排按钮,每排各4个,后续操作时各对应一副图片;部署一个TextView用于显示操作提示;部署一个ImageView用于显示百度图片;
需求3
点击第一组按钮时,通过URL的输入流获取图片,存入Bitmap变量中,然后显示到ImageView上,同时修改提示;
需求4
点击第二组按钮时,通过URLConnectde get方式从输入流获取图片,存入Bitmap变量中,然后显示到ImageView上,同时修改提示;
需求5
点击第三组按钮时,通过URLConnectde POST方式从输入流获取图片,存入Bitmap变量中,然后显示到ImageView上,同时修改提示;
3.2 步骤1 准备主布局、Demo13020101Fragment类、百度图片
//图片资源如下,其余内容省略
String mUrl1[]=new String[]{
};
String mUrl2[]=new String[]{
"
http://image.baidu.com/detail/newindex?col=搞笑&tag=百思不得姐&pn=5&pid=11224252012&aid=11568319695&user_id=10086&setid=-1&sort=0&newsPn=&fr=&from=1",
"
http://image.baidu.com/detail/newindex?col=搞笑&tag=百思不得姐&pn=1&pid=11568319695&aid=11568319695&user_id=10086&setid=-1&sort=0&newsPn=&fr=&from=1",
"
http://image.baidu.com/detail/newindex?col=搞笑&tag=熊孩子&pn=17&pid=11736427002&aid=11736427002&user_id=10086&setid=-1&sort=0&newsPn=&fr=&from=1",
"
http://image.baidu.com/detail/newindex?col=搞笑&tag=全部&pn=10&pid=9857966033&aid=9857966033&user_id=10086&setid=-1&sort=0&newsPn=&fr=&from=1"
};
3.3 步骤2 准备Button的事件
/***
* 初始化Fragment内部的组件
*/
private void initViewInRootView(View rootView){
//找到第1组按钮
mButton11=(Button)rootView.findViewById(R.id.button11);
mButton12=(Button)rootView.findViewById(R.id.button12);
mButton13=(Button)rootView.findViewById(R.id.button13);
mButton14=(Button)rootView.findViewById(R.id.button14);
//找到第2组按钮
mButton21=(Button)rootView.findViewById(R.id.button21);
mButton22=(Button)rootView.findViewById(R.id.button22);
mButton23=(Button)rootView.findViewById(R.id.button23);
mButton24=(Button)rootView.findViewById(R.id.button24);
//找到第3组按钮
mButton31=(Button)rootView.findViewById(R.id.button31);
mButton32=(Button)rootView.findViewById(R.id.button32);
mButton33=(Button)rootView.findViewById(R.id.button33);
mButton34=(Button)rootView.findViewById(R.id.button34);
//找到提示和图片组件
mTextView=(TextView)rootView.findViewById(R.id.textView1);
mImageView=(ImageView)rootView.findViewById(R.id.imageView1);
//设置按钮的点击事件侦听器
mButton11.setOnClickListener(mButtonOnClickListener);
mButton12.setOnClickListener(mButtonOnClickListener);
mButton13.setOnClickListener(mButtonOnClickListener);
mButton14.setOnClickListener(mButtonOnClickListener);
mButton21.setOnClickListener(mButtonOnClickListener);
mButton22.setOnClickListener(mButtonOnClickListener);
mButton23.setOnClickListener(mButtonOnClickListener);
mButton24.setOnClickListener(mButtonOnClickListener);
mButton31.setOnClickListener(mButtonOnClickListener);
mButton32.setOnClickListener(mButtonOnClickListener);
mButton33.setOnClickListener(mButtonOnClickListener);
mButton34.setOnClickListener(mButtonOnClickListener);
}
//按钮的点击事件侦听器
private View.OnClickListener mButtonOnClickListener=new View.OnClickListener(){
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch(v.getId()){
case R.id.button11:
demoByURL(0);
break;
case R.id.button12:
demoByURL(1);
break;
case R.id.button13:
demoByURL(2);
break;
case R.id.button14:
demoByURL(3);
break;
case R.id.button21:
demoByGET(0);
break;
case R.id.button22:
demoByGET(1);
break;
case R.id.button23:
demoByGET(2);
break;
case R.id.button24:
demoByGET(3);
break;
case R.id.button31:
demoByPOST(0);
break;
case R.id.button32:
demoByPOST(1);
break;
case R.id.button33:
demoByPOST(2);
break;
case R.id.button34:
demoByPOST(3);
break;
}
}
};
3.4 步骤3 准备处理demoByURL函数的处理内容
(3.4.1)生成一个匿名线程,重写run()。
(3.4.2)根据传入的参数序号提取图片的网址,构造一个URL的实例 url。
(3.4.3)取url的输入流存入到 is中。
(3.4.4)利用BitmapFactory从is中读取Bitmap数据放到mBitmap中。
(3.4.5)在Fragment内生成一个Handler类型的成员变量 mImageViewHandler,并在其handleMessage方法中利用mBitmap刷新ImageView中的显示图片;
(3.4.6)继续在线程的run中利用mImageViewHandler发送一个图片下载成功的消息;
(3.4.7)处理流关闭善后工作;
//URL的演示
private void demoByURL(final int buttonIndex){
//如果当前已经有线程在运行,则不要继续
if(mThreadIsRun)
return;
mThreadIsRun=true;
mTextView.setText("等待:demoByURL("+buttonIndex+")");
//启动一个匿名线程下载图片
new Thread() {
public void run() {
InputStream is=null;
try {
// 定义一个URL对象
URL url = new URL(mUrl1[buttonIndex]);
// 打开该URL对应的资源的输入流
is = url.openStream();
// 从InputStream中解析出图片放到成员变量mBitmap中,
// 注意:此种方式应用比较普遍
mBitmap = BitmapFactory.decodeStream(is);
// 发送消息、通知UI组件显示该图片
mImageViewHandler.sendEmptyMessage(0x123);
}catch (Exception e){
e.printStackTrace();
}finally {
if(is !=null){
try {
is.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//设置线程运行结束
mThreadIsRun=false;
}
}
}.start();
}
3.5 步骤4 准备处理demoByGET函数的处理内容
(3.5.1)生成一个匿名线程,重写run()。
(3.5.2)根据传入的参数序号提取图片的网址,构造一个URL的实例 url。
(3.5.3)通过 url.openConnection()获取一个连接放到变量conn中。
(3.5.4)设置conn的设置通用的请求属性和设置通用的扩展 请求属性。
(3.5.5)执行conn的connect建立实际连接;
(3.5.6) 获取所有响应头字段,显示一下头字段内容;
(3.5.7) 获取输入流,利用BitmapFactory从is中读取Bitmap数据放到mBitmap中;
(3.5.8)继续在线程的run中利用mImageViewHandler发送一个图片下载成功的消息;
(3.5.9)处理流关闭善后工作;
//GET的演示
private void demoByGET(final int buttonIndex){
//如果当前已经有线程在运行,则不要继续
if(mThreadIsRun)
return;
mThreadIsRun=true;
mTextView.setText("等待:demoByGET("+buttonIndex+")");
//启动一个匿名线程下载图片
new Thread() {
public void run() {
InputStream is=null;
try {
// 定义一个URL对象
URL url = new URL(mUrl1[buttonIndex]);
// 打开和URL之间的连接
URLConnection conn= url.openConnection();
//-----设置通用的请求属性
//连接可读入输入信息
//conn.setDoInput(true);
//设置不使用缓存。
conn.setUseCaches(false);
//设置连接网络超时时间,以毫秒为单位;
conn.setConnectTimeout(20*1000);
//设置流读的最长超时时间
conn.setReadTimeout(10 * 1000);
//-----设置通用的扩展 请求属性
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
// 建立实际的连接
conn.connect();
// 获取所有响应头字段
Map> map = conn.getHeaderFields();
//显示一下返回的头字段,大家可以学习一下都有哪些扩展属性
for (String key : map.keySet())
{
Log.d("GET", key + "--->" + map.get(key));
}
//检验一下返回的附件类型
List type=map.get("Content-Type");
if(type != null && type.contains("image/jpeg")){
}
//获取连接的输入流
is=conn.getInputStream();
// 从InputStream中解析出图片放到成员变量mBitmap中,
// 注意:此种方式应用比较普遍
mBitmap = BitmapFactory.decodeStream(is);
// 发送消息、通知UI组件显示该图片
mImageViewHandler.sendEmptyMessage(0x123);
}catch (Exception e){
e.printStackTrace();
}finally {
try {
if(is !=null)
is.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//设置线程运行结束
mThreadIsRun=false;
}
}
}.start();
}
3.6 步骤5 准备处理demoByPOST函数的处理内容
(3.6.1)生成一个匿名线程,重写run()。
(3.6.2)根据传入的参数序号提取图片的网址。
(3.6.3)把网址拆分成地址和参数两个部分,利用地址构造一个URL。
(3.6.4)通过 url.openConnection()获取一个连接放到变量conn中。
(3.6.5)设置conn的设置通用的请求属性和设置通用的扩展 请求属性。
(3.6.6)检查通用头字段属性中的输入流和输出流是否启动;
(3.6.7)获取conn的输出流,并用输出流构建一个格式输出字符流读写器保存到变量os中;
(3.6.8)利用os输出URL的参数部分,并提交;
(3.6.9) 获取所有响应头字段,显示一下头字段内容;
(3.6.10)获取输入流,利用BitmapFactory从is中读取Bitmap数据放到mBitmap中;
(3.6.11)继续在线程的run中利用mImageViewHandler发送一个图片下载成功的消息;
(3.6.12)处理流关闭善后工作;
//POST的演示
private void demoByPOST(final int buttonIndex){
//如果当前已经有线程在运行,则不要继续
if(mThreadIsRun)
return;
mThreadIsRun=true;
mTextView.setText("等待:demoByPOST("+buttonIndex+")");
//启动一个匿名线程下载图片
new Thread() {
public void run() {
InputStream is=null;
BufferedReader br=null;
PrintWriter os=null;
try {
// 定义一个URL对象
String[] urlStr=mUrl2[buttonIndex].split("\\?");
URL url = new URL(urlStr[0]);
//URL url = new URL(mUrl1[buttonIndex]);
// 打开和URL之间的连接
URLConnection conn= url.openConnection();
//-----设置通用的请求属性
//连接可读入输入信息
conn.setDoInput(true);
conn.setDoOutput(true);
//设置不使用缓存。
conn.setUseCaches(false);
//设置连接网络超时时间,以毫秒为单位;
conn.setConnectTimeout(20*1000);
//设置流读的最长超时时间
conn.setReadTimeout(10 * 1000);
//-----设置通用的扩展 请求属性
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
//POST是不需要连接 建立实际的连接
//conn.connect();
// 获取URLConnection对象对应的输出流
os = new PrintWriter(conn.getOutputStream());
// 发送请求参数
os.print(urlStr[1]);
// flush输出流的缓冲
os.flush();
// 获取所有响应头字段
Map> map = conn.getHeaderFields();
//显示一下返回的头字段,大家可以学习一下都有哪些扩展属性
for (String key : map.keySet())
{
Log.d("GET", key + "--->" + map.get(key));
}
String result="";
List type=map.get("Content-Type");
if(type != null && type.contains("text/html")){
// 定义BufferedReader输入流来读取URL的响应
br = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
String line;
while ((line = br.readLine()) != null)
{
result += "\n" + line;
}
}
//获取连接的输入流
is=conn.getInputStream();
// 从InputStream中解析出图片放到成员变量mBitmap中,
// 注意:此种方式应用比较普遍
mBitmap = BitmapFactory.decodeStream(is);
// 发送消息、通知UI组件显示该图片
mImageViewHandler.sendEmptyMessage(0x123);
}catch (Exception e){
e.printStackTrace();
}finally {
try {
if(is !=null)
is.close();
if(os !=null)
os.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//设置线程运行结束
mThreadIsRun=false;
}
}
}.start();
}
3.7 步骤6 加入权限
<!-- 互联网权限 -->
<uses-permission android:name="android.permission.INTERNET" />
3.8 步骤7 测试,发现POST时不能更新图片,原因在于百度服务POST时返回的是网页,不是图片;