[置顶] Android网络通信Volley框架源码浅析(二)

2024-06-10 07:18

本文主要是介绍[置顶] Android网络通信Volley框架源码浅析(二),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

[置顶] Android网络通信Volley框架源码浅析(二)



尊重原创 http://write.blog.csdn.net/postedit/25921795

在前面的一片文章Volley框架浅析(一)中我们知道在RequestQueue这个类中,有两个队列:本地队列和网络队列

[java] view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. /** The cache triage queue. */  
  2.     private final PriorityBlockingQueue<Request<?>> mCacheQueue =  
  3.         new PriorityBlockingQueue<Request<?>>();  
  4.   
  5.     /** The queue of requests that are actually going out to the network. */  
  6.     private final PriorityBlockingQueue<Request<?>> mNetworkQueue =  
  7.         new PriorityBlockingQueue<Request<?>>();  

与之对应的分别有本地线程和网络线程,通过对RequestQueue源码的分析,本地线程有一条,而网络线程默认有四条,我们可以对网络线程的个数进行设置,我们首先来学习一下本地线程:

(1) CacheDispatcher.java

[java] view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. public class CacheDispatcher extends Thread {  
  2.   
  3.     private static final boolean DEBUG = VolleyLog.DEBUG;  
  4.   
  5.     //本地队列,从RequestQueue中传递进来的  
  6.     private final BlockingQueue<Request<?>> mCacheQueue;  
  7.   
  8.     //网络请求队列,也是从RequestQueue中传递进来,当本地缓存没有命中时,需要把请求从本地队列加入网络队列  
  9.     private final BlockingQueue<Request<?>> mNetworkQueue;  
  10.   
  11.     //磁盘缓存对象  
  12.     private final Cache mCache;  
  13.   
  14.     //就是用于从子线程向Ui线程发送数据  
  15.     private final ResponseDelivery mDelivery;  
  16.   
  17.     /** Used for telling us to die. */  
  18.     private volatile boolean mQuit = false;  
  19.   
  20.     /** 
  21.      * Creates a new cache triage dispatcher thread.  You must call {@link #start()} 
  22.      * in order to begin processing. 
  23.      * 
  24.      * @param cacheQueue Queue of incoming requests for triage 
  25.      * @param networkQueue Queue to post requests that require network to 
  26.      * @param cache Cache interface to use for resolution 
  27.      * @param delivery Delivery interface to use for posting responses 
  28.      */  
  29.     public CacheDispatcher(  
  30.             BlockingQueue<Request<?>> cacheQueue, BlockingQueue<Request<?>> networkQueue,  
  31.             Cache cache, ResponseDelivery delivery) {  
  32.         mCacheQueue = cacheQueue;  
  33.         mNetworkQueue = networkQueue;  
  34.         mCache = cache;  
  35.         mDelivery = delivery;  
  36.     }  
  37.   
  38.     /** 
  39.      * Forces this dispatcher to quit immediately.  If any requests are still in 
  40.      * the queue, they are not guaranteed to be processed. 
  41.      */  
  42.     public void quit() {  
  43.         mQuit = true;  
  44.         interrupt();  
  45.     }  
  46.   
  47.     @Override  
  48.     public void run() {  
  49.         if (DEBUG) VolleyLog.v("start new dispatcher");  
  50.         Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  
  51.   
  52.         // 缓存初始化,将磁盘中的数据读入内存  
  53.         mCache.initialize();  
  54.   
  55.         while (true) {  
  56.             try {  
  57.                   
  58.                 // 阻塞式从队列中取出请求  
  59.                 final Request<?> request = mCacheQueue.take();  
  60.                 request.addMarker("cache-queue-take");  
  61.   
  62.                 // 判断request是否被取消了(调用cancel方法),如果取消了就不执行,再次到队列中取请求  
  63.                 if (request.isCanceled()) {  
  64.                     request.finish("cache-discard-canceled");  
  65.                     continue;  
  66.                 }  
  67.   
  68.                 // 从缓存中读取数据  
  69.                 Cache.Entry entry = mCache.get(request.getCacheKey());  
  70.                 if (entry == null) {  
  71.                     //没有命中  
  72.                     request.addMarker("cache-miss");  
  73.                     // 没有命中时,就将请求放入网络队列  
  74.                     mNetworkQueue.put(request);  
  75.                     continue;  
  76.                 }  
  77.   
  78.                 // 数据已经过期,将请求放入网络队列  
  79.                 if (entry.isExpired()) {  
  80.                     request.addMarker("cache-hit-expired");  
  81.                     request.setCacheEntry(entry);  
  82.                     mNetworkQueue.put(request);  
  83.                     continue;  
  84.                 }  
  85.   
  86.                 // 本地命中  
  87.                 request.addMarker("cache-hit");  
  88.                 Response<?> response = request.parseNetworkResponse(  
  89.                         new NetworkResponse(entry.data, entry.responseHeaders));  
  90.                 request.addMarker("cache-hit-parsed");  
  91.   
  92.                 if (!entry.refreshNeeded()) {  
  93.                     // Completely unexpired cache hit. Just deliver the response.  
  94.                     //命中,并且不需要刷新  
  95.                     mDelivery.postResponse(request, response);  
  96.                 } else {  
  97.                     //命中,需要刷新,将请求放入网络队列,这里面的代码其实可以根据需求自己重写  
  98.                     // Soft-expired cache hit. We can deliver the cached response,  
  99.                     // but we need to also send the request to the network for  
  100.                     // refreshing.  
  101.                     request.addMarker("cache-hit-refresh-needed");  
  102.                     request.setCacheEntry(entry);  
  103.   
  104.                     // Mark the response as intermediate.  
  105.                     response.intermediate = true;  
  106.   
  107.                     // Post the intermediate response back to the user and have  
  108.                     // the delivery then forward the request along to the network.  
  109.                     mDelivery.postResponse(request, response, new Runnable() {  
  110.                         @Override  
  111.                         public void run() {  
  112.                             try {  
  113.                                 mNetworkQueue.put(request);  
  114.                             } catch (InterruptedException e) {  
  115.                                 // Not much we can do about this.  
  116.                             }  
  117.                         }  
  118.                     });  
  119.                 }  
  120.   
  121.             } catch (InterruptedException e) {  
  122.                 // We may have been interrupted because it was time to quit.  
  123.                 if (mQuit) {  
  124.                     return;  
  125.                 }  
  126.                 continue;  
  127.             }  
  128.         }  
  129.     }  
  130. }  

