Volley 소스 해석 (2)

29554 단어 원본 코드Volley

그림 로드


Volley 그림을 사용해서 불러온 오래된 관리자들은 이 코드에 대해 매우 잘 알고 있을 것이다.
imageLoader.get( url,
                ImageLoader.getImageListener(iv, R.mipmap.aio_image_default, R.mipmap.aio_image_fail))

그림 주소, 표시할 ImageView를 ImageLoader에 보내면 자동으로 불러올 수 있습니다. 도대체 그가 어떻게 실현한 것입니까?우리 함께 원본 코드에 가 봅시다.

ImageLoader


1.ImageListener
ImageListener는 이미지 로드 결과의 콜백이며 그 자체는 인터페이스입니다.
    public interface ImageListener extends ErrorListener {

        public void onResponse(ImageContainer response, boolean isImmediate);

    }

    public interface ErrorListener {
        void onErrorResponse(VolleyError var1);
    }

그 안에는 두 가지 실현되지 못한 방법이 있는데, 각각 성공과 실패의 반전이다.이것은 이해하기 쉽다. 다음은 그것을 어떻게 얻는지 보자.
    public static ImageListener getImageListener(final ImageView view,
            final int defaultImageResId, final int errorImageResId) {
        return new ImageListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                if (errorImageResId != 0) {
                    view.setImageResource(errorImageResId);
                }
            }

            @Override
            public void onResponse(ImageContainer response, boolean isImmediate) {
                if (response.getBitmap() != null) {
                    view.setImageBitmap(response.getBitmap());
                } else if (defaultImageResId != 0) {
                    view.setImageResource(defaultImageResId);
                }
            }
        };
    }

내부 클래스를 직접 사용해서 그것을 실현시켰고 되돌아왔다. 외부에서 이 방법을 찾았을 때 ImageListener 대상을 얻었다는 것만 알면 된다.
2. 비동기식 실행
  public ImageContainer get(String requestUrl, ImageListener imageListener,
            int maxWidth, int maxHeight) {
        // only fulfill requests that were initiated from the main thread.
        throwIfNotOnMainThread();

        final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight);

        // Try to look up the request in the cache of remote images.
        Bitmap cachedBitmap = mCache.getBitmap(cacheKey);
        if (cachedBitmap != null) {
            // Return the cached bitmap.
            ImageContainer container = new ImageContainer(cachedBitmap, requestUrl, null, null);
            imageListener.onResponse(container, true);
            return container;
        }

        // The bitmap did not exist in the cache, fetch it!
        ImageContainer imageContainer =
                new ImageContainer(null, requestUrl, cacheKey, imageListener);

        // Update the caller to let them know that they should use the default bitmap.
        imageListener.onResponse(imageContainer, true);

        // Check to see if a request is already in-flight.
        BatchedImageRequest request = mInFlightRequests.get(cacheKey);
        if (request != null) {
            // If it is, add this request to the list of listeners.
            request.addContainer(imageContainer);
            return imageContainer;
        }

        // The request is not already in flight. Send the new request to the network and
        // track it.
        Request<?> newRequest =
            new ImageRequest(requestUrl, new Listener<Bitmap>() {
                @Override
                public void onResponse(Bitmap response) {
                    onGetImageSuccess(cacheKey, response);
                }
            }, maxWidth, maxHeight,
            Config.RGB_565, new ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    onGetImageError(cacheKey, error);
                }
            });

        mRequestQueue.add(newRequest);
        mInFlightRequests.put(cacheKey,
                new BatchedImageRequest(newRequest, imageContainer));
        return imageContainer;
    }

이 코드는 좀 길어, 우리 한 걸음 한 걸음 보자.우선 주 라인에서 이 방법을 사용해야 한다.두 번째 단계는 그림의 URL, 길이, 너비에 따라 캐시에 있는 키를 가져옵니다. 캐시에 있으면 바로 되돌려줍니다.여기서 주의해야 할 점은 이 캐시 대상이다. Volley는 우리가 실현하는 것을 도와주지 않고 인터페이스이다.
    public interface ImageCache {
        public Bitmap getBitmap(String url);
        public void putBitmap(String url, Bitmap bitmap);
    }

