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

URL和URLConnection

  • URL(Uniform Resource Locator)对象代表统一资源定位器
    指向互联网“资源”指针
    这里的资源可以是简单的文件或目录
    也可以是对更为复杂的对象引用
    例如对数据库搜索引擎查询

  • 通常情况而言,
    URL可以由协议名、主机、端口和资源组成,
    满足如下的格式
    protocol://host:port/resourceName

  • 例如下面就是一个合法的URL地址:
    http://www.oneedu.cn/Index.htm

  • 在Android系统中可以通过URL获取网络资源,
    其中的URLConnectionHTTPURLConnection
    是最为常用的两种方式。

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());
        }

}

运行效果:
9125154-fc7f13d3dbbb3267.png
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

9125154-99e1093d9f1b9f02.png

OKHttp

9125154-832d20d8b5014352.png

  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>

运行结果:
9125154-74155bbd01e4d36a.png
  • 方式二:使用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="查看" />

    <ImageView
        android:id="@+id/iv_show"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</LinearLayout>

运行结果:
9125154-c1ae0f0aa22ada2b.png





  • 参考《精通Android网络开发》
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

凌川江雪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值