HttpURLConnection,HttpClient,Volley


HttpURLConnection

继承关系:
java.lang.Object
-java.net.URLConnection
-java.net.HttpURLConnection
HttpURLConnection继承自URLConnection,可以使用HTTP协议发送接收网络数据。可以接收发送提前不知道长度的数据。

HttpURLConnection的使用

其使用方式参考如下模式:
1.通过调用 URL.openConnection() 方法获得一个 URLConnection,并将其转为HttpURLConnection对象。
2.准备request请求,request的首要属性是它的URI。request 头还可以包括例如整数,首选内容类型,会话cookies等这些元数据。
3.也可以选择上传request body.(必须设定setDoOutput(true)),然后通过URLConnection.getOutputStream()返回的 OutputStream 发送出去。
4.读取response。response header一般包含一些元数据,比如response body的内容类型和长度,修改时间,会话cookies。response body可以通过URLConnection.getInputStream()方法返回的InputStream中读取。如果response没有body。URLConnection.getInputStream()返回一个空的stream。
5.关闭连接,一旦response body被读取完毕,HttpURLConnection应该通过disconnect()方法来关闭,从而释放连接所持有的资源。

典型示例代码:

URL url = new URL("http://www.Android.com/");
   HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
   try {
     InputStream in = new BufferedInputStream(urlConnection.getInputStream());
     readStream(in);
   } finally {
     urlConnection.disconnect();
   }
  • 1

代码中并没有步骤3.

通过HttpURLConnection简单实现下载网络图片并展示在ImageView