Collections의 정렬 방법과 마찬가지로 정책 설계 모드를 사용하여 정렬 방식을 사용자 정의할 수 있습니다.
 public static <T> void sort(List<T> list, Comparator<? super T> c) {
        Object[] a = list.toArray();
        Arrays.sort(a, (Comparator)c);
        ListIterator i = list.listIterator();
        for (int j=0; j<a.length; j++) {
            i.next();
            i.set(a[j]);
        }
    }

여기서도 사용자 정의 캐시의 구체적인 실현을 할 수 있다.그러면 세 번째 단계는 캐시에 데이터가 없으면 ImageContainer 대상을 구축하고 이미지 리스트 대상으로onResponse 방법을 실행하여 외부에서 기본적인 그림으로 먼저 표시하도록 한다.4단계에서는 mInFlightRequests 객체에서 실행 중인BatchedImageRequest를 가져옵니다.BatchedImageRequest 객체가 패키지되어 있습니다.
    /** The request being tracked */
        private final Request<?> mRequest;

        /** The result of the request being tracked by this item */
        private Bitmap mResponseBitmap;

        /** Error if one occurred for this response */
        private VolleyError mError;

        /** List of all of the active ImageContainers that are interested in the request */
        private final LinkedList<ImageContainer> mContainers = new LinkedList<ImageContainer>();

아주 간단해서 상세하게 설명할 필요가 없다.mInFlightRequests라는 대상은 이전 네트워크 요청의 mWaitingRequests 대상과 매우 비슷하지만 약간 다르다.만약에 인터넷 요청 원본에 대해 잘 모르면 제 앞의 Volley 원본 해석(一)을 보실 수 있습니다. 마찬가지로 그도 같은 요청을 여러 번 하는 것을 방지하기 위해서였지만 ImageListener를 ImageContainer 대상에 봉인했습니다.이 코드를 자세히 보려면 다음과 같이 하십시오.
     // The bitmap did not exist in the cache, fetch it!
        ImageContainer imageContainer =
                new ImageContainer(null, requestUrl, cacheKey, imageListener);

        // Update the caller to let them know that they should use the default bitmap.
        imageListener.onResponse(imageContainer, true);

        // Check to see if a request is already in-flight.
        BatchedImageRequest request = mInFlightRequests.get(cacheKey);
        if (request != null) {
            // If it is, add this request to the list of listeners.
            request.addContainer(imageContainer);
            return imageContainer;
        }

우리의 같은 요청 실행이 끝난 후에 각각의 ImageListener로 각각의 UI 요청 인터페이스에 나누어 주고 뒤에 있는 코드로 가면 알 수 있기 때문이다.다음 다섯 번째 단계는 캐시에 존재하지 않고 요청이 항공편(실행 중)이 아니라면 요청을 실행하러 가야 합니다.
    Request<?> newRequest =
            new ImageRequest(requestUrl, new Listener<Bitmap>() {
                @Override
                public void onResponse(Bitmap response) {
                    onGetImageSuccess(cacheKey, response);
                }
            }, maxWidth, maxHeight,
            Config.RGB_565, new ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    onGetImageError(cacheKey, error);
                }
            });

        mRequestQueue.add(newRequest);

ImageRequest를 사용했습니다. 요청 결과는 잠시 후에 보겠습니다.마지막 단계는 요청을 항공편 대기열에 올려 한 요청이 여러 번 네트워크를 요청하는 것을 방지하는 것이다.
        mInFlightRequests.put(cacheKey,
                new BatchedImageRequest(newRequest, imageContainer));

3. 요청 결과 처리 요청이 성공했거나 실패한 코드가 많지 않다. 실행이 성공했는지 무엇을 바꿨는지 살펴보자.
  private void onGetImageSuccess(String cacheKey, Bitmap response) {
        // cache the image that was fetched.
        mCache.putBitmap(cacheKey, response);

        // remove the request from the list of in-flight requests.
        BatchedImageRequest request = mInFlightRequests.remove(cacheKey);

        if (request != null) {
            // Update the response bitmap.
            request.mResponseBitmap = response;

            // Send the batched response
            batchResponse(cacheKey, request);
        }
    }

