Android网络 | URL和URLConnection详解及其实战案例

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

建立和 远程资源 之间的 实际连接 即可;
如果需要发送 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响应头字段的值。
案例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详解

主要分四个功能实现:

  1. 从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读取数据;

  1. 从Internet获取文件

(1)~(5)同上
(6)写出得到的文件流:

    outStream.write(buffer, 0, len);
  1. 向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

  1. 向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="查看" />