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获取网络资源,

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

}

运行效果:

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

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

</LinearLayout>

运行结果:





  • 参考《精通Android网络开发》
相关文章
|
1月前
|
数据采集 存储 JSON
Python网络爬虫:Scrapy框架的实战应用与技巧分享
【10月更文挑战第27天】本文介绍了Python网络爬虫Scrapy框架的实战应用与技巧。首先讲解了如何创建Scrapy项目、定义爬虫、处理JSON响应、设置User-Agent和代理,以及存储爬取的数据。通过具体示例,帮助读者掌握Scrapy的核心功能和使用方法,提升数据采集效率。
114 6
|
23天前
|
存储 安全 网络安全
网络安全的盾与剑:漏洞防御与加密技术的实战应用
在数字化浪潮中,网络安全成为保护信息资产的重中之重。本文将深入探讨网络安全的两个关键领域——安全漏洞的防御策略和加密技术的应用,通过具体案例分析常见的安全威胁,并提供实用的防护措施。同时,我们将展示如何利用Python编程语言实现简单的加密算法,增强读者的安全意识和技术能力。文章旨在为非专业读者提供一扇了解网络安全复杂世界的窗口,以及为专业人士提供可立即投入使用的技术参考。
|
26天前
|
存储 缓存 监控
Docker容器性能调优的关键技巧,涵盖CPU、内存、网络及磁盘I/O的优化策略,结合实战案例,旨在帮助读者有效提升Docker容器的性能与稳定性。
本文介绍了Docker容器性能调优的关键技巧,涵盖CPU、内存、网络及磁盘I/O的优化策略,结合实战案例,旨在帮助读者有效提升Docker容器的性能与稳定性。
67 7
|
1月前
|
数据采集 前端开发 中间件
Python网络爬虫:Scrapy框架的实战应用与技巧分享
【10月更文挑战第26天】Python是一种强大的编程语言,在数据抓取和网络爬虫领域应用广泛。Scrapy作为高效灵活的爬虫框架,为开发者提供了强大的工具集。本文通过实战案例,详细解析Scrapy框架的应用与技巧,并附上示例代码。文章介绍了Scrapy的基本概念、创建项目、编写简单爬虫、高级特性和技巧等内容。
90 4
|
1月前
|
网络协议 物联网 API
Python网络编程:Twisted框架的异步IO处理与实战
【10月更文挑战第26天】Python 是一门功能强大且易于学习的编程语言,Twisted 框架以其事件驱动和异步IO处理能力,在网络编程领域独树一帜。本文深入探讨 Twisted 的异步IO机制,并通过实战示例展示其强大功能。示例包括创建简单HTTP服务器,展示如何高效处理大量并发连接。
54 1
|
1月前
|
网络协议 安全 NoSQL
网络空间安全之一个WH的超前沿全栈技术深入学习之路(8-2):scapy 定制 ARP 协议 、使用 nmap 进行僵尸扫描-实战演练、就怕你学成黑客啦!
scapy 定制 ARP 协议 、使用 nmap 进行僵尸扫描-实战演练等具体操作详解步骤;精典图示举例说明、注意点及常见报错问题所对应的解决方法IKUN和I原们你这要是学不会我直接退出江湖;好吧!!!
网络空间安全之一个WH的超前沿全栈技术深入学习之路(8-2):scapy 定制 ARP 协议 、使用 nmap 进行僵尸扫描-实战演练、就怕你学成黑客啦!
|
1月前
|
网络协议 安全 算法
网络空间安全之一个WH的超前沿全栈技术深入学习之路(9):WireShark 简介和抓包原理及实战过程一条龙全线分析——就怕你学成黑客啦!
实战:WireShark 抓包及快速定位数据包技巧、使用 WireShark 对常用协议抓包并分析原理 、WireShark 抓包解决服务器被黑上不了网等具体操作详解步骤;精典图示举例说明、注意点及常见报错问题所对应的解决方法IKUN和I原们你这要是学不会我直接退出江湖;好吧!!!
网络空间安全之一个WH的超前沿全栈技术深入学习之路(9):WireShark 简介和抓包原理及实战过程一条龙全线分析——就怕你学成黑客啦!
|
2月前
|
网络协议 Shell 网络安全
解决两个 Android 模拟器之间无法网络通信的问题
让同一个 PC 上运行的两个 Android 模拟器之间能相互通信,出(qiong)差(ren)的智慧。
34 3
|
1月前
|
网络协议 调度 开发者
Python网络编程:Twisted框架的异步IO处理与实战
【10月更文挑战第27天】本文介绍了Python网络编程中的Twisted框架,重点讲解了其异步IO处理机制。通过反应器模式,Twisted能够在单线程中高效处理多个网络连接。文章提供了两个实战示例:一个简单的Echo服务器和一个HTTP服务器,展示了Twisted的强大功能和灵活性。
49 0
|
1月前
|
网络协议 安全 算法
网络空间安全之一个WH的超前沿全栈技术深入学习之路(9-2):WireShark 简介和抓包原理及实战过程一条龙全线分析——就怕你学成黑客啦!
实战:WireShark 抓包及快速定位数据包技巧、使用 WireShark 对常用协议抓包并分析原理 、WireShark 抓包解决服务器被黑上不了网等具体操作详解步骤;精典图示举例说明、注意点及常见报错问题所对应的解决方法IKUN和I原们你这要是学不会我直接退出江湖;好吧!!!