(2) NetworkDispatcher.java

[java] view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. public class NetworkDispatcher extends Thread {  
  2.     /** 网络队列 */  
  3.     private final BlockingQueue<Request<?>> mQueue;  
  4.     /** 用于Http请求,根据前面的学习,他其实使用的是HttpURLConnection或者HttpClient. */  
  5.     private final Network mNetwork;  
  6.     /** 本地缓存,网络请求成功后,放入缓存. */  
  7.     private final Cache mCache;  
  8.     /** For posting responses and errors. */  
  9.     private final ResponseDelivery mDelivery;  
  10.     /** Used for telling us to die. */  
  11.     private volatile boolean mQuit = false;  
  12.   
  13.     /** 
  14.      * Creates a new network dispatcher thread.  You must call {@link #start()} 
  15.      * in order to begin processing. 
  16.      * 
  17.      * @param queue Queue of incoming requests for triage 
  18.      * @param network Network interface to use for performing requests 
  19.      * @param cache Cache interface to use for writing responses to cache 
  20.      * @param delivery Delivery interface to use for posting responses 
  21.      */  
  22.     public NetworkDispatcher(BlockingQueue<Request<?>> queue,  
  23.             Network network, Cache cache,  
  24.             ResponseDelivery delivery) {  
  25.         mQueue = queue;  
  26.         mNetwork = network;  
  27.         mCache = cache;  
  28.         mDelivery = delivery;  
  29.     }  
  30.   
  31.     /** 
  32.      * Forces this dispatcher to quit immediately.  If any requests are still in 
  33.      * the queue, they are not guaranteed to be processed. 
  34.      */  
  35.     public void quit() {  
  36.         mQuit = true;  
  37.         interrupt();  
  38.     }  
  39.   
  40.     @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)  
  41.     private void addTrafficStatsTag(Request<?> request) {  
  42.         // Tag the request (if API >= 14)  
  43.         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {  
  44.             TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());  
  45.         }  
  46.     }  
  47.   
  48.     @Override  
  49.     public void run() {  
  50.         Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  
  51.         Request<?> request;  
  52.         while (true) {  
  53.             try {  
  54.                 // 从队列中阻塞式取出一个请求.  
  55.                 request = mQueue.take();  
  56.             } catch (InterruptedException e) {  
  57.                 // We may have been interrupted because it was time to quit.  
  58.                 if (mQuit) {  
  59.                     return;  
  60.                 }  
  61.                 continue;  
  62.             }  
  63.   
  64.             try {  
  65.                 request.addMarker("network-queue-take");  
  66.   
  67.                   
  68.                 // 同理需要判断是否取消,如果取消执行下一个请求  
  69.                 if (request.isCanceled()) {  
  70.                     request.finish("network-discard-cancelled");  
  71.                     continue;  
  72.                 }  
  73.   
  74.                 addTrafficStatsTag(request);  
  75.   
  76.                 // 通过NetWork的perfromRequest方法放回一个NetworkResponse对象  
  77.                 NetworkResponse networkResponse = mNetwork.performRequest(request);  
  78.                 request.addMarker("network-http-complete");  
  79.   
  80.                   
  81.                 // 如果这个返回结果已经发送到了ui线程,就将它finish  
  82.                 if (networkResponse.notModified && request.hasHadResponseDelivered()) {  
  83.                     request.finish("not-modified");  
  84.                     continue;  
  85.                 }  
  86.   
  87.                 // 将NetworkResponse 解析成Response.  
  88.                 Response<?> response = request.parseNetworkResponse(networkResponse);  
  89.                 request.addMarker("network-parse-complete");  
  90.   
  91.                   
  92.                 // 如果需要缓存,那么将结果存入缓存  
  93.                 if (request.shouldCache() && response.cacheEntry != null) {  
  94.                     mCache.put(request.getCacheKey(), response.cacheEntry);  
  95.                     request.addMarker("network-cache-written");  
  96.                 }  
  97.   
  98.                 // 标记为已经发送  
  99.                 request.markDelivered();  
  100.                 //将数据发送到Ui线程  
  101.                 mDelivery.postResponse(request, response);  
  102.             } catch (VolleyError volleyError) {  
  103.                 parseAndDeliverNetworkError(request, volleyError);  
  104.             } catch (Exception e) {  
  105.                 VolleyLog.e(e, "Unhandled exception %s", e.toString());  
  106.                 mDelivery.postError(request, new VolleyError(e));  
  107.             }  
  108.         }  
  109.     }  
  110.   
  111.     private void parseAndDeliverNetworkError(Request<?> request, VolleyError error) {  
  112.         error = request.parseNetworkError(error);  
  113.         mDelivery.postError(request, error);  
  114.     }  
  115. }  
