Heim  >  Artikel  >  Java  >  Android-Entwicklung – detaillierte Analyse des Volley-Quellcodes

Android-Entwicklung – detaillierte Analyse des Volley-Quellcodes

黄舟
黄舟Original
2017-03-10 09:24:491776Durchsuche

0. Vorwort

Eigentlich wurde dieser Artikel nur für einen Zweck geschrieben, Obwohl VolleyEs ist großartig zu verwenden, aber der Interviewer fragt Sie, wie Sie es intern implementieren können. Wenn Sie den Quellcode nicht gesehen haben, sind Sie in den Augen des Interviewers der das Gleiche wie jemand, der ein Buch hältVolleyballEs gibt keinen Unterschied zwischen einem Gymnasiasten, der ein Handbuch benutzt. Wie man so schön sagt: Zu wissen, wie man es benutzt, ist eine Sache, aber es gründlich zu verstehen, ist eine andere.

1. VolleyQuellcode-Analyse

1.1 Volley Eingang

VolleyDas erste, was man bekommt, ist die RequestQueue-Instanz. Der Quellcode ruft direkt die newRequestQueue-Methode auf.

public static RequestQueue newRequestQueue(Context context) {  
    return newRequestQueue(context, null);  
}  
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {  
    File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);  
    String userAgent = "volley/0";  
    try {  
        String packageName = context.getPackageName();  
        PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);  
        userAgent = packageName + "/" + info.versionCode;  
    } catch (NameNotFoundException e) {  
    }  
    if (stack == null) {  
        if (Build.VERSION.SDK_INT >= 9) {  
            stack = new HurlStack();  
        } else {  
            stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));  
        }  
    }  
    Network network = new BasicNetwork(stack);  
    RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);  
    queue.start();  
    return queue;  
}


Wie Sie dem obigen Quellcode entnehmen können, wird festgestellt, dass der eingehende Stack ist leer, dann erstellt ein HttpStack-Objekt entsprechend der Systemversion, das größer oder gleich 9, erstellen Sie eine Instanz von HurlStack (intern verwenden Sie HttpURLConnection) , andernfalls erstellen Sie eine HttpClientStack (intern unter Verwendung von HttpClient) . Dies geschieht, weil:

(

1)HttpURLConnection in FEHLER in der Version von 🎜>9, dass der Aufruf von imputStream.close() den Verbindungspool verursacht , relativ gesehen, hat HTTPClient weniger BUGs in Systemversionen unter 9 . (

2) und in Versionen größer oder gleich 9, HttpURLConnection Implementierung hat mehr Vorteile, API ist einfacher, kleiner, hat eine Komprimierungsfunktion und 4.0Der später hinzugefügte -Caching-Mechanismus (z. B. lokaler Cache, 304-Cache, Server-Cache). ). Nachdem HttpStack erstellt wurde, wird es als Parameter zum Erstellen eines Netzwerkobjekts verwendet und anschließend ein

RequestQueue

-Objekt 🎜>, und rufen Sie die Methode start() auf, um zu starten, und geben Sie dann die Instanz zurück.

1.2 RequestQueue.start()

public void start() {  
    stop(); 
    mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);  
    mCacheDispatcher.start();  
for (int i = 0; i < mDispatchers.length; i++) { //默认循环4次 
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,  mCache, mDelivery);  
        mDispatchers[i] = networkDispatcher;  
        networkDispatcher.start();  
    }  
}

这里CacheDispatcherNetworkDispatcher都是继承自Thread的,也就是说当调用了Volley.newRequestQueue(context)之后,一个缓存线程和四个网络请求线程会一直在后台运行,并等待网络请求的到来。


1.3  RequestQueue.add()

既然上面已经迫不及待等待网络请求的到来了,那么是时候将我们的Request添加进队列了。


public <T> Request<T> add(Request<T> request) {  
    // Tag the request as belonging to this queue and add it to the set of current requests.  
    request.setRequestQueue(this);  
    synchronized (mCurrentRequests) {  
        mCurrentRequests.add(request);  
    }  
    // Process requests in the order they are added.  
    request.setSequence(getSequenceNumber());  
    request.addMarker("add-to-queue");  
    // If the request is uncacheable, skip the cache queue and go straight to the network.  
    if (!request.shouldCache()) {  
        mNetworkQueue.add(request);  
        return request;  
    }  
    // Insert request into stage if there&#39;s already a request with the same cache key in flight.  
    synchronized (mWaitingRequests) {  
        String cacheKey = request.getCacheKey();  
        if (mWaitingRequests.containsKey(cacheKey)) {  
            // There is already a request in flight. Queue up.  
            Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);  
            if (stagedRequests == null) {  
                stagedRequests = new LinkedList<Request<?>>();  
            }  
            stagedRequests.add(request);  
            mWaitingRequests.put(cacheKey, stagedRequests);  
            if (VolleyLog.DEBUG) {  
                VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);  
            }  
        } else {  
            // Insert &#39;null&#39; queue for this cacheKey, indicating there is now a request in  
            // flight.  
            mWaitingRequests.put(cacheKey, null);  
            mCacheQueue.add(request);  
        }  
        return request;  
    }  
}


