码迷,mamicode.com
首页 > 移动开发 > 详细

android的HTTP框架之Volley

时间:2016-05-13 20:33:20      阅读:337      评论:0      收藏:0      [点我收藏+]

标签:

  Volley是android官方开发的一个HTTP框架,简化了利用java中原生的HTTP操作API-HttpURLConnection和HttpClient的操作。

一、首先是Volley的简单使用示例:

技术分享
  1 package com.dqxst.first;
  2 
  3 import android.graphics.Bitmap;
  4 import android.graphics.BitmapFactory;
  5 import android.os.Bundle;
  6 import android.os.Handler;
  7 import android.os.Message;
  8 import android.support.v7.app.AppCompatActivity;
  9 import android.util.LruCache;
 10 import android.widget.ImageView;
 11 import android.widget.Toast;
 12 
 13 import com.android.volley.RequestQueue;
 14 import com.android.volley.Response;
 15 import com.android.volley.VolleyError;
 16 import com.android.volley.toolbox.ImageLoader;
 17 import com.android.volley.toolbox.ImageRequest;
 18 import com.android.volley.toolbox.NetworkImageView;
 19 import com.android.volley.toolbox.Volley;
 20 import com.dqxst.first.adapter.Images;
 21 
 22 import java.io.IOException;
 23 import java.net.HttpURLConnection;
 24 import java.net.MalformedURLException;
 25 import java.net.URL;
 26 
 27 public class HttpActivity extends AppCompatActivity {
 28     private final static int LOAD_FAIL = 0;
 29     private final static int LOAD_SUCCESS = 1;
 30 
 31     private final Handler handler = new Handler() {
 32         //        private Context that=context;
 33         @Override
 34         public void handleMessage(Message msg) {
 35             Bitmap photo = (Bitmap) msg.obj;
 36             switch (msg.what) {
 37                 case LOAD_FAIL:
 38 //                    Toast.makeText(that,"加载失败",Toast.LENGTH_SHORT).show();
 39                     break;
 40                 case LOAD_SUCCESS:
 41                     iv.setImageBitmap(photo);
 42             }
 43         }
 44     };
 45 
 46     private static ImageView iv;
 47     private NetworkImageView niv;
 48 
 49     @Override
 50     protected void onCreate(Bundle savedInstanceState) {
 51         super.onCreate(savedInstanceState);
 52         setContentView(R.layout.activity_http);
 53         init();
 54 //        httpURLConnection();
 55 //        volley();
 56         volley2();
 57         volley3();
 58     }
 59 
 60     private void volley3() {
 61         //这是第三种加载图片的方式,其实内部还是第二种凡是进行实现,但是通过一种自定义控件的形式表现
 62         RequestQueue queue=Volley.newRequestQueue(this);
 63         ImageLoader loader=new ImageLoader(queue,new BitmapCache());
 64 
 65         niv.setDefaultImageResId(R.drawable.loading);
 66         niv.setErrorImageResId(R.drawable.load_error);
 67         niv.setImageUrl(Images.imageThumbUrls[3],loader);
 68     }
 69 
 70     private void volley2() {
 71         //这是Volley中一种加载图片的方式,和最基本的Request方式不同,
 72         //优点是在loader中可以传入一个用于缓存的参数,可以利用LruCache来进行缓存管理
 73         RequestQueue queue = Volley.newRequestQueue(HttpActivity.this);
 74         ImageLoader loader = new ImageLoader(queue, new BitmapCache());
 75         ImageLoader.ImageListener listener = ImageLoader.getImageListener(iv,R.drawable.loading,R.drawable.load_error);
 76         loader.get(Images.imageThumbUrls[2],listener);
 77     }
 78 
 79     public class BitmapCache implements ImageLoader.ImageCache{
 80         private int maxMemory=10*1024*1024;
 81         private LruCache<String,Bitmap> cache=new LruCache<String,Bitmap>(maxMemory){
 82             @Override
 83             protected int sizeOf(String key, Bitmap value) {
 84                 return value.getByteCount();
 85             }
 86         };
 87         @Override
 88         public Bitmap getBitmap(String s) {
 89             return cache.get(s);
 90         }
 91 
 92         @Override
 93         public void putBitmap(String s, Bitmap bitmap) {
 94             cache.put(s,bitmap);
 95         }
 96     }
 97 
 98     private void volley() {
 99         //Volley基本使用分3步
100         //1、创建RequestQueue请求队列,
101         RequestQueue queue = Volley.newRequestQueue(HttpActivity.this);
102         //2、创建一个请求对象,这里是加载图片的请求对象
103         ImageRequest request = new ImageRequest(Images.imageThumbUrls[1], new Response.Listener<Bitmap>() {
104             @Override
105             public void onResponse(Bitmap bitmap) {
106                 iv.setImageBitmap(bitmap);
107             }
108         }, 500, 500, null, new Response.ErrorListener() {
109             @Override
110             public void onErrorResponse(VolleyError volleyError) {
111                 Toast.makeText(getApplicationContext(), "加载出错", Toast.LENGTH_SHORT).show();
112             }
113         });
114         //3、最后,需要将请求对象添加到请求队列中进行工作。
115         queue.add(request);
116     }
117 
118     private void httpURLConnection() {
119         new Thread(new Runnable() {
120             @Override
121             public void run() {
122                 try {
123                     Bitmap bitmap;
124                     URL url = new URL(Images.imageThumbUrls[0]);
125                     HttpURLConnection con = (HttpURLConnection) url.openConnection();
126                     con.setConnectTimeout(5 * 1000);
127                     con.setReadTimeout(10 * 1000);
128                     bitmap = BitmapFactory.decodeStream(con.getInputStream());
129                     Message msg = new Message();
130                     if (bitmap != null) {
131                         msg.what = LOAD_SUCCESS;
132                         msg.obj = bitmap;
133                     } else {
134                         msg.what = LOAD_FAIL;
135                     }
136                     handler.sendMessage(msg);
137                 } catch (MalformedURLException e) {
138                     e.printStackTrace();
139                 } catch (IOException e) {
140                     e.printStackTrace();
141                 }
142             }
143         }).start();
144     }
145 
146     private void init() {
147         iv = (ImageView) findViewById(R.id.http_imageView);
148         niv= (NetworkImageView) findViewById(R.id.volley_NetworkImageView);
149     }
150 }
HttpActivity
技术分享
 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     xmlns:tools="http://schemas.android.com/tools"
 4     android:layout_width="match_parent"
 5     android:layout_height="match_parent"
 6     android:orientation="vertical"
 7     android:paddingBottom="@dimen/activity_vertical_margin"
 8     android:paddingLeft="@dimen/activity_horizontal_margin"
 9     android:paddingRight="@dimen/activity_horizontal_margin"