우선, 우리는 얻은 결과를 캐시에 넣은 다음, 비행기의 요청을 제거하여, 요청 실행이 끝났다는 것을 나타낸다.요청 결과를 BatchedImageRequest 대상에 넣습니다.포인트는batchResponse 방법입니다. 눌러보세요.
  private void batchResponse(String cacheKey, BatchedImageRequest request) {
        mBatchedResponses.put(cacheKey, request);
        // If we don't already have a batch delivery runnable in flight, make a new one.
        // Note that this will be used to deliver responses to all callers in mBatchedResponses.
        if (mRunnable == null) {
            mRunnable = new Runnable() {
                @Override
                public void run() {
                    for (BatchedImageRequest bir : mBatchedResponses.values()) {
                        for (ImageContainer container : bir.mContainers) {
                            // If one of the callers in the batched request canceled the request
                            // after the response was received but before it was delivered,
                            // skip them.
                            if (container.mListener == null) {
                                continue;
                            }
                            if (bir.getError() == null) {
                                container.mBitmap = bir.mResponseBitmap;
                                container.mListener.onResponse(container, false);
                            } else {
                                container.mListener.onErrorResponse(bir.getError());
                            }
                        }
                    }
                    mBatchedResponses.clear();
                    mRunnable = null;
                }

            };
            // Post the runnable.
            mHandler.postDelayed(mRunnable, mBatchResponseDelayMs);
        }
    }

여기까지, Batched Image Request의 결과를 각각 각각의 Image Listener로 실행하여 결과를 되돌려줍니다.

NetworkImageView


NetworkImageView는 ImageView를 상속하고 ImageLoader의 패키지로서 비교적 간단합니다.먼저 확인된 그림과 실패한 그림을 불러오기:
 /** * Sets the default image resource ID to be used for this view until the attempt to load it * completes. */
    public void setDefaultImageResId(int defaultImage) {
        mDefaultImageId = defaultImage;
    }

    /** * Sets the error image resource ID to be used for this view in the event that the image * requested fails to load. */
    public void setErrorImageResId(int errorImage) {
        mErrorImageId = errorImage;
    }

setImageUrl () 방법을 호출할 때 불러오기 시작합니다.
    public void setImageUrl(String url, ImageLoader imageLoader) {
        mUrl = url;
        mImageLoader = imageLoader;
        // The URL has potentially changed. See if we need to load it.
        loadImageIfNecessary(false);
    }

주요 코드는loadImageIfNecessary에 있습니다. 누르십시오
  private void loadImageIfNecessary(final boolean isInLayoutPass) {
        int width = getWidth();
        int height = getHeight();

        boolean isFullyWrapContent = getLayoutParams() != null
                && getLayoutParams().height == LayoutParams.WRAP_CONTENT
                && getLayoutParams().width == LayoutParams.WRAP_CONTENT;
        // if the view's bounds aren't known yet, and this is not a wrap-content/wrap-content
        // view, hold off on loading the image.
        if (width == 0 && height == 0 && !isFullyWrapContent) {
            return;
        }

        // if the URL to be loaded in this view is empty, cancel any old requests and clear the
        // currently loaded image.
        if (TextUtils.isEmpty(mUrl)) {
            if (mImageContainer != null) {
                mImageContainer.cancelRequest();
                mImageContainer = null;
            }
            setDefaultImageOrNull();
            return;
        }

        // if there was an old request in this view, check if it needs to be canceled.
        if (mImageContainer != null && mImageContainer.getRequestUrl() != null) {
            if (mImageContainer.getRequestUrl().equals(mUrl)) {
                // if the request is from the same URL, return.
                return;
            } else {
                // if there is a pre-existing request, cancel it if it's fetching a different URL.
                mImageContainer.cancelRequest();
                setDefaultImageOrNull();
            }
        }

        // The pre-existing content of this view didn't match the current URL. Load the new image
        // from the network.
        ImageContainer newContainer = mImageLoader.get(mUrl,
                new ImageListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        if (mErrorImageId != 0) {
                            setImageResource(mErrorImageId);
                        }
                    }

                    @Override
                    public void onResponse(final ImageContainer response, boolean isImmediate) {
                        // If this was an immediate response that was delivered inside of a layout
                        // pass do not set the image immediately as it will trigger a requestLayout
                        // inside of a layout. Instead, defer setting the image by posting back to
                        // the main thread.
                        if (isImmediate && isInLayoutPass) {
                            post(new Runnable() {
                                @Override
                                public void run() {
                                    onResponse(response, false);
                                }
                            });
                            return;
                        }

                        if (response.getBitmap() != null) {
                            setImageBitmap(response.getBitmap());
                        } else if (mDefaultImageId != 0) {
                            setImageResource(mDefaultImageId);
                        }
                    }
                });

        // update the ImageContainer to be the new bitmap container.
        mImageContainer = newContainer;
    }