通过上面的代码,我们来总结一下一个请求的执行过程吧:
1、一个请求就是一个Request对象,首先将Request对象加入到RequestQueue中.
2、判断Request是否可以缓存,如果可以,则加入到本地缓存队列,否则加入网络队列
3、本地线程不断监听本地队列是否有请求,如果有请求取出来
4、判断Request是否取消,如果取消,处理下一个请求
5、判断缓存是否命中,如果没有命中,将该请求加入网络队列
6、如果命中,但是过期,同样将该请求加入网络队列
7、如果命中,并且不用刷新,那么直接放回结果,不用加入网络队列
8、如果命中,并且需要刷新,那么放回结果,并且加入网络队列
9、同样4条网络线程也在不断监听网络队列是否有请求,一旦发现有请求,取出请求,判断是否取消,如果取消,那么取出下一个请求
10、如果没有取消,那么通过NetWork进行http请求,将请求结果封装成NetworkResponse,然后转换为Response
11、如果可以缓存,那么将数据写入缓存
12、通过Delivery将Response返回到ui线程



通过以上12步,完成了一个完整的请求

研究了这么久,我们还没有研究Request和Response是什么呢,如果熟悉http请求的同学相信很好理解,
Request就是一个http请求,Response就是http返回的内容,先看看Request这个类吧
Request是一个抽象类,我只介绍比较重要的几个方法:

