Welcome 微信登录
编程资源 图片资源库 蚂蚁家优选 PDF转换器

首页 / 操作系统 / Linux / 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.linuxidc.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 linuxidcImage = "http://www.linuxidc.com/img/bd_logo1.png";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);iv = (ImageView) findViewById(R.id.imageView1);bt = (Button) findViewById(R.id.button1);bt.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {new BackGroundTast().execute(linuxidcImage);}});}class BackGroundTast extends AsyncTask<String, Void, Bitmap> {Bitmap bitmap;HttpURLConnection urlConnection;InputStream in;@Overrideprotected Bitmap doInBackground(String... params) {try {URL url = new URL(params[0]);urlConnection = (HttpURLConnection) url.openConnection();urlConnection.setDoInput(true);urlConnection.connect();in = urlConnection.getInputStream();bitmap = BitmapFactory.decodeStream(in);in.close();} catch (IOException e) {e.printStackTrace();} finally {urlConnection.disconnect();}return bitmap;}@Overrideprotected void onPostExecute(Bitmap result) {super.onPostExecute(result);if (result != null) {iv.setImageBitmap(result);}}@Overrideprotected void onPreExecute() {// TODO Auto-generated method stubsuper.onPreExecute();}}}
  • 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"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"tools:context=".MainActivity" ><ImageViewandroid:id="@+id/imageView1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:contentDescription="@string/hello_world" /><Buttonandroid: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:layout_marginBottom="20dp"android:text="GetImage" /></RelativeLayout>AndroidManifest.xml 别忘了添加权限<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.example.hurlconnectionpic"android:versionCode="1"android:versionName="1.0" ><uses-permission android:name="android.permission.INTERNET"/><uses-sdkandroid:minSdkVersion="8"android:targetSdkVersion="17" /><applicationandroid:allowBackup="true"android:icon="@drawable/ic_launcher"android:label="@string/app_name"android:theme="@style/AppTheme" ><activityandroid:name="com.example.hurlconnectionpic.MainActivity"android:label="@string/app_name" ><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application></manifest>效果图:

HttpClient

HttpURLConnection是Java的网络库的类,而HttpClient是 Apache Jakarta Common 下的子项目,可以用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。HttpClient相对HttpURLConnection功能更加完善易用,主要特点如下:
(1)实现了所有 HTTP 的方法(GET,POST,PUT,HEAD 等)
(2)支持自动转向
(3)支持 HTTPS 协议
(4)支持代理服务器等

使用步骤:

GET:
1. 创建 HttpClient 的实例
2. 创建某HttpGet。在创建时传入待连接的地址
3. 调用HttpClient实例的 execute 方法来执行第二步中创建好的实例
4. 读 response
5. 释放连接。无论执行方法是否成功,都必须释放连接
6. 对得到后的内容进行处理
POST:
1. 创建 HttpClient 的实例
2. 创建HttpPost对象。调用setParams(HetpParams params),setEntity(HttpEntity entity)方法来设置请求参数
3. 调用第一步中创建好的实例的 execute 方法来执行第二步中创建好的 method 实例
4. 读 response,getAllHeaders(),getEntity()等
5. 释放连接。无论执行方法是否成功,都必须释放连接
6. 对得到后的内容进行处理

示例代码:

//Get 方式CloseableHttpClient httpclient = HttpClients.createDefault();HttpGet httpGet = new HttpGet("http://targethost/homepage");CloseableHttpResponse response1 = httpclient.execute(httpGet);// 下面的HTTP连接一直被response持有,从而使response内容可以直接从网络socket中传输。//为了保证系统资源能够正确释放,用户必须在finally区块中调用CloseableHttpResponse.close()方法//需要注意:如果response内容没有完全接受,下面的连接是不能安全的复用的,连接将会关闭并被connection manager丢弃。try {System.out.println(response1.getStatusLine());HttpEntity entity1 = response1.getEntity();//确保response内容全被接受,使用EntityUtils.consume(entity1);} finally {response1.close();}//Post 方式HttpPost httpPost = new HttpPost("http://targethost/login");List <NameValuePair> nvps = new ArrayList <NameValuePair>();nvps.add(new BasicNameValuePair("username", "vip"));nvps.add(new BasicNameValuePair("password", "secret"));httpPost.setEntity(new UrlEncodedFormEntity(nvps));CloseableHttpResponse response2 = httpclient.execute(httpPost);try {System.out.println(response2.getStatusLine());HttpEntity entity2 = response2.getEntity();// do something useful with the response body// and ensure it is fully consumedEntityUtils.consume(entity2);} finally {response2.close();}
  • 1
上面代码展示了使用GET和POST两种方式来完成HTTP会话过程。更多信息可以参考apache的网站上的资源:
HttpClient Tutorial (http://hc.apache.org/httpcomponents-client-4.4.x/tutorial/html/index.html)
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/nyyaYnepackage 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 linuxidcImage = "http://www.linuxidc.com/img/bd_logo1.png";// HttpClient client=new DefaultHttpClient();CloseableHttpClient httpclient = HttpClients.custom().useSystemProperties().build();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);iv = (ImageView) findViewById(R.id.imageView1);bt = (Button) findViewById(R.id.button1);bt.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {new BackGroundTast().execute(linuxidcImage);}});}class BackGroundTast extends AsyncTask<String, Void, Bitmap> {Bitmap bitmap;HttpURLConnection urlConnection;InputStream in;CloseableHttpResponse response1;@Overrideprotected Bitmap doInBackground(String... params) {HttpGet get = new HttpGet(params[0]);try {response1 = httpclient.execute(get);HttpEntity entity1 = response1.getEntity();if (entity1 != null) {bitmap = BitmapFactory.decodeStream(entity1.getContent());}} catch (IOException e) {e.printStackTrace();} finally {try {response1.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}return bitmap;}@Overrideprotected void onPostExecute(Bitmap result) {super.onPostExecute(result);if (result != null) {iv.setImageBitmap(result);}}@Overrideprotected void onPreExecute() {// TODO Auto-generated method stubsuper.onPreExecute();}}}
  • 1
代码相对HttpURLConnection变化很小,效果图和HttpURLConnection一致。

Volley使用

Volley 是 Google 推出的 Android 异步网络请求框架和图片加载框架。在 Google I/O 2013 大会上发布。它简化了http操作,并且加入了对异步加载图片的使用。Volley的实现中同样提供了上面提到的HttpURLConnection和HttpClient可供选择定制。官方推荐在API level = 9之前使用HttpClient,而之后使用HttpURLConnection。
具体使用的官方教程请参考:http://developer.android.com/training/volley/index.html对于Volley的代码实现解析请参考:
“http://www.codekk.com/open-source-project-analysis/detail/Android/grumoon/Volley 源码解析”

Volley加载图片

虽然对于Volley来说,加载图片轻而易举,甚至它还提供了NetworkImageView这个自定义的ImageView来简化这一操作://将Layout中的ImageView转换为networkImageViewnetworkImageView = (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(linuxidcImage, imageLoader);上面的ImageCache可以参考官方实现,摘要如下:new ImageLoader.ImageCache() {private final LruCache<String, Bitmap>cache = new LruCache<String, Bitmap>(20);@Overridepublic Bitmap getBitmap(String url) {return cache.get(url);}@Overridepublic 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" ><ListViewandroid:id="@+id/listView1"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginBottom="50dp" ></ListView><Buttonandroid: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" ><LinearLayoutandroid:layout_width="fill_parent"android:layout_height="wrap_content"android:orientation="horizontal" ><TextViewandroid: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" /><TextViewandroid: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><LinearLayoutandroid:layout_width="fill_parent"android:layout_height="wrap_content"android:orientation="horizontal" ><TextViewandroid:id="@+id/Low_Temp"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="20sp"android:text="Low_Temp" /><TextViewandroid:id="@+id/to"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="--" /><TextViewandroid:id="@+id/Top_Temp"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Low_Temp" /><TextViewandroid: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;}@Overridepublic int getCount() {// TODO Auto-generated method stubreturn weatherInfo.size();}@Overridepublic Object getItem(int position) {// TODO Auto-generated method stubreturn null;}@Overridepublic long getItemId(int position) {// TODO Auto-generated method stubreturn 0;}@Overridepublic 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;@Overrideprotected 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() {@Overridepublic 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 listweatherInfo.clear();for(int i=0;i<cityId.length;i++){//make a jsonObjectRequestJsonObjectRequest jsonObjectRequest = new JsonObjectRequest("http://www.weather.com.cn/data/cityinfo/"+cityId[i]+".html",null, new Response.Listener<JSONObject>() {@Overridepublic 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对应weatherinfoJSONObject 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 blocke.printStackTrace();}}weatherInfo.add(map);}}count.addAndGet(1);if(count.get()==cityId.length) {//如果返回的response个数达到了请求的个数,更新listView//Refresh the ListViewmyAdapter.notifyDataSetChanged();}}}, new Response.ErrorListener() {@Overridepublic void onErrorResponse(VolleyError arg0) {}});mQueue.add(jsonObjectRequest);}}不过这个网站有的时候刷新次数多了就不返回响应了,可能是网站加入了保护机制。HttpComponents 的详细介绍:请点这里
HttpComponents 的下载地址:请点这里相关阅读:HttpClient 4.0的使用详解 http://www.linuxidc.com/Linux/2012-02/55502.htmAndroid 如何用HttpClient 以Post方式提交数据并添加http头信息 http://www.linuxidc.com/Linux/2011-09/42772.htmAndroid 如何用HttpClient 以Get方式获取数据并添加http头信息 http://www.linuxidc.com/Linux/2011-09/42771.htm本文永久更新链接地址:http://www.linuxidc.com/Linux/2015-04/116020.htm