10     android:paddingTop="@dimen/activity_vertical_margin"
11     tools:context="com.dqxst.first.HttpActivity">
12 
13     <ImageView
14         android:layout_width="wrap_content"
15         android:layout_height="wrap_content"
16         android:id="@+id/http_imageView"
17         android:src="@drawable/loading"
18         android:contentDescription="test"/>
19 
20     <com.android.volley.toolbox.NetworkImageView
21         android:id="@+id/volley_NetworkImageView"
22         android:layout_width="200dp"
23         android:layout_height="200dp" />
24 </LinearLayout>
activity_http.xml

  通过以上的基本使用代码,可以看到一些Volley的基本情况

  1、由于android的UI线程的限制,所以需要在新建线程中进行网络操作,所以网络操作需要涉及线程之间的通信,而一般使用的HTTP框架都需要对其进行自动处理进行简化,Volley框架也是如此,关于具体实现见下文的源码分析部分

  2、可以看到,Volley中最基本的使用就是第一种方式,就是通过继承Request对象来实现的请求方式。Volley本身就有StringRequest等一系列具体的实现。详细的说明见二。

  3、对于网络加载图片,由于android应用的可用内存通常比较有限,所以需要涉及缓存的部分,所以对于图片,Volley又提供了可以设置缓存的实现方式。

