据Android Dalvik团队的Jesse Wilson 同学讲, 在Android系统中可以使用两种HTTP客户端来收发HTTP数据.
一个就是大名鼎鼎的Apache HTTP Client, 而另外一个就是 HttpURLConnection.
Apache HTTP Client
DefaultHttpClient 和她的兄弟 AndroidHttpClient 是用于浏览器的及具扩展性的HTTP客户端. 他们都有很多APIs. 他们的实现都很可靠并且只有很少的BUGs.
因为已经有了一票APIs的存在, 所以Dalvik团队的同学们想要改进这个客户端并且不破坏其兼容性的情况下是非常非常困难滴! 并且Android团队的同学们也并没有负责Apache HTTP Client 的开发和维护!
HTTP URL Connection
而 HttpURLConnection 就不一样了, 这家伙是通用的 轻量级的一个HTTP客户端实现,对于大多数App来说都是够用的. 这家伙功能简洁并且很容易的增强其功能.
在 Froyo 发布以前, HttpURLConnection 有一些非人让人郁闷的BUGs . 最郁闷的是, 当你在可读取的InputStream上调用 close()函数的时候会
污染连接池 (connection pool). 可以通过禁用连接池的方法还解决这个问题:
private void disableConnectionReuseIfNecessary() { // HTTP connection reuse which was buggy pre-froyo if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.FROYO) { System.setProperty("http.keepAlive", "false"); } }
在Gingerbread这个版本中, Dalvik团队的同学又添加了自动压缩数据的功能. 当你调用HttpURLConnection的时候,她回自动的偷偷的添加gzip属性到请求头中,并且会自己解压返回的数据, 开发者完全不用为了处理压缩数据而增加工作量, 只要服务器支持gzip就ok啦:
Accept-Encoding: gzip
如果你发现服务器返回的压缩数据有问题,可以参考该类的doc文档来看看如何禁用该功能!
由于Content-Length返回的是压缩后的数据长度,所以使用 getContentLength() 函数得到的Buffer数据大小是不正确的哦! 你要使用从响应中一直读取字节流直到InputStream.read()函数返回-1为止.
在Gingerbread版本中,同样也增强了HTTPs的功能. HttpsURLConnection尝试和Server Name Indication (SNI)连接,这样多个HTTPs主机可以共享同一个IP地址. 同样支持压缩和session tickets. 如果连接失败,她会自动禁用这些功能去重新连接.
而在 Ice Cream Sandwich版本中,Dalvik团队的同学又不安分了, 继续添加了一些新的特性: 响应缓存(response cache) . 如果使用了缓存,那么HTTP请求会有3种情况:
- 完全缓存的结果将直接从本地缓存中返回,省去了联网访问服务器的过程, 在中国的龟速移动网络环境中很有用哦
- 有条件(期限)的缓存将通过服务器来判断,客户端将发送这样一个请求”如果昨天得到的/foo.png这个图片已经更新了的话,就返回给我一个新的图片”,如果服务器更新了图片就返回新的数据 如果没有更新则返回”
304 Not Modified
”.对于没有更新的内容就节约了流量. - 对于没有缓存过的内容就直接请求服务器的数据,然后把这个结果再放到缓存中去.
private void enableHttpResponseCache() { try { long httpCacheSize = 10 * 1024 * 1024; // 10 MiB File httpCacheDir = new File(getCacheDir(), "http"); Class.forName("android.net.http.HttpResponseCache") .getMethod("install", File.class, long.class) .invoke(null, httpCacheDir, httpCacheSize); } catch (Exception httpResponseCacheNotAvailable) { } }
开发者该选择哪个客户端使用呢?
之前我们在软件开发中,cache都是自己来写,不管是图片缓存还是其他从网络获取的数据,有了HttpResponseCache,它帮助我们可以很好的解决cache这个问题(我现在感觉他只适合cache一些小的数据,如果大量的图片cache还是自己缓存到SD卡上面去比较好)。
HttpResponseCache的好处:
1.明显一点节约电,减少了网络请求。
2.开发者不用自己在去写cache机制了。
3.最根本的一点就是,如果开发者在开发中不是使用的HttpClient, HttpDefaultClient..., 而是用 HttpURLConnection的话, 你根本不用改本來的 Code。
这个我们就不多说了,直接看示例:
在开发中你不用写其他任何东西,只要在Application层将其启动就好了 其他的全部交给HttpURLConnection处理就行。
- public class HttpCacheApplication extends Application {
- @Override
- public void onCreate() {
- super.onCreate();
- new Thread() {
- @Override
- public void run() {
- enableHttpResponseCache();
- }
- }.start();
- }
- private void enableHttpResponseCache() {
- try {
- long httpCacheSize = 10 * 1024 * 1024;// 10M
- File httpCacheDir = new File(getCacheDir(), "http");
- Class.forName("android.net.http.HttpResponseCache")
- .getMethod("install", File.class, long.class)
- .invoke(null, httpCacheDir, httpCacheSize);
- } catch (Exception e) {
- Log.e("===>", e.getMessage(), e);
- }
- }
- }
接下来我们来看看HttpUrlConnection是怎么处理的,怎么缓存的。
- public class MainActivity extends Activity {
- private final String TAG = getClass().getSimpleName();
- ImageView img;
- Button msg;
- TextView tv;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- img = (ImageView) findViewById(R.id.imageView1);
- tv = (TextView)findViewById(R.id.textView1);
- msg = (Button) findViewById(R.id.button1);
- msg.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- new InternetTask().execute();
- }
- });
- findViewById(R.id.button2).setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- MainActivity.this.finish();
- }
- });
- }
- class InternetTask extends AsyncTask<String, String, Boolean> {
- Bitmap bitmap;
- String jsonStr;
- @Override
- protected void onPostExecute(Boolean result) {
- super.onPostExecute(result);
- img.setImageBitmap(bitmap);
- tv.setText(jsonStr);
- }
- @Override
- protected Boolean doInBackground(String... params) {
- // Test download image
- try {
- URL url = new URL("http://news.baidu.com/resource/img/logo_news_137_46.png");
- HttpURLConnection conn = (HttpURLConnection) (url
- .openConnection());
- conn.connect();
- InputStream is = conn.getInputStream();
- BitmapFactory.Options ops = new BitmapFactory.Options();
- bitmap = BitmapFactory.decodeStream(is, null, ops);
- is.close();
- conn.disconnect();
- } catch (Exception e) {
- Log.e(TAG, e.getMessage(), e);
- }
- // Test download JSON data
- try {
- URL url = new URL("http://www.baidu.com/");
- HttpURLConnection conn = (HttpURLConnection) (url
- .openConnection());
- conn.connect();
- BufferedReader reader = new BufferedReader(
- new InputStreamReader(conn.getInputStream(), "UTF-8"));
- jsonStr = reader.readLine();
- InputStream is = conn.getInputStream();
- is.close();
- conn.disconnect();
- } catch (Exception e) {
- Log.e(TAG, e.getMessage(), e);
- }
- return true;
- }
- }
- }
看下缓存文件,每个文件会产生两个文件,一个是数据文件,一个是http header 信息