在第11行的时候会判断当前的请求是否可以缓存,默认情况下是可以缓存的,除非主动调用了RequestsetShouldCache(false)方法来不允许其进行缓存。因此默认情况下会将该请求加入到缓存队列否则直接加入网络请求队列。下面看看缓存线程中的run方法。


1.4  CacheDispatcher中的run()


public void run() {  
        if (DEBUG) VolleyLog.v("start new dispatcher");  
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  
        // Make a blocking call to initialize the cache.  
        mCache.initialize();  
        while (true) {  
            try {  
                // Get a request from the cache triage queue, blocking until  
                // at least one is available.  
                final Request<?> request = mCacheQueue.take();  
                request.addMarker("cache-queue-take");  
                // If the request has been canceled, don&#39;t bother dispatching it.  
                if (request.isCanceled()) {  
                    request.finish("cache-discard-canceled");  
                    continue;  
                }  
                // Attempt to retrieve this item from cache.  
                Cache.Entry entry = mCache.get(request.getCacheKey());  
                if (entry == null) {  
                    request.addMarker("cache-miss");  
                    // Cache miss; send off to the network dispatcher.  
                    mNetworkQueue.put(request);  
                    continue;  
                }  
                // If it is completely expired, just send it to the network.  
                if (entry.isExpired()) {  
                    request.addMarker("cache-hit-expired");  
                    request.setCacheEntry(entry);  
                    mNetworkQueue.put(request);  
                    continue;  
                }  
                // We have a cache hit; parse its data for delivery back to the request.  
                request.addMarker("cache-hit");  
                Response<?> response = request.parseNetworkResponse(  
                        new NetworkResponse(entry.data, entry.responseHeaders));  
                request.addMarker("cache-hit-parsed");  
                if (!entry.refreshNeeded()) {  
                    // Completely unexpired cache hit. Just deliver the response.  
                    mDelivery.postResponse(request, response);  
                } else {  
                    // Soft-expired cache hit. We can deliver the cached response,  
                    // but we need to also send the request to the network for  
                    // refreshing.  
                    request.addMarker("cache-hit-refresh-needed");  
                    request.setCacheEntry(entry);  
                    // Mark the response as intermediate.  
                    response.intermediate = true;  
                    // Post the intermediate response back to the user and have  
                    // the delivery then forward the request along to the network.  
                    mDelivery.postResponse(request, response, new Runnable() {  
                        @Override  
                        public void run() {  
                            try {  
                                mNetworkQueue.put(request);  
                            } catch (InterruptedException e) {  
                                // Not much we can do about this.  
                            }  
                        }  
                    });  
                }  
            } catch (InterruptedException e) {  
                // We may have been interrupted because it was time to quit.  
                if (mQuit) {  
                    return;  
                }  
                continue;  
            }  
        }  
}


可以看到while(true)的循环说明缓存线程始终是在运行,接着会尝试从缓存当中取出响应结果,如何为空则把这条请求加入到网络请求队列中,否则判断该缓存是否已过期,如果已经过期那么肯定还是同上处理,没有过期就直接使用缓存中的数据

之后就是和网络请求队列请求到数据后一样的数据解析逻辑以及解析结果回调逻辑。


1.5  NetworkDispatcher中的run()

接下来就是分析正常情况下,没有缓存的情况下,网络请求是什么样子的。直接看网络请求线程中的run()

public void run() {  
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  
        Request<?> request;  
        while (true) {  
            try {  
                // Take a request from the queue.  
                request = mQueue.take();  
            } catch (InterruptedException e) {  
                // We may have been interrupted because it was time to quit.  
                if (mQuit) {  
                    return;  
                }  
                continue;  
            }  
            try {  
                request.addMarker("network-queue-take");  
                // If the request was cancelled already, do not perform the  
                // network request.  
                if (request.isCanceled()) {  
                    request.finish("network-discard-cancelled");  
                    continue;  
                }  
                addTrafficStatsTag(request);  
                // Perform the network request.  
                NetworkResponse networkResponse = mNetwork.performRequest(request);  
                request.addMarker("network-http-complete");  
                // If the server returned 304 AND we delivered a response already,  
                // we&#39;re done -- don&#39;t deliver a second identical response.  
                if (networkResponse.notModified && request.hasHadResponseDelivered()) {  
                    request.finish("not-modified");  
                    continue;  
                }  
                // Parse the response here on the worker thread.  
                Response<?> response = request.parseNetworkResponse(networkResponse);  
                request.addMarker("network-parse-complete");  
                // Write to cache if applicable.  
                // TODO: Only update cache metadata instead of entire record for 304s.  
                if (request.shouldCache() && response.cacheEntry != null) {  
                    mCache.put(request.getCacheKey(), response.cacheEntry);  
                    request.addMarker("network-cache-written");  
                }  
                // Post the response back.  
                request.markDelivered();  
                mDelivery.postResponse(request, response);  
            } catch (VolleyError volleyError) {  
                parseAndDeliverNetworkError(request, volleyError);  
            } catch (Exception e) {  
                VolleyLog.e(e, "Unhandled exception %s", e.toString());  
                mDelivery.postError(request, new VolleyError(e));  
            }  
        }  
    }  
}

