标签:
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 }
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>
通过以上的基本使用代码,可以看到一些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
通过学习上面的实现过程,可以对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 }
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 }
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 }
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 }
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可以看做是最终的操作类,而且肩负着线程跳转的任务,所以上面源码中的3个主要功能是重点。
看完了源码的简要分析,就可以搞明白Volley官方对其进行解释的一幅图了,如下:
参考:http://blog.csdn.net/guolin_blog/article/details/17482095
标签:
原文地址:http://www.cnblogs.com/songfeilong2325/p/5487741.html