二、继承Request实现的HTTP请求。

  首先先看Volley中的StringRequest的实现,

技术分享
 1 package com.android.volley.toolbox;
 2 
 3 import com.android.volley.NetworkResponse;
 4 import com.android.volley.Request;
 5 import com.android.volley.Response;
 6 import com.android.volley.Response.ErrorListener;
 7 import com.android.volley.Response.Listener;
 8 import com.android.volley.toolbox.HttpHeaderParser;
 9 import java.io.UnsupportedEncodingException;
10 
11 public class StringRequest extends Request<String> {
12     private final Listener<String> mListener;
13 
14     //构造函数,主要作用有2,
15     //1、调用父类来进行初始化
16     //2、初始化监听类属性,用于监听Response服务器返回的结果
17     public StringRequest(int method, String url, Listener<String> listener, ErrorListener errorListener) {
18         super(method, url, errorListener);
19         this.mListener = listener;
20     }
21 
22     public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {
23         this(0, url, listener, errorListener);
24     }
25 
26     //该方法是响应的处理,调用监听器中的相应方法
27     protected void deliverResponse(String response) {
28         this.mListener.onResponse(response);
29     }
30 
31     //处理响应数据的方法,
32     //参数NetworkResponse是对响应结果的一种封装,包括了响应头和响应体data
33     protected Response<String> parseNetworkResponse(NetworkResponse response) {
34         String parsed;
35         try {
36             parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
37         } catch (UnsupportedEncodingException var4) {
38             parsed = new String(response.data);
39         }
40 
41         return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
42     }
43 }
44  
StringRequest源码

  通过学习上面的实现过程,可以对Volley进行扩展实现,满足个性化的使用。例如,

  1、对响应结果为XML的数据进行解析(就是将parseNetworkResponse中的数据通过xml的方式进行解析即可),

  2、使用Gson/fastjson对JSON数据进行解析,因为Volley中使用的是Android中自带的json解析方式,并且分为JsonArrayRequest和JsonObjectRequest两个具体实现

三、源码解析:

  1、在使用Volley时,第一步通常是创建一个RequestQueue对象,而且是由Volley对象的方法创建而不是直接new一个,源码见下

技术分享
 1     RequestQueue queue = Volley.newRequestQueue(HttpActivity.this);
 2     
 3     public static RequestQueue newRequestQueue(Context context) {
 4         return newRequestQueue(context, (HttpStack)null);
 5     }
 6 
 7     public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
 8         File cacheDir = new File(context.getCacheDir(), "volley");
 9         String userAgent = "volley/0";
10 
11         try {
12             String network = context.getPackageName();
13             PackageInfo queue = context.getPackageManager().getPackageInfo(network, 0);
14             userAgent = network + "/" + queue.versionCode;
15         } catch (NameNotFoundException var6) {
16             ;
17         }
18 
19         //1、创建http连接操作对象,其内部使用的是HttpURLConnection(sdk>=9)和HttpClient(sdk<9)
20         if(stack == null) {
21             if(VERSION.SDK_INT >= 9) {
22                 stack = new HurlStack();
23             } else {
24                 stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
25             }
26         }
27         
28         //2、创建一个Network对象,这里使用的是Volley中的实现BasicNetwork,
29         //!!!该对象主要是调用上面的http连接进行连接,并对响应结果进行处理,
30         BasicNetwork network1 = new BasicNetwork((HttpStack)stack);
31 
32         //3、创建RequestQueue对象,并调用start(),这一部分解释见2
33         RequestQueue queue1 = new RequestQueue(new DiskBasedCache(cacheDir), network1);
34         queue1.start();
35         return queue1;
36     }
通过Volley对象创建RequestQueue

  2、RequestQueue对象是整个Volley中最重要的一个对象,