[java] view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. public abstract class Request<T> implements Comparable<Request<T>> {  
  2.     //Http 请求方法 POST,GET  
  3.     private final int mMethod;  
  4.   
  5.     /** 请求URL*/  
  6.     private final String mUrl;  
  7.     //用于出错时的回调接口  
  8.     private final Response.ErrorListener mErrorListener;  
  9.   
  10.     /** 这个请求在队列中的顺序 */  
  11.     private Integer mSequence;  
  12.   
  13.    ...  
  14.   
  15.     /** 是否可以缓存 */  
  16.     private boolean mShouldCache = true;  
  17.   
  18.     /** 是否已经取消了,网络线程和本地线程都会对此判断,如果取消了就不请求了 */  
  19.     private boolean mCanceled = false;  
  20.   
  21.     /** 请求策略,比如设置最大重试次数之类的*/  
  22.     private RetryPolicy mRetryPolicy;  
  23.   
  24.     /** 
  25.      * Creates a new request with the given method (one of the values from {@link Method}), 
  26.      * URL, and error listener.  Note that the normal response listener is not provided here as 
  27.      * delivery of responses is provided by subclasses, who have a better idea of how to deliver 
  28.      * an already-parsed response. 
  29.      */  
  30.     public Request(int method, String url, Response.ErrorListener listener) {  
  31.         mMethod = method;  
  32.         mUrl = url;  
  33.         mErrorListener = listener;  
  34.         setRetryPolicy(new DefaultRetryPolicy());  
  35.   
  36.         mDefaultTrafficStatsTag = findDefaultTrafficStatsTag(url);  
  37.     }  
  38.   
  39.     
  40.     /** 
  41.      * Sets the retry policy for this request. 
  42.      * 
  43.      * @return This Request object to allow for chaining. 
  44.      */  
  45.     public Request<?> setRetryPolicy(RetryPolicy retryPolicy) {  
  46.         mRetryPolicy = retryPolicy;  
  47.         return this;  
  48.     }  
  49.      
  50.    ...  
  51.   
  52.     /** 
  53.      * 通过此方法取消一个请求 
  54.      */  
  55.     public void cancel() {  
  56.         mCanceled = true;  
  57.     }  
  58.   
  59.     /** 
  60.      * 判断是否已经取消. 
  61.      */  
  62.     public boolean isCanceled() {  
  63.         return mCanceled;  
  64.     }  
  65.   
  66.     /** 
  67.      * 获取请求头 
  68.      * @throws AuthFailureError In the event of auth failure 
  69.      */  
  70.     public Map<String, String> getHeaders() throws AuthFailureError {  
  71.         return Collections.emptyMap();  
  72.     }  
  73.   
  74.     /** 
  75.      * Returns a Map of POST parameters to be used for this request, or null if 
  76.      * a simple GET should be used.  Can throw {@link AuthFailureError} as 
  77.      * authentication may be required to provide these values. 
  78.      * 
  79.      * <p>Note that only one of getPostParams() and getPostBody() can return a non-null 
  80.      * value.</p> 
  81.      * @throws AuthFailureError In the event of auth failure 
  82.      * 
  83.      * @deprecated Use {@link #getParams()} instead. 
  84.      */  
  85.     @Deprecated  
  86.     protected Map<String, String> getPostParams() throws AuthFailureError {  
  87.         return getParams();  
  88.     }  
  89.      
  90.   
  91.     /** 
  92.      * Returns a Map of parameters to be used for a POST or PUT request.  Can throw 
  93.      * {@link AuthFailureError} as authentication may be required to provide these values. 
  94.      * 
  95.      * <p>Note that you can directly override {@link #getBody()} for custom data.</p> 
  96.      * 
  97.      * @throws AuthFailureError in the event of auth failure 
  98.      */  
  99.     protected Map<String, String> getParams() throws AuthFailureError {  
  100.         return null;  
  101.     }  
  102.   
  103.     
  104.   
  105.     public String getBodyContentType() {  
  106.         return "application/x-www-form-urlencoded; charset=" + getParamsEncoding();  
  107.     }  
  108.   
  109.       
  110.       
  111.   
  112.     /** 
  113.      * 设置能否缓存 
  114.      * 
  115.      * @return This Request object to allow for chaining. 
  116.      */  
  117.     public final Request<?> setShouldCache(boolean shouldCache) {  
  118.         mShouldCache = shouldCache;  
  119.         return this;  
  120.     }  
  121.   
  122.     /** 
  123.      * 判断是否能够缓存 
  124.      */  
  125.     public final boolean shouldCache() {  
  126.         return mShouldCache;  
  127.     }  
  128.       
  129.     /** 
  130.      * 这是个抽象方法,我们必须实现,用于将NetworkResponse 转化为Response 
  131.      * @param response Response from the network 
  132.      * @return The parsed response, or null in the case of an error 
  133.      */  
  134.     abstract protected Response<T> parseNetworkResponse(NetworkResponse response);  
  135.   
  136.       
  137.   
  138.     /** 
  139.      * 这个我们也必须实现,用于将Response发送到ui线程 
  140.      * @param response The parsed response returned by 
  141.      * {@link #parseNetworkResponse(NetworkResponse)} 
  142.      */  
  143.     abstract protected void deliverResponse(T response);  
  144.   
  145. }  
  146.   
  147. 下面继续看看Response这个类:  
  148. public class Response<T> {  
  149.   
  150.     /** 成功的时候回调. */  
  151.     public interface Listener<T> {  
  152.         /** Called when a response is received. */  
  153.         public void onResponse(T response);  
  154.     }  
  155.   
  156.     /** 失败的时候回调 */  
  157.     public interface ErrorListener {  
  158.         /** 
  159.          * Callback method that an error has been occurred with the 
  160.          * provided error code and optional user-readable message. 
  161.          */  
  162.         public void onErrorResponse(VolleyError error);  
  163.     }  
  164.   
  165.     /** 成功的时候创建一个Response. */  
  166.     public static <T> Response<T> success(T result, Cache.Entry cacheEntry) {  
  167.         return new Response<T>(result, cacheEntry);  
  168.     }  
  169.   
  170.     /** 
  171.      * 失败的时候创建一个Response 
  172.      */  
  173.     public static <T> Response<T> error(VolleyError error) {  
  174.         return new Response<T>(error);  
  175.     }  
  176.   
  177.     /** Parsed response, or null in the case of error. */  
  178.     public final T result;  
  179.   
  180.     
  181.   
  182.     /** 
  183.      * Returns whether this response is considered successful. 
  184.      */  
  185.     public boolean isSuccess() {  
  186.         return error == null;  
  187.     }  
  188.   
  189.     //私有的,我们无法调用  
  190.     private Response(T result, Cache.Entry cacheEntry) {  
  191.         this.result = result;  
  192.         this.cacheEntry = cacheEntry;  
  193.         this.error = null;  
  194.     }  
  195.   
  196.     private Response(VolleyError error) {  
  197.         this.result = null;  
  198.         this.cacheEntry = null;  
  199.         this.error = error;  
  200.     }  
  201. }  