通过上面的例子可以看出,我们可以通过URI来得到网络上的图片(例如https://www.bkjia.com/img/bd_logo1.png)。并通过InputStream得到得到图像数据,用BitmapFactory解析。最后在ImageView中绘制图像。

注意:由于网络操作耗时较大,不要放在主线程,这里本例将其放在AsyncTask中来做。
  • 1

示例代码:

package com.example.hurlconnectionpic;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;

public class MainActivity extends Activity {
    ImageView iv;
    Button bt;
    String bkjiaImage = "l) 
HttpClient API文档:http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/

因为Android中集成了HttpClient,所以我们可以直接使用HttpClient来进行网络编程。使用HttpClient直接加载一个图片实在是有点杀鸡用牛刀,因为HttpClient还可以实现用户登陆,维持和服务器之间的Session。不过为了简单期间,我们还是实现一个如上面的下载图片并显示在ImageView中的例子,还是以Get为例。

HttpClient使用起来同样是发送Request,接收response。

注:由于我的开发环境中Android自带的是3.X的httpClient。上面讲的例子是4.3的httpClient,API有所区别。如果需要使用最新的httpClient,可以自行下载jar包导入
下载:http://hc.apache.org/downloads.cgi 里面有个HttpClient for Android 4.3.5 (GA) 版本。不过是源码,自行编译成Jar即可。
如果使用老版本的httpClient问题也不大,只是API不太一样了,下面的示例代码使用的是HttpClient for Android 4.3.5。
示例代码:

使用HttpClients.createDefault();创建居然会报错,使用HttpClients.custom().useSystemProperties().build();正常
见http://www.tuicool.com/articles/nyyaYne
package com.example.hurlconnectionpic;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

import org.apache.http.HttpEntity;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.HttpClients;

import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;

public class MainActivity extends Activity {
    ImageView iv;
    Button bt;
    String bkjiaImage = "l

对于Volley的代码实现解析请参考:
“http://www.codekk.com/open-source-project-analysis/detail/Android/grumoon/Volley 源码解析”

Volley加载图片

虽然对于Volley来说,加载图片轻而易举,甚至它还提供了NetworkImageView这个自定义的ImageView来简化这一操作:

        //将Layout中的ImageView转换为networkImageView
        networkImageView = (NetworkImageView) findViewById(R.id.network_image_view);    
        //快速创建一个默认的RequestQueue(处理request的队列,一般一个Application只需要维护一个单例,这里简化了代码)
        mQueue = Volley.newRequestQueue(this);
        //创建一个ImageCache缓存,ImageCache是ImageLoader内的一个接口,需要自己去实现它
        MyImageCache mImageCache = MyImageCache.instance();
        //根据上面两个实例创建一个ImageLoader,ImageLoader是Volley里提供的简化图像加载的类
        ImageLoader imageLoader = new ImageLoader(mQueue,mImageCache);  
        //设定NetworkImageView默认情况下和出错情况下的图片样式
        networkImageView.setDefaultImageResId(R.drawable.ic_launcher);
        networkImageView.setErrorImageResId(R.drawable.ic_launcher);
        //设定NetworkImageView的URL地址,和辅助的ImageLoader。
        //调用完该语句后,会通过ImageLoader首先从ImageCache中找是否有缓存的图片,如果有就显示
        //没有就通过网络下载图片并加载
        networkImageView.setImageUrl(bkjiaImage, imageLoader);

上面的ImageCache可以参考官方实现,摘要如下:

new ImageLoader.ImageCache() {
            private final LruCache<String, Bitmap>
                    cache = new LruCache<String, Bitmap>(20);

            @Override
            public Bitmap getBitmap(String url) {
                return cache.get(url);
            }

            @Override
            public void putBitmap(String url, Bitmap bitmap) {
                cache.put(url, bitmap);
            }
        });
  • 1

如果有一个问题就是其是在内存中做的缓存,当请求的图片过大的时候,容易发生OOM问题,而且缓存也不可能做的过大,所以自己实现的时候可以实现一个Disk+内存双重缓存的方式来做,做一个LruMemCache和LruDiskCache,在LruMemCache满了的时候trim到LruDiskCache。get的时候先从LruMemCache找,找不到则从ruDiskCache中找,再找不到则从网络下载,并加入到LruMemCache中。
说到底Volley并不是专门设计用来下载加载网络图片的。如果有大量图片加载任务,可以考虑使用Android-Universal-Image-Loader (https://github.com/nostra13/Android-Universal-Image-Loader) 它在内存缓存和硬盘缓存的管理做的更好。

Volley更擅长的是将一系列各种请求加入到异步网络请求队列。大大的提高了网络请求的销量和使用的便捷性,比如JsonReques。StringRequest。而不是通过HttpURLConnection,然后我们自己管理多线程,内存溢出这些问题。

Volley获取天气

使用中国天气网的接口来获取Json数据,然后解析天气内容并显示
接口地址:http://www.weather.com.cn/data/cityinfo/城市代码.html
以北京为例,接口地址为:http://www.weather.com.cn/data/cityinfo/101010100.html
返回数据为:

{  
    "weatherinfo": {  
        "city": "北京", // 城市中文名  
        "cityid": "101010100", // 城市 ID  
        "temp1": "22℃", // ?   
        "temp2": "31℃", // ?   
        "weather": "阴转晴", // 天气  
        "img1": "n2.gif", // ? 天气图标编号  
        "img2": "d0.gif", // ? 天气图标编号  
        "ptime": "18:00" // 发布时间  
    }  
}  

实现思路:
1,界面显示ListView。分别显示不同城市的天气状况,每个Item只显示城市名称,高低气温,天气状况,发布时间。
2,刷新时通过Volley发送JsonRequest,并根据返回的Response更新天气信息。

Q:如果做到比如5个城市发到队列里去请求,然后返回的数据异步更新ListView?
notifyDataSetChanged? 拿到具体的Item的View去更新?
notifyDataSetChanged等于是一个个刷新所有的View。算了,为了方便起见,就等所有的城市都更新完数据后,调用一次notifyDataSetChanged来更新所有的列表。

先上效果动画:
这里写图片描述

代码:

manifest中不要忘记加网络权限

<uses-permission android:name="android.permission.INTERNET"/>
  • 1

主界面Layout

<RelativeLayout 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"
    tools:context=".MainActivity" >

    <ListView
        android:id="@+id/listView1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="50dp"
 >
    </ListView>

    <Button
        android:id="@+id/button1"
        style="?android:attr/buttonStyleSmall"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:text="Refresh" />

</RelativeLayout>

ListView Item的Layout

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <TextView
            android:id="@+id/city"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20sp"
            android:layout_weight="1"
            android:text="城市"
            android:textAppearance="?android:attr/textAppearanceLarge" />

        <TextView
            android:id="@+id/Weather"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="right"
            android:layout_marginRight="20sp"
            android:text="Weather"
            android:textAppearance="?android:attr/textAppearanceMedium" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <TextView
            android:id="@+id/Low_Temp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20sp"
            android:text="Low_Temp" />

        <TextView
            android:id="@+id/to"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="--" />

        <TextView
            android:id="@+id/Top_Temp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Low_Temp" />

        <TextView
            android:id="@+id/Time"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="right"
            android:layout_weight="1"
            android:gravity="right"
            android:paddingRight="20sp"
            android:text="Time" />
    </LinearLayout>

</LinearLayout>

自定义的BaseAdapter

package com.example.volleyweather;

import java.util.List;
import java.util.Map;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
//
public class weatherAdapter extends BaseAdapter {
    private List<Map<String, String>> weatherInfo;
    private LayoutInflater mInflater;

    public weatherAdapter(Context context,List<Map<String, String>> weatherInfo) {
        super();
        this.mInflater = LayoutInflater.from(context);
        this.weatherInfo = weatherInfo;
    }

    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return weatherInfo.size();
    }

    @Override
    public Object getItem(int position) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public long getItemId(int position) {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder = null;
        if (convertView == null) {

            holder=new ViewHolder();  

            convertView = mInflater.inflate(R.layout.weather_info, null);
            holder.city = (TextView)convertView.findViewById(R.id.city);
            holder.weather= (TextView)convertView.findViewById(R.id.Weather);
            holder.lowTemp= (TextView)convertView.findViewById(R.id.Low_Temp);
            holder.topTemp= (TextView)convertView.findViewById(R.id.Top_Temp);
            holder.time= (TextView)convertView.findViewById(R.id.Time);
            convertView.setTag(holder);

        }else {

            holder = (ViewHolder)convertView.getTag();
        }



        holder.city.setText(weatherInfo.get(position).get("city"));
        holder.weather.setText(weatherInfo.get(position).get("weather"));
        holder.lowTemp.setText(weatherInfo.get(position).get("temp1"));
        holder.topTemp.setText(weatherInfo.get(position).get("temp2"));
        holder.time.setText(weatherInfo.get(position).get("ptime"));


        return convertView;
    }

    final class ViewHolder{

        public TextView city;
        public TextView weather;
        public TextView lowTemp;
        public TextView topTemp;
        public TextView time;
    }
}

MainActivity


package com.example.volleyweather;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import org.json.JSONException;
import org.json.JSONObject;

import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonObjectRequest;
import com.android.volley.toolbox.Volley;

import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;

public class MainActivity extends Activity {
    String Tag="Weather";
    ListView weatherList;
    Button refreshButton;
    List<Map<String, String>> weatherInfo=new ArrayList<Map<String, String>>();
    int[] cityId = { 101010100, 101030100, 101040100, 101050101, 101060801 };
    private RequestQueue mQueue;  
    private weatherAdapter myAdapter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        weatherList = (ListView) findViewById(R.id.listView1);
        refreshButton = (Button) findViewById(R.id.button1);
        mQueue=Volley.newRequestQueue(this);
        myAdapter=new weatherAdapter(this,weatherInfo);
        weatherList.setAdapter(myAdapter);


        refreshButton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                fetchWeather(weatherInfo);
            }
        });
    }

    void fetchWeather(final List weatherInfo){
        final AtomicInteger count=new AtomicInteger(0);
        //Request weather with Volly,save the result into weatherInfo list
        weatherInfo.clear();
        for(int i=0;i<cityId.length;i++){

            //make a jsonObjectRequest
            JsonObjectRequest jsonObjectRequest = new JsonObjectRequest("http://www.weather.com.cn/data/cityinfo/"+cityId[i]+".html",
                    null, 
                    new Response.Listener<JSONObject>() {
                @Override
                public void onResponse(JSONObject arg0) {
                    Log.e(Tag,"weather got!!");
                    //iterator of the String names in this object.
                    Iterator<String> it=arg0.keys();
                    while(it.hasNext()){
                        String key=it.next();//这个key对应weatherinfo
                        JSONObject weatherinfoObj=null;
                        try {
                            weatherinfoObj=arg0.getJSONObject(key);//对应weatherinfo的整个结构
                        } catch (JSONException e) {
                            e.printStackTrace();
                        }

                        if(weatherinfoObj!=null){
                            Iterator<String> weatherItems = weatherinfoObj.keys();//分别是city,cityid等。
                            Map<String, String> map = new HashMap<String, String>();
                            while(weatherItems.hasNext()){
                                String weatherItem=weatherItems.next();
                                String weatherContent;
                                try {
                                    weatherContent=weatherinfoObj.getString(weatherItem);
                                    map.put(weatherItem, weatherContent);


                                } catch (JSONException e) {
                                    // TODO Auto-generated catch block
                                    e.printStackTrace();
                                }
                            }
                            weatherInfo.add(map);

                        }
                    }
                    count.addAndGet(1);
                    if(count.get()==cityId.length) {
                        //如果返回的response个数达到了请求的个数,更新listView
                        //Refresh the ListView
                        myAdapter.notifyDataSetChanged();
                    }
                }
            }, new Response.ErrorListener() {

                @Override
                public void onErrorResponse(VolleyError arg0) {
                }
            });

            mQueue.add(jsonObjectRequest);
        }

    }

不过这个网站有的时候刷新次数多了就不返回响应了,可能是网站加入了保护机制。

HttpComponents 的详细介绍:请点这里
HttpComponents 的下载地址:请点这里

HttpClient 4.0的使用详解

Android 如何用HttpClient 以Post方式提交数据并添加http头信息

Android 如何用HttpClient 以Get方式获取数据并添加http头信息

本文永久更新链接地址

相关内容