URL和URLConnection
URL(Uniform Resource Locator)
对象代表统一资源定位器
,
是指向互联网“资源”
的指针
。
这里的资源可以是简单的文件或目录
,
也可以是对更为复杂的对象引用
,
例如对数据库
或搜索引擎
的查询
。通常情况而言,
URL可以由协议名、主机、端口和资源
组成,
满足如下的格式
protocol://host:port/resourceName
例如下面就是一个合法的URL地址:
http://www.oneedu.cn/Index.htm
在Android系统中可以通过
URL
获取网络资源,
其中的URLConnection
和HTTPURLConnection
是最为常用的两种方式。
URL类详解
在JDK中还提供了一个
URI(Uniform Resource Identifiers)
类,
其实例代表一个统一资源标识符
,
Java的URI不能用于定位任何资源
,
它的唯一作用
就是解析
。与此对应的是,
URL
则包含
一个可打开到达该资源
的输入流
,
因此我们可以将URL
理解成URI的特例
。-
在
类URL
中,
提供了多个
可以创建URL对象的构造器
,
一旦获得了URL对象
之后,
可以调用下面的方法来访问该URL对应的资源
。String getFile()
:获取此URL的资源名
。String getHost()
:获取此URL的主机名
。String getPath()
:获取此URL的路径部分
。int getPort()
:获取此URL的端口号
。String getProtocol()
:获取此URL的协议名称
。String getQuery()
:获取此URL的查询字符串部分
。URLConnection openConnection()
:
返回一个URLConnection对象
,
它表示到URL所引用的远程对象的连接
。InputStream openStream()
:
打开与此 URL 的连接
,
并返回一个用于读取该 URL 资源
的InputStream
。
在
URL
中,
可以使用方法openConnection()
返回一个URLConnection
对象,
该对象表示应用程序
和URL
之间的通信链接
。应用程序
可以通过URLConnection实例
向此URL
发送请求
,
并读取URL引用的资源
。
创建
一个和URL连接
,
并发送请求
;-
读取此URL引用的资源的步骤:
- (1)通过调用URL对象
openConnection()
方法来创建URLConnection
对象。 - (2)设置
URLConnection
的参数
和普通请求属性
。 - (3)如果只是发送
Get 方式
请求,使用方法connect
建立和远程资源
之间的实际连接
即可;
如果需要发送Post
方式请求,
需要获取URLConnection实例
对应的输出流
来发送请求参数
。 - (4)
远程资源
变为可用
,
程序可以访问远程资源的头字段
或通过输入流
读取
远程资源的数据
。
- (1)通过调用URL对象
- 在
建立
和远程资源的实际连接之前
,
可以通过如下方法来设置请求头字段
。setAllowUserInteraction
:设置该URLConnection的allowUserInteraction请求头字段的值。setDoInput
:设置该URLConnection的doInput请求头字段的值。setDoOutput
:设置该URLConnection的doOutput请求头字段的值。setIfModifiedSince
:设置该URLConnection的ifModifiedSince请求头字段的值。setUseCaches
:设置该URLConnection的useCaches请求头字段的值。
除此之外,还可以使用如下方法来设置或增加通用头字段。setRequestProperty(String key, String value)
:设置该URLConnection的key请求头字段的值为value。addRequestProperty(String key, String value)
:为该URLConnection的key请求头字段的增加value值,该方法并不会覆盖原请求头字段的值,而是将新值追加到原请求头字段中。
- 当发现
远程资源
可以使用
后,
使用如下方法访问头字段和内容
。-
Object getContent()
:获取该URLConnection的内容。 -
String getHeaderField(String name)
:获取指定响应头字段的值。 -
getInputStream()
:返回该URLConnection对应的输入流,用于获取URLConnection响应的内容。 -
getOutputStream()
:返回该URLConnection对应的输出流,用于向URLConnection发送请求参数。 -
getHeaderField
:根据响应头字段来返回对应的值。
因为在程序中需要经常访问某些头字段,所以Java为我们提供了如下方法来访问特定响应头字段的值。 -
getContentEncoding
:获取content-encoding响应头字段的值。 -
getContentLength
:获取content-length响应头字段的值。 -
getContentType
:获取content-type响应头字段的值。 -
getDate()
:获取date响应头字段的值。 -
getExpiration()
:获取expires响应头字段的值。 -
getLastModified()
:获取last-modified响应头字段的值。
-
案例1:InetAddress的简单用法:
public class UseInetAddress {
public UseInetAddress() {
// TODO Auto-generated constructor stub
}
public static void main(String[] args)
throws Exception
{
//根据主机名来获取对应的InetAddress实例
InetAddress ip = InetAddress.getByName("www.sohu.com");
//判断是否可达
System.out.println("sohu是否可达:" + ip.isReachable(2000));
//获取该InetAddress实例的IP字符串
System.out.println(ip.getHostAddress());
//根据原始IP地址来获取对应的InetAddress实例
InetAddress local = InetAddress.getByAddress(new byte[]{127,0,0,1});
System.out.println("本机是否可达:" + local.isReachable(5000));
//获取该InetAddress实例对应的全限定域名
System.out.println(local.getCanonicalHostName());
}
}
运行效果:
sohu是否可达:true
14.18.240.22
本机是否可达:true
127.0.0.1
凌川江雪阁是否可达:true
47.100.78.251
案例2:普通字符和MIME字符的转换
注意:
encode编码;decode解码/译码;
编码和解码所用的编码标准(UTF-8/GBK)要一样!
比方说,某一个普通String,
encode用的标准是UTF-8,
那编码出来的码在decode时,
用的标准也要是UTF-8,方可译码,
否则用GBK是无法解码的!
public class URLDecodery {
public static void main(String[] args)
throws Exception
{
//将application/x-www-form-urlencoded MIME字符串
//转换成普通字符串
String keyWord = URLDecoder.decode(
"%CE%CA%CA%C0%BC%E4%C7%E9%CE%AA%BA%CE%CE%EF", "GBK");
System.out.println(keyWord);
//将普通字符串转换成
//application/x-www-form-urlencoded MIME字符串
String urlStr = URLEncoder.encode("直教人生死相许" , "GBK");
System.out.println(urlStr);
keyWord = URLDecoder.decode("%E7%8B%97%E7%8B%97%E6%90%9E%E7%AC%91", "UTF-8");
System.out.println(keyWord);
}
}
运行结果:
问世间情为何物
%D6%B1%BD%CC%C8%CB%C9%FA%CB%C0%CF%E0%D0%ED
狗狗搞笑
HttpURLConnection详解
主要分四个功能实现:
- 从Internet获取网页
需要先发送请求,
然后将网页以流的形式读回来:
(1)创建一个URL对象:
URL url = new URL("http://www.sohu.com");
(2)利用HttpURLConnection对象从网络中获取网页数据:
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
(3)设置连接超时:
conn.setConnectTimeout(6* 1000);
(4)对响应码进行判断:
if (conn.getResponseCode() != 200) throw new RuntimeException("请求url失败");
(5)得到网络返回的输入流:
InputStream is = conn.getInputStream();
接着可以用bufferReader读取数据;
- 详见网络技术基础梳理
- 从Internet获取文件
(1)~(5)同上
(6)写出得到的文件流:
outStream.write(buffer, 0, len);
- 向Internet发送请求参数
(1)将地址和参数存到byte数组中:
byte[] data = params.toString().getBytes();
(2)创建URL对象:
URL realUrl = new URL(requestUrl);
(3)用HttpURLConnection对象向网络地址发送请求:
HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection();
(4)设置容许输出:
conn.setDoOutput(true);
(5)设置不使用缓存:
conn.setUseCaches(false);
(6)设置使用Post的方式发送:
conn.setRequestMethod("POST");
(7)设置维持长连接:
conn.setRequestProperty("Connection", "Keep-Alive");
(8)设置文件字符集:
conn.setRequestProperty("Charset", "UTF-8");
(9)设置文件长度:
conn.setRequestProperty("Content-Length", String.valueOf(data.length));
(10)设置文件类型:
conn.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
(11)最后以流的方式输出。
在实现此功能时,
在发送Post请求时必须设置允许输出。
建议不要使用缓存,避免出现不应该出现的问题。
在开始就用HttpURLConnection对象的setRequestProperty()设置,
即生成HTML文件头。
当然,具体的还可以参考郭神的写法:
HttpURLConnection
:
OKHttp
:
- 可见笔记网络技术基础梳理
- 向Internet发送XML数据
可参考其他博客,这里不再赘述
注意
使用Android中的HttpUrlConnection时,有个地方需要注意一下,
就是如果程序中有跳转,并且跳转有外部域名的跳转,
那么非常容易超时并抛出域名无法解析的异常(Host Unresolved),
建议做跳转处理的时候不要使用它自带的方法设置成为自动跟随跳转,
最好自己做处理,以防出现异常。
这个问题模拟器上面看不出来,只有真机上面能看出来。
案例1:在Android手机屏幕中显示网络中的图片
- 在日常应用中,
我们经常不需
要将网络中
的图片保存到手机中
,
而只是在网络浏览
一下即可。
这里用HttpURLConnection
打开连接,
即可获取连接数据
了。
在本实例中,
使用HttpURLConnection
方法来连接
并获取网络数据
,
将获取的数据
用InputStream
的方式保存
在内存
中。
注意:
这里必须把网络请求
这个耗时操作
放在子线程
,
否则可能会阻塞主线程
,造成报错!
(各种乱起八糟的错误,
IDE待会儿什么v4和v7组件库版本
不匹配的错误都给你搬出来。。。)
主要思路是:
在子线程
中进行网络请求
,
具体的网络请求
操作如上所述
(这里用的是HttpURLConnection
去连接远程资源
,
实际开发中可以尝试集成第三方库),
请求成功
后
把得到的资源在子线程编码(decodeStream()
)成bitmap
接着把bitmap
转交到主线程
进行UI更新
即可完成!
- 方式一:直接用
runOnUiThread()
把bitmap
转交到主线程
进行UI更新
:
public class GetImageActivity extends AppCompatActivity {
private Button mButton1;
private TextView mTextView1;
private ImageView mImageView1;
String uriPic = "http://www.baidu.com/img/baidu_sylogo1.gif";
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_get_image);
mButton1 = (Button) findViewById(R.id.myButton1);
mTextView1 = (TextView) findViewById(R.id.myTextView1);
mImageView1 = (ImageView) findViewById(R.id.myImageView1);
mButton1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
/* 设置Bitmap在ImageView中 */
getURLBitmap();
}
});
}
public void getURLBitmap()
{
new Thread(new Runnable() {
@Override
public void run() {
URL imageUrl = null;
Bitmap bitmap = null;
try {
/* new URL对象将网址传入 */
imageUrl = new URL(uriPic);
} catch (MalformedURLException e)
{
e.printStackTrace();
}
try {
/* 取得连接 */
HttpURLConnection conn = (HttpURLConnection) (imageUrl != null ? imageUrl.openConnection() : null);
if (conn != null) {
conn.connect();
}
/* 取得返回的InputStream */
InputStream is = null;
if (conn != null) {
is = conn.getInputStream();
}
/* !!!!!!!!!!!
将InputStream变成Bitmap
!!!!!!!!!!!!!*/
bitmap = BitmapFactory.decodeStream(is);
showImage(bitmap);
/* 关闭InputStream */
if (is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
private void showImage(final Bitmap bitmap) {
runOnUiThread(new Runnable() {
@Override
public void run() {
mImageView1.setImageBitmap(bitmap);
mTextView1.setText("");
}
});
}
}
对应的xml布局:
运行结果:
- 方式二:使用
handle消息机制
把bitmap
转交到主线程
进行UI更新
:
public class GetImageActivityTwo extends AppCompatActivity {
ImageView iv_show;
EditText et_path;
String path;
@SuppressLint("HandlerLeak")
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
Bitmap bitmap = (Bitmap) msg.obj;
iv_show.setImageBitmap(bitmap);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_get_image_two);
//寻找相应控件
et_path = findViewById(R.id.et_path);
iv_show = findViewById(R.id.iv_show);
}
public void click(View v){
new Thread(){
Message message = Message.obtain();
@Override
public void run() {
File file = new File(getCacheDir(),"test.png");
if(file.exists() && file.length()>=0){
//如果要缓存
// System.out.print("本地缓存");
// Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
// message.obj = bitmap;
// handler.sendMessage(message);
}
else{
path = et_path.getText().toString().trim();
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");//设置请求方法
conn.setConnectTimeout(5000);//设置超时时间
InputStream in = conn.getInputStream();//拿到服务器返回的输出流
Bitmap bitmap = BitmapFactory.decodeStream(in);
message.obj = bitmap;
message.what = 2;
handler.sendMessage(message);//发送消息
} catch (Exception e) {
e.printStackTrace();
}
}
}
}.start();
}
}
xml布局:
运行结果:
- 参考《精通Android网络开发》