안 드 로 이 드 볼 리 라 이브 러 리 의 이미지 로드 기능 을 깊이 분석 하 다.

21963 단어 AndroidVolley
1.기본 사용 요점 회고
Volley 프레임 워 크 는 네트워크 그림 을 요청 하 는 데 도 많은 작업 을 했 고 여러 가지 방법 을 제공 했다.본 고 는 ImageLoader 를 사용 하여 네트워크 그림 을 불 러 오 는 것 을 소개 한다.
ImageLoader 의 내 부 는 ImageRequest 를 사용 하여 이 루어 집 니 다.구조 기 는 ImageCache 캐 시 형 삼 을 전송 하여 이미지 캐 시 기능 을 실현 할 수 있 으 며 중복 링크 를 걸 러 서 중복 전송 요청 을 피 할 수 있 습 니 다.
다음은 ImageLoader 에서 그림 을 불 러 오 는 실현 방법 입 니 다.

public void displayImg(View view){ 
 ImageView imageView = (ImageView)this.findViewById(R.id.image_view); 
 RequestQueue mQueue = Volley.newRequestQueue(getApplicationContext()); 
   
 ImageLoader imageLoader = new ImageLoader(mQueue, new BitmapCache()); 
 
 ImageListener listener = ImageLoader.getImageListener(imageView,R.drawable.default_image, R.drawable.default_image); 
 imageLoader.get("http://developer.android.com/images/home/aw_dac.png", listener); 
 //               
 //imageLoader.get("http://developer.android.com/images/home/aw_dac.png",listener, 200, 200); 
} 

ImageLoader.getImageListener()방법 으로 ImageListener 인 스 턴 스 를 만 든 후,imageLoader.get()방법 에 이 모니터 와 그림 의 url 을 추가 하면 네트워크 그림 을 불 러 올 수 있 습 니 다.
다음은 LruCache 로 구현 되 는 캐 시 클래스 입 니 다.

public class BitmapCache implements ImageCache { 
 
 private LruCache<String, Bitmap> cache; 
 
 public BitmapCache() { 
  cache = new LruCache<String, Bitmap>(8 * 1024 * 1024) { 
   @Override 
   protected int sizeOf(String key, Bitmap bitmap) { 
    return bitmap.getRowBytes() * bitmap.getHeight(); 
   } 
  }; 
 } 
 
 @Override 
 public Bitmap getBitmap(String url) { 
  return cache.get(url); 
 } 
 
 @Override 
 public void putBitmap(String url, Bitmap bitmap) { 
  cache.put(url, bitmap); 
 } 
} 

마지막 으로 AndroidManifest.xml 파일 에 네트워크 에 접근 할 수 있 는 권한 을 추가 하 는 것 을 잊 지 마 세 요.

<uses-permission android:name="android.permission.INTERNET"/> 

2.소스 코드 분석
(1)Volley 요청 대기 열 초기 화

mReqQueue = Volley.newRequestQueue(mCtx);

주로 이 줄 입 니 다.

#Volley

public static RequestQueue newRequestQueue(Context context) {
  return newRequestQueue(context, null);
 }

public static RequestQueue newRequestQueue(Context context, HttpStack stack)
 {
  return newRequestQueue(context, stack, -1);
 }
public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
  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 {
    // Prior to Gingerbread, HttpUrlConnection was unreliable.
    // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
    stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
   }
  }

  Network network = new BasicNetwork(stack);

  RequestQueue queue;
  if (maxDiskCacheBytes <= -1)
  {
   // No maximum size specified
   queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
  }
  else
  {
   // Disk cache size specified
   queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
  }

  queue.start();

  return queue;
 }

여 기 는 주로 HttpStack 을 초기 화 하 는 것 입 니 다.HttpStack 은 API 가 9 보다 클 때 HttpUrl Connetcion 을 선택 하고 반대로 HttpClient 를 선택 합 니 다.여 기 는 Http 관련 코드 에 관심 이 없습니다.
이 어 RequestQueue 를 초기 화하 고 start()방법 을 호출 했다.
다음은 RequestQueue 의 구 조 를 살 펴 보 겠 습 니 다.

public RequestQueue(Cache cache, Network network) {
  this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
 }
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
  this(cache, network, threadPoolSize,
    new ExecutorDelivery(new Handler(Looper.getMainLooper())));
 }
public RequestQueue(Cache cache, Network network, int threadPoolSize,
   ResponseDelivery delivery) {
  mCache = cache;
  mNetwork = network;
  mDispatchers = new NetworkDispatcher[threadPoolSize];
  mDelivery = delivery;
 }