技术分享
 1     public void start() {
 2         this.stop();
 3 
 4         //1、 创建一个mCacheDispatcher并调用其start(),其实就是创建一个缓存线程
 5         this.mCacheDispatcher = new CacheDispatcher(this.mCacheQueue, this.mNetworkQueue, this.mCache, this.mDelivery);
 6         this.mCacheDispatcher.start();
 7 
 8         //2、这里默认循环4次,创建4个NetworkDispatcher对象并调用start(),其实是创建四个工作线程
 9         for(int i = 0; i < this.mDispatchers.length; ++i) {
10             NetworkDispatcher networkDispatcher = new NetworkDispatcher(this.mNetworkQueue, this.mNetwork, this.mCache, this.mDelivery);
11             this.mDispatchers[i] = networkDispatcher;
12             networkDispatcher.start();
13         }
14 
15     }
RequestQueue.start()

  3、使用Volley的最后一步就是使用RequestQueue的add()将实现的Request加入到队列中,如queue.add(request);

技术分享
 1     public <T> Request<T> add(Request<T> request) {
 2         request.setRequestQueue(this);
 3         Set var2 = this.mCurrentRequests;
 4         synchronized(this.mCurrentRequests) {
 5             this.mCurrentRequests.add(request);
 6         }
 7 
 8         request.setSequence(this.getSequenceNumber());
 9         request.addMarker("add-to-queue");
10         //这里判断的是请求是否可以缓存,使用的是mShouldCache属性,默认值为true,即可以缓存
11         if(!request.shouldCache()) {
12             this.mNetworkQueue.add(request);
13             return request;
14         } else {
15             Map var8 = this.mWaitingRequests;
16             synchronized(this.mWaitingRequests) {
17                 String cacheKey = request.getCacheKey();
18                 if(this.mWaitingRequests.containsKey(cacheKey)) {
19                     Object stagedRequests = (Queue)this.mWaitingRequests.get(cacheKey);
20                     if(stagedRequests == null) {
21                         stagedRequests = new LinkedList();
22                     }
23 
24                     ((Queue)stagedRequests).add(request);
25                     this.mWaitingRequests.put(cacheKey, stagedRequests);
26                     if(VolleyLog.DEBUG) {
27                         VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", new Object[]{cacheKey});
28                     }
29                 } else {
30                     this.mWaitingRequests.put(cacheKey, (Object)null);
31                     //在这里将Request对象加入到缓存队列中进行操作
32                     this.mCacheQueue.add(request);
33                 }
34 
35                 return request;
36             }
37         }
38     }
add()源码

  4、通过上面的源码可以看到,最终的操作是落在

