volley源码分析


         下图为Volley执行的简单流程的步骤


     这里以JsonRequest(即网络请求数据为json格式)为例: (先示例一段代码, 可以边看代码边分析)

1. 请求队列(缓存请求队列和网络请求队列)的初始化和启动 : 

public class VolleyApplication extends Application{

	@Override
	public void onCreate() {
		super.onCreate();
		
		RequestTest.init(getApplicationContext());
	}
}

===============================

public class RequestTest {

	private static RequestQueue mRequestQueue;

	public static void init(Context context) {
		mRequestQueue = Volley.newRequestQueue(context);
		
	}
	
	/**
	 * 发生请求 
	 * @param request
	 */
	public static void sendRequest(Request<?> request) {
		mRequestQueue.add(request);
<span style="white-space:pre">	</span>   // 将请求添加到队列中,代码中先判断请求是否需要缓存,需要则将请求添加到缓存请求队列中
<span style="white-space:pre">	</span>   //  不需要则将请求添加到网络请求队列中
	}
}<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>

2 创建对应类继承JsonRequest : (传入对应的请求参数 , 并重写解析方法 对网络数据进行解析返回)

public class VolleyRequest<T> extends JsonRequest<T> {

	private Gson mGson;
	
	private Class<T> mClass;//T表示你想要解析的java bean的类

	/**
	 * 
	 * @param method 请求方法 GET POST (PUT DELETE),假设目前只有获取数据(只有GET可以直接写)
	 * @param url 网络请求地址
	 * @param requestBody 上传的数据
	 * @param listener 网络请求成功返回回调, 能够获取到解析后的结果
	 * @param errorListener 网络请求失败的回调
	 */
	public Volley<span style="font-family: Arial, Helvetica, sans-serif;">Request(Class<T> classz, String url, Listener<T> listener, ErrorListener errorListener) {</span>
		super(Method.GET, url, null, listener, errorListener);
		mGson = new Gson();
		mClass = classz;
	}

	/**
	 * 解析网络请求的结果,回调listener,返回解析解析后的结果,前提是网络已经成功返回
	 * 在子线程解析 , 后边将解析结果分发给主线程
	 */
	@Override
	protected Response parseNetworkResponse(NetworkResponse response) {
		//将网络返回字节数组转成字符串
		try {
			String result= new String(response.data, PROTOCOL_CHARSET);
		
			T resultBean = mGson.fromJson(result, mClass);

			//从网络请求返回的响应头里面解析出缓存相关的数据,比如缓存过期时间
			Cache.Entry cacheEntry = HttpHeaderParser.parseCacheHeaders(response);
			//返回解析后的结果,构成一个Response
			return Response.success(resultBean, cacheEntry);
			
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

}
3,   将请求添加到请求队列中

public class RequestDatas {

	public void loadDatas(){
		
		String url = "...";
		VolleyRequest<TestBean> request = new VolleyRequest<TestBean>(TestBean.class<span style="font-family: Arial, Helvetica, sans-serif;">, url, </span>
				successListener, errorListener);
		
		RequestTest.sendRequest(request);     // 即调用  <span style="font-family: Arial, Helvetica, sans-serif;">mRequestQueue.add(request);</span>

	}
	//  回调已经被分发在主线程中执行
	private Listener<TestBean> successListener = new Listener<TestBean>(){

		@Override
		public void onResponse(TestBean bean) {
			//  成功的回调 
			
		}
		
	};
	
	private ErrorListener errorListener = new ErrorListener() {

		@Override
		public void onErrorResponse(VolleyError error) {
		    //  失败的回调
			
		}
		
	};
}

    以下来通过源码分析每个步骤的具体实现:     

               从代码最初的初始化一个请求队列开始, 由于一个app中请求数据的频繁, 可以将初始化请求队列的代码写在application中,表示当应用一开始创建就初始化好;

       RequestQueue requestQueue = Volley.newRequestQueue(getApplicationContext);   //  这一行代码里面分别执行了1和2两个步骤

1  请求队列的初始化 : 

            具体分析 :  (1) Volley.newRequestQueue(context)方法中调用了 : 

                                          newRequestQueue(context, null);  指的是 ==> newRequestQueue(Context context, HttpStack stack)  中又用了

                                          newRequestQueue(context, stack, -1);指的是 ==> newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes)

其中参数 stack 指的是栈,默认为null, 参数maxDiskCacheBytes指的是最大磁盘缓存字节大小,默认为-1;

                              在 newRequestQueue(.....)方法中会有两个判断: if (stack == null)  和  if (maxDiskCacheBytes <= -1) 作为第一次初始化栈和磁盘缓存大小的条件

             a . 初始化栈的请求方式:

 SDK版本在9或以上里面封装的是HttpUrlConnextion比较常用的也比较轻量

                 SDK版本在9以前用的是阿帕奇下的第三方请求方式 :里面封装的是 httpclient,因为9以上的HttpUrlConnextion并不稳定(可能会出BUG)

if (stack == null) {
            if (Build.VERSION.SDK_INT >= 9) {
                stack = new HurlStack();
            } else {
                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
            }
        }

接下来是创建了一个network对象 ==> Network network = new BasicNetwork(stack);

并把这个stack传给了一个网络工作的对象

          b . 初始化了磁盘缓存的对象,里面定义了缓存的路径和大小 : 路径就是data/data/包名/cache/volley,  默认大小是5M