초기 화 는 주로 4 개의 매개 변수:mCache,mNetwork,mDispatchers,mDelivery 입 니 다.첫 번 째 는 하 드 디스크 캐 시 입 니 다.두 번 째 는 Http 관련 작업 에 사 용 됩 니 다.세 번 째 는 퍼 가기 요청 에 사용 되 는 것 입 니 다.네 번 째 매개 변 수 는 결 과 를 UI 스 레 드 로 전송 하 는 데 사 용 됩 니 다(ps:new Handler(Looper.getMainLooper())를 볼 수 있 습 니 다.
이제 start 방법 을 보 겠 습 니 다.

#RequestQueue
 /**
  * Starts the dispatchers in this queue.
  */
 public void start() {
  stop(); // Make sure any currently running dispatchers are stopped.
  // Create the cache dispatcher and start it.
  mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
  mCacheDispatcher.start();

  // Create network dispatchers (and corresponding threads) up to the pool size.
  for (int i = 0; i < mDispatchers.length; i++) {
   NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
     mCache, mDelivery);
   mDispatchers[i] = networkDispatcher;
   networkDispatcher.start();
  }
 }

우선 stop 입 니 다.전송 기 가 종료 되 었 는 지 확인 하 십시오.사실은 내부 의 몇 개의 스 레 드 가 종료 되 었 습 니 다.여기 서 관심 이 있 으 면 소스 코드 를 볼 수 있 습 니 다.Volley 에서 스 레 드 가 종료 되 었 는 지 참고 하 십시오(몇 개의 스 레 드 는 while(true){/doSomething}).
다음은 CacheDispatcher 를 초기 화하 고 start()를 호출 합 니 다.NetworkDispatcher 를 초기 화하 고 start()를 호출 합 니 다.
위의 트랜스 퍼 는 모두 스 레 드 입 니 다.볼 수 있 습 니 다.여기 몇 개의 스 레 드 가 우 리 를 도와 일 을 하고 있 습 니 다.구체 적 인 소스 코드 는 우리 가 잠시 후에 보고 있 습 니 다.
자,여기까지 볼 리 의 초기 화 에 관 한 코드 를 완 료 했 습 니 다.다음은 ImageLoader 관련 소스 코드 를 초기 화 하 는 것 을 보 겠 습 니 다.
(2)ImageLoader 초기 화

#VolleyHelper
mImageLoader = new ImageLoader(mReqQueue, new ImageCache()
  {
   private final LruCache<String, Bitmap> mLruCache = new LruCache<String, Bitmap>(
     (int) (Runtime.getRuntime().maxMemory() / 10))
   {
    @Override
    protected int sizeOf(String key, Bitmap value)
    {
     return value.getRowBytes() * value.getHeight();
    }
   };

   @Override
   public void putBitmap(String url, Bitmap bitmap)
   {
    mLruCache.put(url, bitmap);
   }

   @Override
   public Bitmap getBitmap(String url)
   {
    return mLruCache.get(url);
   }
  });

#ImageLoader

public ImageLoader(RequestQueue queue, ImageCache imageCache) {
  mRequestQueue = queue;
  mCache = imageCache;
 }

간단 합 니 다.우리 가 초기 화 한 RequestQueue 와 LruCache 에 따라 ImageLoader 를 초기 화 했 습 니 다.
(3)그림 불 러 오기
우리 가 그림 을 불 러 올 때 호출 하 는 것 은:

 # VolleyHelper
 getInstance().getImageLoader().get(url, new ImageLoader.ImageListener());

다음은 get 방법:

#ImageLoader
 public ImageContainer get(String requestUrl, final ImageListener listener) {
  return get(requestUrl, listener, 0, 0);
 }
public ImageContainer get(String requestUrl, ImageListener imageListener,
   int maxWidth, int maxHeight) {
  return get(requestUrl, imageListener, maxWidth, maxHeight, ScaleType.CENTER_INSIDE);
 }
public ImageContainer get(String requestUrl, ImageListener imageListener,
   int maxWidth, int maxHeight, ScaleType scaleType) {

  // only fulfill requests that were initiated from the main thread.
  throwIfNotOnMainThread();

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

  // 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<Bitmap> newRequest = makeImageRequest(requestUrl, maxWidth, maxHeight, scaleType,
    cacheKey);

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