学习了上面两个类后,我们需要知道如下知识:
Volley中的任何请求都是继承Request的,如Volley提供的StringRequest,JsonArrayRequest,JsonObjectRequest
ImageRequest等等,并且要实现其中的两个方法
abstract protected Response<T> parseNetworkResponse(NetworkResponse response);

abstract protected void deliverResponse(T response);

T是泛型,StringRequest中T表示String,后期我将会简单介绍这几种Request的使用,敬请大家期待。。。

最后在介绍一个接口,就是ResponseDelivery.java

它的一个实现类是ExecutorDelivery.java


[java] view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. public class ExecutorDelivery implements ResponseDelivery {  
  2.     /** 执行已提交的 Runnable 任务的对象。此接口提供一种将任务提交与每个任务将如何运行的机制(包括线程使用的细节、调度等)分离开来的方法,在线程池中经常用到 */  
  3.     private final Executor mResponsePoster;  
  4.   
  5.     /** 
  6.      * 传入一个Handler,其实就是运行在主线的Handler,我想你应该明白为什么他能够从子线程 
  7.      将数据传入ui线程了 
  8.      * @param handler {@link Handler} to post responses on 
  9.      */  
  10.     public ExecutorDelivery(final Handler handler) {  
  11.         // Make an Executor that just wraps the handler.  
  12.         mResponsePoster = new Executor() {  
  13.             @Override  
  14.             public void execute(Runnable command) {  
  15.                 //这里调用了handler的post方法  
  16.                 handler.post(command);  
  17.             }  
  18.         };  
  19.     }  
  20.   
  21.     /** 
  22.      * Creates a new response delivery interface, mockable version 
  23.      * for testing. 
  24.      * @param executor For running delivery tasks 
  25.      */  
  26.     public ExecutorDelivery(Executor executor) {  
  27.         mResponsePoster = executor;  
  28.     }  
  29.   
  30.     @Override  
  31.     public void postResponse(Request<?> request, Response<?> response) {  
  32.         postResponse(request, response, null);  
  33.     }  
  34.   
  35.     @Override  
  36.     public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {  
  37.         request.markDelivered();  
  38.         request.addMarker("post-response");  
  39.         mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));  
  40.     }  
  41.   
  42.     
  43.     /** 
  44.      * A Runnable used for delivering network responses to a listener on the 
  45.      * main thread. 
  46.      */  
  47.     @SuppressWarnings("rawtypes")  
  48.     private class ResponseDeliveryRunnable implements Runnable {  
  49.         private final Request mRequest;  
  50.         private final Response mResponse;  
  51.         private final Runnable mRunnable;  
  52.   
  53.         public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {  
  54.             mRequest = request;  
  55.             mResponse = response;  
  56.             mRunnable = runnable;  
  57.         }  
  58.   
  59.         @SuppressWarnings("unchecked")  
  60.         @Override  
  61.         public void run() {  
  62.             // If this request has canceled, finish it and don't deliver.  
  63.             if (mRequest.isCanceled()) {  
  64.                 mRequest.finish("canceled-at-delivery");  
  65.                 return;  
  66.             }  
  67.   
  68.             // Deliver a normal response or error, depending.  
  69.             if (mResponse.isSuccess()) {  
  70.                 //在这里调用了deliverResponse  
  71.                 mRequest.deliverResponse(mResponse.result);  
  72.             } else {  
  73.                 mRequest.deliverError(mResponse.error);  
  74.             }  
  75.   
  76.             // If this is an intermediate response, add a marker, otherwise we're done  
  77.             // and the request can be finished.  
  78.             if (mResponse.intermediate) {  
  79.                 mRequest.addMarker("intermediate-response");  
  80.             } else {  
  81.                 mRequest.finish("done");  
  82.             }  
  83.   
  84.             // If we have been provided a post-delivery runnable, run it.  
  85.             if (mRunnable != null) {  
  86.                 mRunnable.run();  
  87.             }  
  88.        }  
  89.     }  
  90. }