技术分享
 1 public class CacheDispatcher extends Thread {
 2 
 3     @Override
 4     public void run() {
 5         if (DEBUG) VolleyLog.v("start new dispatcher");
 6         Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
 7         // Make a blocking call to initialize the cache.
 8         mCache.initialize();
 9         //这里表明该线程一直在运行
10         while (true) {
11             try {
12                 // Get a request from the cache triage queue, blocking until
13                 // at least one is available.
14                 final Request<?> request = mCacheQueue.take();
15                 request.addMarker("cache-queue-take");
16                 // 检测请求是否被取消,是则退出
17                 if (request.isCanceled()) {
18                     request.finish("cache-discard-canceled");
19                     continue;
20                 }
21                 // 检测是否有缓存的结果,没有则调用NetworkQueue重新进行请求
22                 Cache.Entry entry = mCache.get(request.getCacheKey());
23                 if (entry == null) {
24                     request.addMarker("cache-miss");
25                     // Cache miss; send off to the network dispatcher.
26                     mNetworkQueue.put(request);
27                     continue;
28                 }
29                 // 检测缓存是否过期,是则重新请求
30                 if (entry.isExpired()) {
31                     request.addMarker("cache-hit-expired");
32                     request.setCacheEntry(entry);
33                     mNetworkQueue.put(request);
34                     continue;
35                 }
36                 // We have a cache hit; parse its data for delivery back to the request.
37                 request.addMarker("cache-hit");
38                 Response<?> response = request.parseNetworkResponse(
39                         new NetworkResponse(entry.data, entry.responseHeaders));
40                 request.addMarker("cache-hit-parsed");
41                 if (!entry.refreshNeeded()) {
42                     // Completely unexpired cache hit. Just deliver the response.
43                     mDelivery.postResponse(request, response);
44                 } else {
45                     // Soft-expired cache hit. We can deliver the cached response,
46                     // but we need to also send the request to the network for
47                     // refreshing.
48                     request.addMarker("cache-hit-refresh-needed");
49                     request.setCacheEntry(entry);
50                     // Mark the response as intermediate.
51                     response.intermediate = true;
52                     // Post the intermediate response back to the user and have
53                     // the delivery then forward the request along to the network.
54                     mDelivery.postResponse(request, response, new Runnable() {
55                         @Override
56                         public void run() {
57                             try {
58                                 mNetworkQueue.put(request);
59                             } catch (InterruptedException e) {
60                                 // Not much we can do about this.
61                             }
62                         }
63                     });
64                 }
65             } catch (InterruptedException e) {
66                 // We may have been interrupted because it was time to quit.
67                 if (mQuit) {
68                     return;
69                 }
70                 continue;
71             }
72         }
73     }
74 }
CacheDispatcher的run()
技术分享
 1 //该方法中最终要的就是以下几步:
 2 public class NetworkDispatcher extends Thread {
 3     ……
 4     @Override
 5     public void run() {
 6         Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
 7         Request<?> request;
 8         while (true) {
 9             try {
10                 request = mQueue.take();
11             } catch (InterruptedException e) {
12                 // We may have been interrupted because it was time to quit.
13                 if (mQuit) {
14                     return;
15                 }
16                 continue;
17             }
18             try {
19                 request.addMarker("network-queue-take");
20                 if (request.isCanceled()) {
21                     request.finish("network-discard-cancelled");
22                     continue;
23                 }
24                 addTrafficStatsTag(request);
25                 //1、执行网络请求,最终调用的是BasicNetwork中的对应方法
26                 NetworkResponse networkResponse = mNetwork.performRequest(request);
27                 request.addMarker("network-http-complete");
28                 // If the server returned 304 AND we delivered a response already,
29                 // we‘re done -- don‘t deliver a second identical response.
30                 if (networkResponse.notModified && request.hasHadResponseDelivered()) {
31                     request.finish("not-modified");
32                     continue;
33                 }
34                 //2、解析响应数据,最终调用的是实现Request接口的对象中的方法,这也是这个接口必须要完成的方法
35                 Response<?> response = request.parseNetworkResponse(networkResponse);
36                 request.addMarker("network-parse-complete");
37                 // Write to cache if applicable.
38                 // TODO: Only update cache metadata instead of entire record for 304s.
39                 if (request.shouldCache() && response.cacheEntry != null) {
40                     mCache.put(request.getCacheKey(), response.cacheEntry);
41                     request.addMarker("network-cache-written");
42                 }
43                 //3、交付响应结果,下面的操作最终调用的就是Listener/ErrListener中的onResponse(),!!!同时最重要的一点就是保证了其在主线程中完成。
44                 request.markDelivered();
45                 mDelivery.postResponse(request, response);
46             } catch (VolleyError volleyError) {
47                 parseAndDeliverNetworkError(request, volleyError);
48             } catch (Exception e) {
49                 VolleyLog.e(e, "Unhandled exception %s", e.toString());
50                 mDelivery.postError(request, new VolleyError(e));
51             }
52         }
53     }
54 }
NetworkDispatcher的run()

  !!!NetworkDispatcher可以看做是最终的操作类,而且肩负着线程跳转的任务,所以上面源码中的3个主要功能是重点

看完了源码的简要分析,就可以搞明白Volley官方对其进行解释的一幅图了,如下:

技术分享

参考:http://blog.csdn.net/guolin_blog/article/details/17482095

android的HTTP框架之Volley

标签:

原文地址:http://www.cnblogs.com/songfeilong2325/p/5487741.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!