          c . 最后是正式的创建一个请求队列的对象, 并把netWork对象和缓存对象传给他

queue = new RequestQueue(new DiskBasedCache(cacheDir), network);方法里调用了this.RequestQueue(....),

最后是调用了RequestQueue(Cache cache, Network network, int threadPoolSize, ResponseDelivery delivery)又传入了两个参数 : 

线程池大小threadPoolSize (默认为开启四个线程NetworkDispatcher)  ====> 定义了在线程池中执行网络请求,线程池中可以开启四个子线程

mDispatchers = new NetworkDispatcher[threadPoolSize]; //threadPoolSize = 4;

响应的分发器 delivery ===> 定义了将网络请求的结果处理分发到主线程去处理

delivery =new ExecutorDelivery(new Handler(Looper.getMainLooper())); { handle.post() };

2 请求队列的启动 ( mCacheQueue缓存的请求队列,mNetworkQueue网络请求队列)

由代码 queue.start(); 开始: // 这里面总共开启了五个子线程 , 一个为获取缓存数据线程 , 四个为获取网络数据线程

a. 创建缓存的分发器CacheDispather并且启动它
     mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
     mCacheDispatcher.start();

这里开启了一个缓存线程, 调用了其中的run()方法, 方法中 :

(1) . mCache.initialize(); // 磁盘缓存的初始化  读出所有缓存的文件的头(包括key判断是否有缓存数据,ttl记录缓存过期时间等等),

并没有读出数据(当网络请求有缓存,再去读取数据),将CacheHeader存入头的集合

private final Map<String, CacheHeader> mEntries =  new LinkedHashMap<String, CacheHeader>(16, .75f, true);

(2) . while (true) { ... } // 这里是一个死循环 , 判断缓存队列 mCacheQueue 中有没有请求, 有的话将其取出, 查询一下有没有缓存
         Cache.Entry entry = mCache.get(request.getCacheKey()); 

如果entry不为null,有缓存,并且缓存没有过期,就解析缓存网络请求的响应

 Response<?> response = request.parseNetworkResponse( new NetworkResponse(entry.data, entry.responseHeaders));  

并将解析后的结果分发到主线程中 : 

mDelivery.postResponse(request, response); 

如果缓存没有或者缓存已经过期 , 就将请求分发给网络请求队列

mNetworkQueue.put(request);
b. 网络请求线程池的网络线程的创建,创建4个NetworkDispather
     NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,mCache, mDelivery);
networkDispatcher.start(); // 并且启动这四个线程

                            在其中的run()方法中: 

while (true) { ... } // 这里也是一个死循环 , 判断网络请求队列 mNetworkQueue 中有没有请求, 有的话则执行网络请求  

NetworkResponse networkResponse = mNetwork.performRequest(request);

Response<?> response = request.parseNetworkResponse(networkResponse);

同时将网络请求的结果做缓存处理 : 

mCache.put(request.getCacheKey(), response.cacheEntry);

                                                        最后同样将解析后的结果分发到主线程中

mDelivery.postResponse(request, response);

(注 : 方法parseNetworkResponse()  为一个抽象方法 , 解析数据逻辑需要自己重写去实现 )



         

EnMAP-Box是一款高效、便捷的遥感图像处理软件,其独特之处在于它是一个免安装的应用程序,用户可以直接运行而无需进行复杂的安装过程。这款工具主要用于处理和分析来自各种遥感传感器的数据,如EnMAP(环境多波段光谱成像仪)和其他同类设备获取的高光谱图像。EnMAP-Box的设计目标是为科研人员和实践工作者提供一个直观、易用的平台,以执行复杂的遥感数据处理任务。 在使用EnMAP-Box之前,一个关键的前提条件是需要有一个兼容的IDL(Interactive Data Language)环境。IDL是一种强大的编程语言,特别适用于科学数据的处理和可视化,尤其是在地球科学和遥感领域。它提供了丰富的库函数,支持对多维数组操作,这使得它成为处理遥感图像的理想选择。EnMAP-Box是基于IDL开发的,因此,用户在使用该软件之前需要确保已经正确配置了IDL环境。 EnMAP-Box的主要功能包括: 1. 数据导入:能够读取多种遥感数据格式,如ENVI、HDF、GeoTIFF等,方便用户将不同来源的遥感图像导入到软件中进行分析。 2. 预处理:提供辐射校正、大气校正、几何校正等功能,用于改善原始图像的质量,确保后续分析的准确性。 3. 分光分析:支持高光谱图像的光谱特征提取,如光谱指数计算、光谱端元分离等,有助于识别地物类型和监测环境变化。 4. 图像分类:通过监督或非监督方法进行图像分类,可以自动或半自动地将图像像素划分为不同的地物类别。 5. 时间序列分析:对于多时相遥感数据,EnMAP-Box能进行时间序列分析,揭示地表动态变化趋势。 6. 结果导出与可视化:处理后的结果可以导出为各种格式,同时软件内置了图像显示和地图投影功能,帮助用户直观地查看和理解处理结果。 7. 自定义脚本:利用IDL的强大功能,用户可以编写自定义脚本来实现特定的遥感处理需求,增强了软件的灵活性和可扩展性。 在使用EnMAP-Box的过程中,用户可能会遇到一些挑战,例如对IDL编程语言不熟悉,或者对遥感数据处理的基本概念和方法缺乏了解。这时,可以通过查阅软件自带的文档、教程,以及在线资源来提升技能。同时,积极参与相关的学习社区和论坛,与其他用户交流经验,可以帮助解决遇到的问题。 EnMAP-Box作为一款基于IDL的遥感图像处理工具,为遥感数据分析提供了便利,但需要用户具备一定的IDL基础和遥感知识。通过熟练掌握EnMAP-Box,用户可以高效地处理和解析遥感数据,揭示地表信息,为环境保护、资源管理等领域提供科学支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值