Android网络 | URL和URLConnection详解及其实战案例
简介: Android网络 | URL和URLConnection详解及其实战案例
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
-
(1)通过调用URL对象
建立和
远程资源
之间的
实际连接
即可;
如果需要发送
Post
方式请求,
需要
获取URLConnection实例
对应的
输出流
来发送
请求参数
。
- (4)`远程资源`变为`可用`,
程序可以访问
远程资源的头字段
或通过
输入流
读取
远程资源的
数据
。
-
在
建立
和远程资源的实际连接之前
,
可以通过如下方法来
设置请求头字段
。
- `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响应头字段的值。
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
注意:
- 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布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:background="#FFFFFF"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/myTextView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/app_name"/>
<Button
android:id="@+id/myButton1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="获取网络上的图片" />
<ImageView
android:id="@+id/myImageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
tools:ignore="ContentDescription" />
</LinearLayout>
运行结果:
-
方式二:使用
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布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".UI.GetImageActivityTwo">
<EditText
android:id="@+id/et_path"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="http://www.baidu.com/img/baidu_sylogo1.gif"
android:hint="请输入图片地址" />
<Button
android:onClick="click"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="查看" />