while(true)循环说明网络请求线程也是在不断运行的。在第25行可以看到调用了NetworkperformRequest()方法发送网络请求,而Network是一个接口,这里具体的实现是BasicNetwork,我们来看下它的performRequest()方法,如下所示:

public NetworkResponse performRequest(Request<?> request) throws VolleyError {  
        long requestStart = SystemClock.elapsedRealtime();  
        while (true) {  
            HttpResponse httpResponse = null;  
            byte[] responseContents = null;  
            Map<String, String> responseHeaders = new HashMap<String, String>();  
            try {  
                // Gather headers.  
                Map<String, String> headers = new HashMap<String, String>();  
                addCacheHeaders(headers, request.getCacheEntry());  
                httpResponse = mHttpStack.performRequest(request, headers);  
                StatusLine statusLine = httpResponse.getStatusLine();  
                int statusCode = statusLine.getStatusCode();  
                responseHeaders = convertHeaders(httpResponse.getAllHeaders());  
                // Handle cache validation.  
                if (statusCode == HttpStatus.SC_NOT_MODIFIED) {  
                    return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,  
                            request.getCacheEntry() == null ? null : request.getCacheEntry().data,  
                            responseHeaders, true);  
                }  
                // Some responses such as 204s do not have content.  We must check.  
                if (httpResponse.getEntity() != null) {  
                  responseContents = entityToBytes(httpResponse.getEntity());  
                } else {  
                  // Add 0 byte response as a way of honestly representing a  
                  // no-content request.  
                  responseContents = new byte[0];  
                }  
                // if the request is slow, log it.  
                long requestLifetime = SystemClock.elapsedRealtime() - requestStart;  
                logSlowRequests(requestLifetime, request, responseContents, statusLine);  
                if (statusCode < 200 || statusCode > 299) {  
                    throw new IOException();  
                }  
                return new NetworkResponse(statusCode, responseContents, responseHeaders, false);  
            } catch (Exception e) {  
                ……  
            }  
        }  
}

里面除去一些网络请求的细节,看到在第11行调用了HttpStackperformRequest()方法,这里的HttpStack就是在一开始调用newRequestQueue()方法是创建的实例,之后会将服务器返回的数据组装成一个NetworkResponse对象进行返回。

收到了NetworkResponse这个返回值后会调用RequestparseNetworkResponse()方法来解析NetworkResponse中的数据,以及将数据写入到缓存,这个方法的实现是交给Request的子类来完成的,因为不同种类的Request解析的方式也肯定不同。在解析完了NetworkResponse中的数据之后,又会调用ExecutorDeliverypostResponse()方法来回调解析出的数据,代码如下所示:

public ExecutorDelivery(final Handler handler) {
// Make an Executor that just wraps the handler.
mResponsePoster = new Executor() {
@Override
public void execute(Runnable command) {
handler.post(command);
 }
};
}
# postResponse方法
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {  
    request.markDelivered();  
    request.addMarker("post-response");  
    mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}


在该类的构造函数中传入了一个主线程创建的handler,最后一行代码在mResponsePosterexecute()方法中传入一个ResponseDeliveryRunnable对象,这样就可以通过handler.postrunnable切换到了主线程。我们继续看下这个Runnable中的run()方法中的代码是什么样的:

private class ResponseDeliveryRunnable implements Runnable {  
    private final Request mRequest;  
    private final Response mResponse;  
    private final Runnable mRunnable;  
  
    public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {  
        mRequest = request;  
        mResponse = response;  
        mRunnable = runnable;  
    }  
  
    @SuppressWarnings("unchecked")  
    @Override  
    public void run() {  
        // If this request has canceled, finish it and don&#39;t deliver.  
        if (mRequest.isCanceled()) {  
            mRequest.finish("canceled-at-delivery");  
            return;  
        }  
        // Deliver a normal response or error, depending.  
        if (mResponse.isSuccess()) {  
            mRequest.deliverResponse(mResponse.result);  
        } else {  
            mRequest.deliverError(mResponse.error);  
        }  
        // If this is an intermediate response, add a marker, otherwise we&#39;re done  
        // and the request can be finished.  
        if (mResponse.intermediate) {  
            mRequest.addMarker("intermediate-response");  
        } else {  
            mRequest.finish("done");  
        }  
        // If we have been provided a post-delivery runnable, run it.  
        if (mRunnable != null) {  
            mRunnable.run();  
        }  
   }  
}

其中在第22行调用了RequestdeliverResponse()方法,这个和parseNetworkResponse()方法一样都是我们在自定义Request时需要重写的方法,每一条网络请求的响应都是回调到这个方法中,再在这个方法中将响应的数据回调到ListeneronResponse()方法中,即我们创建Request时传入的那个listener。



Das obige ist der detaillierte Inhalt vonAndroid-Entwicklung – detaillierte Analyse des Volley-Quellcodes. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn