前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android网络 | URL和URLConnection详解及其实战案例

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

作者头像
凌川江雪
发布2019-10-18 10:48:36
1.6K0
发布2019-10-18 10:48:36
举报
文章被收录于专栏:李蔚蓬的专栏

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的简单用法:
代码语言:javascript
复制
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());
        }

}

运行效果:

代码语言:javascript
复制
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是无法解码的!
代码语言:javascript
复制
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);
        
    }

}

运行结果:

代码语言:javascript
复制
问世间情为何物
%D6%B1%BD%CC%C8%CB%C9%FA%CB%C0%CF%E0%D0%ED
狗狗搞笑

HttpURLConnection详解

主要分四个功能实现:

  1. 从Internet获取网页 需要先发送请求, 然后将网页以流的形式读回来:

(1)创建一个URL对象:

代码语言:javascript
复制
    URL url = new URL("http://www.sohu.com");

(2)利用HttpURLConnection对象从网络中获取网页数据:

代码语言:javascript
复制
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();

(3)设置连接超时:

代码语言:javascript
复制
    conn.setConnectTimeout(6* 1000);

(4)对响应码进行判断:

代码语言:javascript
复制
    if (conn.getResponseCode() != 200) throw new RuntimeException("请求url失败");

(5)得到网络返回的输入流:

代码语言:javascript
复制
    InputStream is = conn.getInputStream();

接着可以用bufferReader读取数据;

(1)~(5)同上

(6)写出得到的文件流:

代码语言:javascript
复制
    outStream.write(buffer, 0, len);
  1. 向Internet发送请求参数

(1)将地址和参数存到byte数组中:

代码语言:javascript
复制
    byte[] data = params.toString().getBytes();

(2)创建URL对象:

代码语言:javascript
复制
URL realUrl = new URL(requestUrl);

(3)用HttpURLConnection对象向网络地址发送请求:

代码语言:javascript
复制
HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection();

(4)设置容许输出:

代码语言:javascript
复制
conn.setDoOutput(true);

(5)设置不使用缓存:

代码语言:javascript
复制
conn.setUseCaches(false);

(6)设置使用Post的方式发送:

代码语言:javascript
复制
conn.setRequestMethod("POST");

(7)设置维持长连接:

代码语言:javascript
复制
conn.setRequestProperty("Connection", "Keep-Alive");

(8)设置文件字符集:

代码语言:javascript
复制
conn.setRequestProperty("Charset", "UTF-8");

(9)设置文件长度:

代码语言:javascript
复制
conn.setRequestProperty("Content-Length", String.valueOf(data.length));

(10)设置文件类型:

代码语言:javascript
复制
conn.setRequestProperty("Content-Type","application/x-www-form-urlencoded");

(11)最后以流的方式输出。

在实现此功能时, 在发送Post请求时必须设置允许输出。 建议不要使用缓存,避免出现不应该出现的问题。 在开始就用HttpURLConnection对象的setRequestProperty()设置, 即生成HTML文件头。

当然,具体的还可以参考郭神的写法:

HttpURLConnection

OKHttp

注意 使用Android中的HttpUrlConnection时,有个地方需要注意一下, 就是如果程序中有跳转,并且跳转有外部域名的跳转, 那么非常容易超时并抛出域名无法解析的异常(Host Unresolved), 建议做跳转处理的时候不要使用它自带的方法设置成为自动跟随跳转, 最好自己做处理,以防出现异常。 这个问题模拟器上面看不出来,只有真机上面能看出来。

案例1:在Android手机屏幕中显示网络中的图片

  • 在日常应用中, 我们**经常不需**要将**网络中**的图片 保存到手机中**,** 而只是**在网络浏览**一下即可。

这里用 HttpURLConnection 打开连接,

即可**获取连接数据**了。

在本实例中,

使用**HttpURLConnection**方法来**连接**并**获取网络数据**,

将**获取的数据**用**InputStream**的方式**保存**在**内存**中。

注意: 这里必须把**网络请求**这个**耗时操作**放在**子线程**, 否则可能会**阻塞主线程**,造成报错! (各种乱起八糟的错误, IDE待会儿**什么v4和v7组件库版本**不匹配的错误都给你搬出来。。。) 主要思路是: 在**子线程**中进行**网络请求**, 具体的**网络请求**操作如上所述 (这里用的是 HttpURLConnection**去连接**远程资源**,** 实际开发中可以尝试集成第三方库), 请求成功**后** 把得到的资源在子线程编码(**decodeStream()**)成**bitmap** 接着把**bitmap**转交到**主线程**进行**UI更新**即可完成!

  • 方式一:直接用**runOnUiThread()**把**bitmap**转交到**主线程**进行**UI更新**:
代码语言:javascript
复制
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布局:

代码语言:javascript
复制
<?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更新**:
代码语言:javascript
复制
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布局:

代码语言:javascript
复制
<?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网络开发》
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019.10.16 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • URL和URLConnection
    • HttpURLConnection详解
      • 案例1:在Android手机屏幕中显示网络中的图片
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档