这篇关于[置顶] Android网络通信Volley框架源码浅析(二)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1047497

相关文章

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

如何在Visual Studio中调试.NET源码

今天偶然在看别人代码时,发现在他的代码里使用了Any判断List<T>是否为空。 我一般的做法是先判断是否为null,再判断Count。 看了一下Count的源码如下: 1 [__DynamicallyInvokable]2 public int Count3 {4 [__DynamicallyInvokable]5 get

cross-plateform 跨平台应用程序-03-如果只选择一个框架,应该选择哪一个?

跨平台系列 cross-plateform 跨平台应用程序-01-概览 cross-plateform 跨平台应用程序-02-有哪些主流技术栈? cross-plateform 跨平台应用程序-03-如果只选择一个框架,应该选择哪一个? cross-plateform 跨平台应用程序-04-React Native 介绍 cross-plateform 跨平台应用程序-05-Flutte

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、

Spring框架5 - 容器的扩展功能 (ApplicationContext)

private static ApplicationContext applicationContext;static {applicationContext = new ClassPathXmlApplicationContext("bean.xml");} BeanFactory的功能扩展类ApplicationContext进行深度的分析。ApplicationConext与 BeanF

android-opencv-jni

//------------------start opencv--------------------@Override public void onResume(){ super.onResume(); //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是 //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存