get 방법 을 볼 수 있 습 니 다.먼저 throw IfNotOnMainThread()방법 을 통 해 UI 스 레 드 에서 호출 해 야 합 니 다.
그리고 들 어 오 는 매개 변수 에 따라 cacheKey 를 계산 하여 cache 를 가 져 옵 니 다.
=>cache 가 존재 하면 되 돌아 오 는 결 과 를 ImageContainer(cached Bitmap,requestUrl)로 봉 한 다음 imageListener.onResponse(container,true)로 직접 되 돌려 줍 니 다.우 리 는 그림 을 설정 할 수 있다.
=>cache 가 존재 하지 않 는 다 면 ImageContainer(bitmap 없 음)를 초기 화하 고,imageListener.onResponse(imageContainer,true)를 직접 리 셋 합 니 다.리 셋 에서 판단 할 수 있 도록 기본 그림 을 설정 합 니 다(그 러 니 여러분 이 listener 를 실현 할 때 resp.getBitmap()판단 하 는 것 을 잊 지 마 세 요!=null);
다음은 이 url 이 이미 요청 에 가 입 했 는 지 확인 합 니 다.이미 가입 했다 면,초기 화 된 ImageContainer 를 BatchedImageRequest 에 가입 하고 끝 을 되 돌려 줍 니 다.
새로운 요청 이 라면 MakeImageRequest 를 통 해 새로운 요청 을 만 든 다음 이 요청 을 각각 mRequestQueue 와 mInFlightRequests 에 추가 합 니 다.mInFlightRequests 에서 BatchedImageRequest 를 초기 화하 여 같은 요청 대기 열 을 저장 합 니 다.
여기 서 mRequestQueue 는 대상 이지 대기 열 데이터 구조 가 아니 므 로 add 방법 을 보 겠 습 니 다.

#RequestQueue
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'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 'null' queue for this cacheKey, indicating there is now a request in
    // flight.
    mWaitingRequests.put(cacheKey, null);
    mCacheQueue.add(request);
   }
   return request;
  }
 }

우선 mCurrent Requests 가입 을 요청 합 니 다.이 mCurrent Requests 는 cancel 의 입 구 를 제공 하기 위해 처리 해 야 할 모든 Request 를 저장 합 니 다.
이 요청 이 캐 시 되 지 않 으 면 mNetwork Queue 에 직접 가입 하고 돌아 갑 니 다.
그리고 이 요청 이 같은 요청 이 처리 되 고 있 는 지 판단 하고 있 으 면 mWaiting Requests 에 가입 합 니 다.하면,만약,만약...
mWaiting Requests.put(cacheKey,null)와 mCacheQueue.add(request)를 추가 합 니 다.
ok,여기 서 우 리 는 직관 적 인 코드 를 분석 하여 완 성 했 습 니 다.그러나 당신 은 도대체 어디에서 네트워크 요청 을 촉발 하고 그림 을 불 러 오 는 것 이 라 고 생각 할 수 있 습 니까?
그러면 먼저 우리 가 그림 을 불 러 올 때 MakeImageRequest 를 한 다음 에 이 요청 을 여러 대기 열 에 추가 합 니 다.주로 mCurrent Requests,mCacheQueue 를 포함 합 니 다.
그리고 우리 가 RequestQueue 를 초기 화 할 때 몇 개의 퍼 가기 스 레 드 를 시 작 했 는 지 기억 하 십 니까?CacheDispatcher 와 NetworkDispatcher.
사실은 네트워크 요청 은 이 몇 개의 스 레 드 에서 진정 으로 불 러 오 는 것 입 니 다.우 리 는 각각 보 겠 습 니 다.
(4)CacheDispatcher
구조 방법 보기;

#CacheDispatcher
 public CacheDispatcher(
   BlockingQueue<Request<?>> cacheQueue, BlockingQueue<Request<?>> networkQueue,
   Cache cache, ResponseDelivery delivery) {
  mCacheQueue = cacheQueue;
  mNetworkQueue = networkQueue;
  mCache = cache;
  mDelivery = delivery;
 }
이것 은 스 레 드 입 니 다.그러면 주요 코드 는 run 안에 있 을 것 입 니 다.

#CacheDispatcher

 @Override
 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'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;
   }
  }
 }

ok,우선 이 캐 시 는 하 드 디스크 캐 시(디 렉 터 리 는 context.getCacheDir()/volley)를 말 합 니 다.메모리 캐 시 는 ImageLoader 에서 이미 판단 되 었 습 니 다.
여 기 는 무한 순환 입 니 다.mCacheQueue 에서 요청 을 계속 꺼 내 고 요청 이 취소 되면 바로 끝 납 니 다.
다음 캐 시 에서 가 져 오기:
=>찾 지 못 하면 mNetworkQueue 가입
=>캐 시가 만 료 되면 mNetworkQueue 가입
그렇지 않 으 면 사용 가능 한 캐 시 를 찾 은 것 입 니 다.request.parseNetworkResponse 를 호출 하여 캐 시 에서 꺼 낸 data 와 responseHeaders 를 분석 합 니 다.다음은 TTL(주로 만 료 여 부 를 판단 합 니 다)을 판단 하고 만 료 되 지 않 으 면 mDelivery.post Response 를 통 해 직접 전달 한 다음 UI 스 레 드 로 되 돌려 줍 니 다.ttl 이 합 법 적 이지 않 으 면 리 셋 이 완료 되면 이 요청 을 mNetworkQueue 에 추가 합 니 다.
자,여 기 는 합 법 적 인 캐 시 를 받 으 면 UI 스 레 드 로 직접 전송 하 는 것 입 니 다.반대로 NetworkQueue 에 가입 합 니 다.
다음은 네트워크 디 스 패 치 를 보 겠 습 니 다.
(5)NetworkDispatcher
CacheDispatcher 와 유사 하고 스 레 드 이 며 핵심 코드 는 run 에 있 습 니 다.

