亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

深入剖析Android的Volley庫中的圖片加載功能

 更新時間:2016年04月08日 14:27:20   作者:鴻洋_  
這篇文章主要介紹了Android的Volley框架中的圖片加載功能,從源碼剖析了Volley加載圖片時的請求隊列處理等方面,需要的朋友可以參考下

一、基本使用要點回顧

Volley框架在請求網(wǎng)絡(luò)圖片方面也做了很多工作,提供了好幾種方法.本文介紹使用ImageLoader來進行網(wǎng)絡(luò)圖片的加載.
ImageLoader的內(nèi)部使用ImageRequest來實現(xiàn),它的構(gòu)造器可以傳入一個ImageCache緩存形參,實現(xiàn)了圖片緩存的功能,同時還可以過濾重復(fù)鏈接,避免重復(fù)發(fā)送請求。
下面是ImageLoader加載圖片的實現(xiàn)方法:

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()方法創(chuàng)建一個ImageListener實例后,在imageLoader.get()方法中加入此監(jiān)聽器和圖片的url,即可加載網(wǎng)絡(luò)圖片.

下面是使用LruCache實現(xiàn)的緩存類

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文件中加入訪問網(wǎng)絡(luò)的權(quán)限

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

二、源碼分析
(一) 初始化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的時候選擇HttpUrlConnetcion,反之則選擇HttpClient,這里我們并不關(guān)注Http相關(guān)代碼。

接下來初始化了RequestQueue,然后調(diào)用了start()方法。

接下來看RequestQueue的構(gòu)造:

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個參數(shù):mCache、mNetwork、mDispatchers、mDelivery。第一個是硬盤緩存;第二個主要用于Http相關(guān)操作;第三個用于轉(zhuǎn)發(fā)請求的;第四個參數(shù)用于把結(jié)果轉(zhuǎn)發(fā)到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,確保轉(zhuǎn)發(fā)器退出,其實就是內(nèi)部的幾個線程退出,這里大家如果有興趣可以看眼源碼,參考下Volley中是怎么處理線程退出的(幾個線程都是while(true){//doSomething})。

接下來初始化CacheDispatcher,然后調(diào)用start();初始化NetworkDispatcher,然后調(diào)用start();

上面的轉(zhuǎn)發(fā)器呢,都是線程,可以看到,這里開了幾個線程在幫助我們工作,具體的源碼,我們一會在看。

好了,到這里,就完成了Volley的初始化的相關(guān)代碼,那么接下來看初始化ImageLoader相關(guān)源碼。

(二) 初始化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;
 }

很簡單,就是根據(jù)我們初始化的RequestQueue和LruCache初始化了一個ImageLoader。

(三) 加載圖片

我們在加載圖片時,調(diào)用的是:

 # 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方法,首先通過throwIfNotOnMainThread()方法限制必須在UI線程調(diào)用;

然后根據(jù)傳入的參數(shù)計算cacheKey,獲取cache;

=>如果cache存在,直接將返回結(jié)果封裝為一個ImageContainer(cachedBitmap, requestUrl),然后直接回調(diào)imageListener.onResponse(container, true);我們就可以設(shè)置圖片了。

=>如果cache不存在,初始化一個ImageContainer(沒有bitmap),然后直接回調(diào),imageListener.onResponse(imageContainer, true);,這里為了讓大家在回調(diào)中判斷,然后設(shè)置默認(rèn)圖片(所以,大家在自己實現(xiàn)listener的時候,別忘了判斷resp.getBitmap()!=null);

接下來檢查該url是否早已加入了請求對了,如果早已加入呢,則將剛初始化的ImageContainer加入BatchedImageRequest,返回結(jié)束。

如果是一個新的請求,則通過makeImageRequest創(chuàng)建一個新的請求,然后將這個請求分別加入mRequestQueue和mInFlightRequests,注意mInFlightRequests中會初始化一個BatchedImageRequest,存儲相同的請求隊列。

這里注意mRequestQueue是個對象,并不是隊列數(shù)據(jù)結(jié)構(gòu),所以我們要看下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;
  }
 }

這里首先將請求加入mCurrentRequests,這個mCurrentRequests保存了所有需要處理的Request,主要為了提供cancel的入口。

如果該請求不應(yīng)該被緩存則直接加入mNetworkQueue,然后返回。

然后判斷該請求是否有相同的請求正在被處理,如果有則加入mWaitingRequests;如果沒有,則
加入mWaitingRequests.put(cacheKey, null)和mCacheQueue.add(request)。

ok,到這里我們就分析完成了直觀的代碼,但是你可能會覺得,那么到底是在哪里觸發(fā)的網(wǎng)絡(luò)請求,加載圖片呢?

那么,首先你應(yīng)該知道,我們需要加載圖片的時候,會makeImageRequest然后將這個請求加入到各種隊列,主要包含mCurrentRequests、mCacheQueue。

然后,還記得我們初始化RequestQueue的時候,啟動了幾個轉(zhuǎn)發(fā)線程嗎?CacheDispatcher和NetworkDispatcher。

其實,網(wǎng)絡(luò)請求就是在這幾個線程中真正去加載的,我們分別看一下;

(四)CacheDispatcher

看一眼構(gòu)造方法;

#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),內(nèi)存緩存在ImageLoader那里已經(jīng)判斷過了。

可以看到這里是個無限循環(huán),不斷的從mCacheQueue去取出請求,如果請求已經(jīng)被取消就直接結(jié)束;

接下來從緩存中獲取:

=>如果沒有取到,則加入mNetworkQueue