우선, NetworkImageView의 길이를 얻지 못하면return을 하고 아무것도 하지 않습니다.그리고 전송된 URL이 비어 있으면 기본 그림을 보기에 놓으십시오.다음 코드도 캐시 정책입니다
     // if there was an old request in this view, check if it needs to be canceled.
        if (mImageContainer != null && mImageContainer.getRequestUrl() != null) {
            if (mImageContainer.getRequestUrl().equals(mUrl)) {
                // if the request is from the same URL, return.
                return;
            } else {
                // if there is a pre-existing request, cancel it if it's fetching a different URL. mImageContainer.cancelRequest(); setDefaultImageOrNull(); } }

우리 mImageContainer는 내부 유지보수의 대상입니다. 이전에 ImageLoader를 보았을 때 실행 결과, url, 캐시 키, ImageListener 대상을 봉인했습니다.그럼 ImageLoader를 호출했습니다.get () 메서드가 나타나면 ImageContainer 객체가 반환됩니다.자, 코드를 계속 보십시오. 만약 mImageContainer의 요청 URL이 전송된 URL과 같다면 되돌아오고 실행할 필요가 없습니다. 결과가 모두 같기 때문입니다.그렇지 않으면 현재 요청을 취소하고 기본 그림을 설정하고 다음 작업을 계속합니다. 생각할 필요도 없습니다. 그림을 불러오는 것이 틀림없습니다.
  // The pre-existing content of this view didn't match the current URL. Load the new image
        // from the network.
        ImageContainer newContainer = mImageLoader.get(mUrl,
                new ImageListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        if (mErrorImageId != 0) {
                            setImageResource(mErrorImageId);
                        }
                    }

                    @Override
                    public void onResponse(final ImageContainer response, boolean isImmediate) {
                        // If this was an immediate response that was delivered inside of a layout
                        // pass do not set the image immediately as it will trigger a requestLayout
                        // inside of a layout. Instead, defer setting the image by posting back to
                        // the main thread.
                        if (isImmediate && isInLayoutPass) {
                            post(new Runnable() {
                                @Override
                                public void run() {
                                    onResponse(response, false);
                                }
                            });
                            return;
                        }

                        if (response.getBitmap() != null) {
                            setImageBitmap(response.getBitmap());
                        } else if (mDefaultImageId != 0) {
                            setImageResource(mDefaultImageId);
                        }
                    }
                });

하지만 여기서 주목할 점은 바로 onResponse 내부의 방법이다.
   if (isImmediate && isInLayoutPass) {
                            post(new Runnable() {
                                @Override
                                public void run() {
                                    onResponse(response, false);
                                }
                            });
                            return;
                        }

이게 무슨 뜻이냐면, 사실은 레이아웃을 다시 그릴 때Network ImageView의onLayout () 방법을 호출할 때
   @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        loadImageIfNecessary(true);
    }

트루가 들어오면 isInLayoutPass는 트루입니다. 즉시 실행할 조건이 충족되면 그 방법을 실행합니다.생각도 간단합니까? 우리가 onDraw를 실행해야 그림을 설정할 수 있기 때문에 Post를 MQ 대기열에 넣고 그림을 다 그린 후에onResponse 방법을 호출합니다. 주의하십시오. 이때false가 들어오면 다음 코드를 실행합니다.이것은 우리의 관심을 필요로 하지 않는다. 일반적으로, 우리가 setImageUrl을 호출해서 전하는 것은false이다.

총결산


ImageLoader를 사용하여 요청을 비동기적으로 실행할 때 ImageContainer에게 되돌려줍니다. 이 ImageContainer를 사용할 때 비공식적인 판단을 해야 합니다. 내부 유지보수의 Bitmap은 빈 NetworkImageView를 사용할 수 있기 때문에 매우 네모나지만 대량의 목록이 실현되면 NetworkImageView를 사용하는 것을 추천하지 않고 대량의 메모리 공간을 차지합니다.

좋은 웹페이지 즐겨찾기