# NetworkDispatcher
//new NetworkDispatcher(mNetworkQueue, mNetwork,mCache, mDelivery)

public NetworkDispatcher(BlockingQueue<Request<?>> queue,
   Network network, Cache cache,
   ResponseDelivery delivery) {
  mQueue = queue;
  mNetwork = network;
  mCache = cache;
  mDelivery = delivery;
 }
@Override
 public void run() {
  Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
  while (true) {
   long startTimeMs = SystemClock.elapsedRealtime();
   Request<?> request;
   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're done -- don'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) {
    volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
    parseAndDeliverNetworkError(request, volleyError);
   } catch (Exception e) {
    VolleyLog.e(e, "Unhandled exception %s", e.toString());
    VolleyError volleyError = new VolleyError(e);
    volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
    mDelivery.postError(request, volleyError);
   }
  }
 }

코드 를 보기 전에 우 리 는 먼저 논 리 를 생각해 보 겠 습 니 다.정상 적 인 상황 에서 우 리 는 요청 을 꺼 내 network 로 하여 금 우리 의 요청 을 처리 하 게 할 것 입 니 다.처리 가 끝 난 후에 캐 시 를 넣 고 퍼 가기 할 것 입 니 다.
그럼 옳 고 그 름 을 봅 시다.
먼저 요청 꺼 내기;그리고 mNetwork.performRequest(request)를 통 해 우리 의 요청 을 처리 하고 NetworkResponse 를 받 습 니 다.다음은 request 를 사용 하여 우리 의 NetworkResponse 를 분석 합 니 다.
Response 를 받 은 후 캐 시 여 부 를 판단 하고 필요 하 다 면 캐 시 합 니 다.
마지막 mDelivery.postResponse(요청,응답);전달 하 다
ok,우리 의 기대 와 차이 가 많 지 않 습 니 다.
이렇게 되면 우리 볼 리 는 그림 을 불 러 오 는 핵심 논 리 를 분석 하고 간단하게 요약 한다.
먼저 RequestQueue 를 초기 화 합 니 다.주로 Dispatcher 스 레 드 를 몇 개 켜 는 것 입 니 다.스 레 드 는 요청 을 계속 읽 습 니 다(사용 하 는 차단 대기 열,메시지 가 없 으 면 차단)
우리 가 요청 을 한 후에 url,ImageView 속성 등에 따라 cacheCey 를 구성 한 다음 에 먼저 LruCache 에서 가 져 옵 니 다(이 캐 시 는 우리 가 구축 한 것 으로 ImageCache 인 터 페 이 스 를 실현 하 는 것 은 모두 합 법 적 입 니 다).가 져 오지 않 으 면 하 드 디스크 캐 시가 있 는 지 판단 합 니 다.이 단 계 는 getCacheDir 에서 가 져 옵 니 다(기본 5M).찾 지 못 하면 네트워크 에서 요청 합 니 다.
그러나 볼 리 의 그림 로드 를 발견 할 수 있 습 니 다.LIFO 라 는 전략 은 없습니다.그림 을 다운로드 하 는 것 도 메모리 에 완전히 추가 한 다음 에 압축 하 는 것 같 습 니 다.이렇게 보면 큰 그림,큰 파일 같은 것 은 폐 기 됩 니 다.
보기 에는 간단 해 보이 지만 보고 나 면 어떻게 하면 더 좋 을 때 이 라 이브 러 리 와 이미지 로 딩 라 이브 러 리 를 디자인 하 는 지 에 큰 도움 이 됩 니 다.
만약 에 관심 이 있다 면 여러분 은 소스 코드 분석 을 보 는 동시에 특정한 세부 적 인 실현 도 생각해 보 실 수 있 습 니 다.예 를 들 어:
Dispatcher 는 무한 순환 스 레 드 로 볼 리 가 어떻게 닫 혔 는 지 확인 할 수 있 습 니 다.
그림 압축 코드 는 ImageRequest 의 parseNetworkResponse 에서 어떻게 압축 되 었 는 지 볼 수 있 습 니 다.
so on…
마지막 으로 대략적인 흐름 도 를 붙 여서 기억 하기 편 하 게 한다.
201648142342794.jpg (1062×747)

좋은 웹페이지 즐겨찾기