=>如果緩存過期,則加入mNetworkQueue

否則,就是取到了可用的緩存了;調(diào)用request.parseNetworkResponse解析從緩存中取出的data和responseHeaders;接下來判斷TTL(主要還是判斷是否過期),如果沒有過期則直接通過mDelivery.postResponse轉(zhuǎn)發(fā),然后回調(diào)到UI線程;如果ttl不合法,回調(diào)完成后,還會將該請求加入mNetworkQueue。

好了,這里其實就是如果拿到合法的緩存,則直接轉(zhuǎn)發(fā)到UI線程;反之,則加入到NetworkQueue.

接下來我們看NetworkDispatcher。

(五)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去請求處理我們的請求,處理完成以后呢:加入緩存,然后轉(zhuǎn)發(fā)。

那么看下是不是:

首先取出請求;然后通過mNetwork.performRequest(request)處理我們的請求,拿到NetworkResponse;接下來,使用request去解析我們的NetworkResponse。
拿到Response以后,判斷是否應(yīng)該緩存,如果需要,則緩存。

最后mDelivery.postResponse(request, response);轉(zhuǎn)發(fā);

ok,和我們的預(yù)期差不多。

這樣的話,我們的Volley去加載圖片的核心邏輯就分析完成了,簡單總結(jié)下:

首先初始化RequestQueue,主要就是開啟幾個Dispatcher線程,線程會不斷讀取請求(使用的阻塞隊列,沒有消息則阻塞)
當(dāng)我們發(fā)出請求以后,會根據(jù)url,ImageView屬性等,構(gòu)造出一個cacheKey,然后首先從LruCache中獲取(這個緩存我們自己構(gòu)建的,凡是實現(xiàn)ImageCache接口的都合法);如果沒有取到,則判斷是否存在硬盤緩存,這一步是從getCacheDir里面獲?。J(rèn)5M);如果沒有取到,則從網(wǎng)絡(luò)請求;
不過,可以發(fā)現(xiàn)的是Volley的圖片加載,并沒有LIFO這種策略;貌似對于圖片的下載,也是完整的加到內(nèi)存,然后壓縮,這么看,對于巨圖、大文件這樣的就廢了;

看起來還是蠻簡單的,不過看完以后,對于如何更好的時候該庫以及如何去設(shè)計圖片加載庫還是有很大的幫助的;

如果有興趣,大家還可以在看源碼分析的同時,想想某些細(xì)節(jié)的實現(xiàn),比如:

Dispatcher都是一些無限循環(huán)的線程,可以去看看Volley如何保證其關(guān)閉的。
對于圖片壓縮的代碼,可以在ImageRequest的parseNetworkResponse里面去看看,是如何壓縮的。
so on…
最后貼個大概的流程圖,方便記憶:

201648142342794.jpg (1062×747)

相關(guān)文章

  • 基于自定義Toast全面解析

    基于自定義Toast全面解析

    下面小編就為大家?guī)硪黄谧远xToast全面解析。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-10-10
  • 10種提升android運行效率的建議

    10種提升android運行效率的建議

    這篇文章主要分享提升android運行效率的建議,如何讓安卓程序在有限的內(nèi)存和電池資源下流暢快速有效率的運行,下面給大家分享下面給出了10種實踐中的建議,需要的朋友可以參考一下
    2021-11-11
  • android手機獲取唯一標(biāo)識的方法

    android手機獲取唯一標(biāo)識的方法

    這篇文章主要 為大家詳細(xì)介紹了android手機獲取唯一標(biāo)識的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • Android開發(fā)常見錯誤小結(jié)

    Android開發(fā)常見錯誤小結(jié)

    這篇文章主要介紹了Android開發(fā)常見錯誤,實例分析了常見的Android開發(fā)中遇到的錯誤,對Android開發(fā)有一定參考借鑒價值,需要的朋友可以參考下
    2015-05-05
  • Kotlin基礎(chǔ)通關(guān)之字符串與數(shù)字類型

    Kotlin基礎(chǔ)通關(guān)之字符串與數(shù)字類型

    這篇文章主要介紹了Kotlin基礎(chǔ)知識中的字符串與數(shù)字類型,編程中的入門知識,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-08-08
  • Android自定義樣式圓角dialog對話框

    Android自定義樣式圓角dialog對話框

    這篇文章主要為大家詳細(xì)介紹了Android自定義樣式圓角dialog對話框,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • Android 3D滑動菜單完全解析 Android實現(xiàn)推拉門式的立體特效

    Android 3D滑動菜單完全解析 Android實現(xiàn)推拉門式的立體特效

    這篇文章主要為大家詳細(xì)介紹了Android 3D滑動菜單,Android實現(xiàn)推拉門式的立體特效,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • Android判斷后臺服務(wù)是否開啟的兩種方法實例詳解

    Android判斷后臺服務(wù)是否開啟的兩種方法實例詳解

    這篇文章主要介紹了Android判斷后臺服務(wù)是否開啟的方法的相關(guān)資料,這里提供了兩種方法及實例,需要的朋友可以參考下
    2017-07-07
  • 分享一個輕量級圖片加載類 ImageLoader

    分享一個輕量級圖片加載類 ImageLoader

    這篇文章給大家分享一個輕量級圖片加載類 ImageLoader,需要的朋友可以參考下
    2016-08-08
  • Android EventBus(普通事件/粘性事件)詳解

    Android EventBus(普通事件/粘性事件)詳解

    這篇文章主要為大家詳細(xì)介紹了Android EventBus 普通事件/粘性事件的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-11-11

最新評論