編輯:關於Android編程
一、前言
在Android開發中,會經常涉及到顯示圖片的相關操作,在網上查閱資料,ImageLoader得到大家廣泛的使用,本篇文章針對初使用者的一個向導,同時也是自己使用該框架的一個總結,主要包含:
## 源碼淺析 ##
## 使用教程 ##
## 用法總結及demo下載 ##
二、源碼淺析
從用法來看,我們在使用該框架的時候,會先做一個初始化操作(一般在Application中),
ImageLoader imageLoader = ImageLoader.getInstance();
imageLoader.init(ImageLoaderConfiguration imageLoaderConfiguration );
我們在源碼進入該方法查看:
private volatile static ImageLoader instance;
/** Returns singleton class instance */
public static ImageLoader getInstance() {
if (instance == null) {
synchronized (ImageLoader.class) {
if (instance == null) {
instance = new ImageLoader();
}
}
}
return instance;
}
protected ImageLoader() {
}
/**
* Initializes ImageLoader instance with configuration.
* If configurations was set before ( {@link #isInited()} == true) then this method does nothing.
* To force initialization with new configuration you should {@linkplain #destroy() destroy ImageLoader} at first.
*
* @param configuration {@linkplain ImageLoaderConfiguration ImageLoader configuration}
* @throws IllegalArgumentException if configuration parameter is null
*/
public synchronized void init(ImageLoaderConfiguration configuration) {
if (configuration == null) {
throw new IllegalArgumentException(ERROR_INIT_CONFIG_WITH_NULL);
}
if (this.configuration == null) {
L.d(LOG_INIT_CONFIG);
engine = new ImageLoaderEngine(configuration);
this.configuration = configuration;
} else {
L.w(WARNING_RE_INIT_CONFIG);
}
}
獲取實例對象的方式用了單例模式,這裡我們主要看一下init()這個方法:
根據注釋的文檔可知初始化時的一些注意事項,該方法主要對engine做初始化:
engine = new ImageLoaderEngine(configuration);
我們繼續進入ImageLoaderEngine中看下這個類的構造方法:
ImageLoaderEngine(ImageLoaderConfiguration configuration) {
this.configuration = configuration;
taskExecutor = configuration.taskExecutor;
taskExecutorForCachedImages = configuration.taskExecutorForCachedImages;
taskDistributor = DefaultConfigurationFactory.createTaskDistributor();
}
可知,該庫是通過Executor對象將線程放入線程池中運行的,此構造方法裡面初始化了taskExecutorForCachedImages、taskExecutor、taskDistributor這三個對象,它們都是Executor接口的實例。關於Executor接口,
關於初始化的操作我們先看到這裡,主要是明白了它是通過Executor來處理任務的。
接下來看下顯示圖片的操作,同樣,我們看下源碼:
/**
* Adds display image task to execution pool. Image will be set to ImageAware when it's turn.
* NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
*
* @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
* @param imageAware {@linkplain com.nostra13.universalimageloader.core.imageaware.ImageAware Image aware view}
* which should display image
* @param options {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image
* decoding and displaying. If null - default display image options
* {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
* from configuration} will be used.
* @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires
* events on UI thread if this method is called on UI thread.
* @param progressListener {@linkplain com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener
* Listener} for image loading progress. Listener fires events on UI thread if this method
* is called on UI thread. Caching on disk should be enabled in
* {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions options} to make
* this listener work.
* @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
* @throws IllegalArgumentException if passed imageAware is null
*/
public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options,
ImageLoadingListener listener, ImageLoadingProgressListener progressListener) {
checkConfiguration();
if (imageAware == null) {
throw new IllegalArgumentException(ERROR_WRONG_ARGUMENTS);
}
if (listener == null) {
listener = emptyListener;
}
if (options == null) {
options = configuration.defaultDisplayImageOptions;
}
if (TextUtils.isEmpty(uri)) {
engine.cancelDisplayTaskFor(imageAware);
listener.onLoadingStarted(uri, imageAware.getWrappedView());
if (options.shouldShowImageForEmptyUri()) {
imageAware.setImageDrawable(options.getImageForEmptyUri(configuration.resources));
} else {
imageAware.setImageDrawable(null);
}
listener.onLoadingComplete(uri, imageAware.getWrappedView(), null);
return;
}
ImageSize targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, configuration.getMaxImageSize());
String memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize);
engine.prepareDisplayTaskFor(imageAware, memoryCacheKey);
listener.onLoadingStarted(uri, imageAware.getWrappedView());
Bitmap bmp = configuration.memoryCache.get(memoryCacheKey);
if (bmp != null && !bmp.isRecycled()) {
L.d(LOG_LOAD_IMAGE_FROM_MEMORY_CACHE, memoryCacheKey);
if (options.shouldPostProcess()) {
ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,
options, listener, progressListener, engine.getLockForUri(uri));
ProcessAndDisplayImageTask displayTask = new ProcessAndDisplayImageTask(engine, bmp, imageLoadingInfo,
defineHandler(options));
if (options.isSyncLoading()) {
displayTask.run();
} else {
engine.submit(displayTask);
}
} else {
options.getDisplayer().display(bmp, imageAware, LoadedFrom.MEMORY_CACHE);
listener.onLoadingComplete(uri, imageAware.getWrappedView(), bmp);
}
} else {
if (options.shouldShowImageOnLoading()) {
imageAware.setImageDrawable(options.getImageOnLoading(configuration.resources));
} else if (options.isResetViewBeforeLoading()) {
imageAware.setImageDrawable(null);
}
ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,
options, listener, progressListener, engine.getLockForUri(uri));
LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(engine, imageLoadingInfo,
defineHandler(options));
if (options.isSyncLoading()) {
displayTask.run();
} else {
engine.submit(displayTask);
}
}
}
可知,我們將顯示圖片的任務加入到線程池中,之後ImageAware進行工作,
這裡看下這段代碼:
if (options == null) {
options = configuration.defaultDisplayImageOptions;
}
其實就是我們設置的圖片顯示失敗時顯示的圖片,這邊是我們設置null時,使用了默認的圖片。
回到剛才,我們發現該類讀取了很多的配置參數信息,其實是我們初始化時,配置的參數,主要是配置緩存相關信息,見第二部分,使用教程。最後我們通過ImageLoaderEngine來執行顯示圖片的任務。
engine.submit(displayTask);
submit內部方法(ImageLoaderEngine類中):
/** Submits task to execution pool */
void submit(ProcessAndDisplayImageTask task) {
initExecutorsIfNeed();
taskExecutorForCachedImages.execute(task);
}
private void initExecutorsIfNeed() {
if (!configuration.customExecutor && ((ExecutorService) taskExecutor).isShutdown()) {
taskExecutor = createTaskExecutor();
}
if (!configuration.customExecutorForCachedImages && ((ExecutorService) taskExecutorForCachedImages)
.isShutdown()) {
taskExecutorForCachedImages = createTaskExecutor();
}
}
以上是主要的一些源碼淺析,想了解更多的可以自行翻閱源碼查看。接下來我們介紹一下簡單的使用教程。
三、使用教程
首先獲取該庫:我用的是gradle配置:
在gradle中加入:
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.3'
Maven 配置:
com.nostra13.universalimageloader
universal-image-loader
1.9.3
在AndroidManifest.xml中加入(涉及到圖片緩存讀寫路徑,訪問網絡操作):
之後我們就可以代碼編寫。
首先在activity_main中設置一個顯示圖片的控件:
在MainActivity中使用ImageLoader加載一張網絡圖片:
imageView = (ImageView) findViewById(R.id.user_image);
ImageLoaderUtil.init(this);
String url_image = "https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWsK1HF6hhy/it/u=3266622398,4228444443&fm=116&gp=0.jpg";
ImageLoaderUtil.displayImage(url_image,imageView,ImageLoaderUtil.getAvatarDisplayOptions());
顯示的效果圖:
當網絡路徑不存在圖片時(顯示預設的圖片):
ImageLoaderUtil是我自己封裝的一個類:
主要我們得編寫init()方法:<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;">
public static void init(Context context) {
File cacheDir = getCacheDirectory(context); //緩存文件夾路徑
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context)
.memoryCacheExtraOptions(480, 800) // default = device screen dimensions 內存緩存文件的最大長寬
.diskCacheExtraOptions(480, 800, null) // 本地緩存的詳細信息(緩存的最大長寬),最好不要設置這個
// .taskExecutor("")
// .taskExecutorForCachedImages("")
.threadPoolSize(3) // default 線程池內加載的數量
.threadPriority(Thread.NORM_PRIORITY - 2) // default 設置當前線程的優先級
.tasksProcessingOrder(QueueProcessingType.FIFO) // default
.denyCacheImageMultipleSizesInMemory()
.memoryCache(new LruMemoryCache(2 * 1024 * 1024)) //可以通過自己的內存緩存實現
.memoryCacheSize(2 * 1024 * 1024) // 內存緩存的最大值
.memoryCacheSizePercentage(13) // default
.diskCache(new UnlimitedDiscCache(cacheDir)) // default 可以自定義緩存路徑
.diskCacheSize(50 * 1024 * 1024) // 50 Mb sd卡(本地)緩存的最大值
.diskCacheFileCount(100) // 可以緩存的文件數量
// default為使用HASHCODE對UIL進行加密命名, 還可以用MD5(new Md5FileNameGenerator())加密
.diskCacheFileNameGenerator(new HashCodeFileNameGenerator())
.imageDownloader(new BaseImageDownloader(context)) // default
.imageDecoder(new BaseImageDecoder(true)) // l
.defaultDisplayImageOptions(DisplayImageOptions.createSimple()) // default
.writeDebugLogs() // 打印debug log
.build(); //開始構建
ImageLoader.getInstance().init(config);
}
可參照注釋對應了解參數信息。
完整的ImageLoaderUtil類:
package constraintlayout.test.test.viviant.imageloadertest.util;
import android.content.Context;
import android.util.Log;
import android.widget.ImageView;
import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiscCache;
import com.nostra13.universalimageloader.cache.disc.naming.HashCodeFileNameGenerator;
import com.nostra13.universalimageloader.cache.memory.impl.LruMemoryCache;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
import com.nostra13.universalimageloader.core.assist.QueueProcessingType;
import com.nostra13.universalimageloader.core.decode.BaseImageDecoder;
import com.nostra13.universalimageloader.core.download.BaseImageDownloader;
import java.io.File;
import constraintlayout.test.test.viviant.imageloadertest.R;
/**
* 作者:viviant on 2016/6/30 09:22
* 描述:
*/
public class ImageLoaderUtil {
private static final String PICTURE_CACHE_DIR = "picture";
private static String TAG = "ImageLoaderUtil";
public static void init(Context context) {
File cacheDir = getCacheDirectory(context); //緩存文件夾路徑
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context)
.memoryCacheExtraOptions(480, 800) // default = device screen dimensions 內存緩存文件的最大長寬
.diskCacheExtraOptions(480, 800, null) // 本地緩存的詳細信息(緩存的最大長寬),最好不要設置這個
// .taskExecutor("")
// .taskExecutorForCachedImages("")
.threadPoolSize(3) // default 線程池內加載的數量
.threadPriority(Thread.NORM_PRIORITY - 2) // default 設置當前線程的優先級
.tasksProcessingOrder(QueueProcessingType.FIFO) // default
.denyCacheImageMultipleSizesInMemory()
.memoryCache(new LruMemoryCache(2 * 1024 * 1024)) //可以通過自己的內存緩存實現
.memoryCacheSize(2 * 1024 * 1024) // 內存緩存的最大值
.memoryCacheSizePercentage(13) // default
.diskCache(new UnlimitedDiscCache(cacheDir)) // default 可以自定義緩存路徑
.diskCacheSize(50 * 1024 * 1024) // 50 Mb sd卡(本地)緩存的最大值
.diskCacheFileCount(100) // 可以緩存的文件數量
// default為使用HASHCODE對UIL進行加密命名, 還可以用MD5(new Md5FileNameGenerator())加密
.diskCacheFileNameGenerator(new HashCodeFileNameGenerator())
.imageDownloader(new BaseImageDownloader(context)) // default
.imageDecoder(new BaseImageDecoder(true)) // l
.defaultDisplayImageOptions(DisplayImageOptions.createSimple()) // default
.writeDebugLogs() // 打印debug log
.build(); //開始構建
ImageLoader.getInstance().init(config);
}
/**
* 獲取緩存文件
*
* @param context
* @return
*/
public final static File getCacheDirectory(Context context) {
String cacheDir = SystemUtility.getAppCachePath();
return createDir(cacheDir + PICTURE_CACHE_DIR);
}
private final static File createDir(String dir) {
File appCacheDir = new File(dir);
if (!appCacheDir.exists()) {
if (!appCacheDir.mkdirs()) {
Log.i(TAG, "createDir# Unable to create external cache directory");
return null;
}
}
return appCacheDir;
}
/**
*顯示出錯,替換的圖片
* @return
*/
public static DisplayImageOptions getAvatarDisplayOptions() {
DisplayImageOptions avatarOptions = new DisplayImageOptions.Builder()
.showImageOnLoading(R.drawable.error)
.showImageForEmptyUri(R.drawable.error)
.showImageOnFail(R.drawable.error)
.cacheInMemory(true).cacheOnDisk(true).build();
return avatarOptions;
}
/**
* 顯示圖片
*
* @param url
* @param imageView
* @param options
*/
public static void displayImage(String url, ImageView imageView,
DisplayImageOptions options) {
ImageLoader.getInstance().displayImage(url, imageView, options);
}
}
四、用法總結及源碼下載:
ImageLoader的用法總的來說還是很便捷的,我們可以設置相應的參數來初始化配置,並且它的優點是應用進行大量的訪問網絡圖片。以上是我對該框架的一些使用方法進行總結,不足之處,歡迎批評。
源碼下載(AndroidStudio直接導入):
https://github.com/viviant1224/ImageLoaderTest
本文實例講述了Android編程使WebView支持HTML5 Video全屏播放的解決方法。分享給大家供大家參考,具體如下:1)需要在AndroidManifest.x
前言在自定義ViewGroup中,有時候需要實現觸摸事件攔截,比如ListView下拉刷新就是典型的觸摸事件攔截的例子。觸摸事件攔截就是在觸摸事件被parent view
上一篇文章中我們講解了App的數據統計,其主要分為兩種:使用第三方服務統計和自身實現數據統計。一般而言我們使用第三方統計服務已經可以很好的滿足我們的也無需求了,只是部分數
static修飾局部變量static修飾局部變量用於修改變量的存儲位置,從自動變量修改為靜態變量(在靜態區開辟空間,不在棧上開辟),但變量的鏈接屬性和作用域不受影響。st