【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>
图片加载有很多,部份都是使用LruCache、弱引用、软引用等编写的,其编目的就是优化内存、缓存、加载效果,不过,它们都是各有千秋,下面看看【Apollo播放器】它是对图片加载如何做的
它主要结构如下:
ImageInfo - 图片信息相关
ImageCache - 图片缓存相关
ImageProvider -图片提供者相关(管理器)
GetBitmapTask -图片下载任务
下面一一分析每个类的结构和使用
ImageInfo.java
public class ImageInfo {
//图像类型
//专辑(ablum), 艺术家(artist), 播放列表(playlist), 流派genre
public String type;
//图像来源
//lastfm - 来自网站
//file - 来自音频
//gallery - 来自相册
//first_avail - 来自 'file' 或 'lastfm'
public String source;
//请求的图像的大小
//缩略图 or 正常图
public String size;
//执行图像获取所需的额外数据
//lastFM - 艺术家需要艺术家的形象
//ablum - 专辑、歌手专辑图像
//file - 需要相册id
//gallery - 需要图像的文件路径
//first_available - 需要两个文件lastfm数据
public String[] data;
}
ImageCache.java
public final class ImageCache {
private static final String TAG = ImageCache.class.getSimpleName();
//二级缓存实例对象,其底层是继承自LinkedHashMap<K, V>
//具体实现请看源码:http://androidxref.com/4.4.2_r1/xref/packages/apps/UnifiedEmail/src/com/android/mail/utils/LruCache.java
private LruCache<String, Bitmap> mLruCache;
private static ImageCache sInstance;
public ImageCache(final Context context) {
init(context);
}
/**
* 图片缓存单例模式
* @param context
* @return
*/
public final static ImageCache getInstance(final Context context) {
if (sInstance == null) {
sInstance = new ImageCache(context.getApplicationContext());
}
return sInstance;
}
public void init(final Context context) {
final ActivityManager activityManager = (ActivityManager)context
.getSystemService(Context.ACTIVITY_SERVICE);
final int lruCacheSize = Math.round(0.25f * activityManager.getMemoryClass()
* 1024 * 1024);//四舍五入计算二级缓存大小
mLruCache = new LruCache<String, Bitmap>(lruCacheSize) {
@Override
protected int sizeOf(final String paramString, final Bitmap paramBitmap) {
//返回每个key所对应的values在二级缓存中报占大小
return paramBitmap.getByteCount();
}
};
}
/**
* 查找或创建的高速缓存
* @param activity
* @return
*/
public static final ImageCache findOrCreateCache(final Activity activity) {
FragmentManager nFragmentManger = activity.getFragmentManager();
RetainFragment retainFragment = (RetainFragment)nFragmentManger.findFragmentByTag(TAG);
if (retainFragment == null) {
retainFragment = new RetainFragment();
nFragmentManger.beginTransaction().add(retainFragment, TAG).commit();
}
ImageCache cache = (ImageCache)retainFragment.getObject();
if (cache == null) {
cache = getInstance(activity);
retainFragment.setObject(cache);
}
return cache;
}
/**
* 存入缓存
* @param data
* @param bitmap
*/
public void add(final String data, final Bitmap bitmap) {
if (data == null || bitmap == null) {
return;
}
if (get(data) == null) {
mLruCache.put(data, bitmap);
}
}
/**
* 获取缓存图片
* @param data
* @return
*/
public final Bitmap get(final String data) {
if (data == null) {
return null;
}
if (mLruCache != null) {
final Bitmap mBitmap = mLruCache.get(data);
if (mBitmap != null) {
return mBitmap;
}
}
return null;
}
/**
* 移除指定缓存
*/
public void remove(final String key) {
if (mLruCache != null) {
mLruCache.remove(key);
}
}
/**
* 清除缴存并回收
*/
public void clearMemCache() {
if (mLruCache != null) {
mLruCache.evictAll();
}
System.gc();
}
/**
* 个人理解为是 保持应用(FragmentManager)只存在这一个实例
* @author xiajun
*
*/
public static final class RetainFragment extends Fragment {
private Object mObject;
public RetainFragment() {
}
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Make sure this Fragment is retained over a configuration change
setRetainInstance(true);
}
public void setObject(final Object object) {
mObject = object;
}
public Object getObject() {
return mObject;
}
}
}
ImageProvider.java
public class ImageProvider implements GetBitmapTask.OnBitmapReadyListener{
private ImageCache memCache;
private Map<String, Set<ImageView>> pendingImagesMap = new HashMap<String, Set<ImageView>>();
private Set<String> unavailable = new HashSet<String>();
private Context mContext;
private int thumbSize;
private static ImageProvider mInstance;
protected ImageProvider( Activity activity ) {
mContext = activity;
memCache = ImageCache.getInstance( activity );
Resources resources = mContext.getResources();
DisplayMetrics metrics = resources.getDisplayMetrics();
thumbSize = (int) ( ( 153 * (metrics.densityDpi/160f) ) + 0.5f );
}
public final static ImageProvider getInstance( final Activity activity ) {
if (mInstance == null) {
mInstance = new ImageProvider( activity );
mInstance.setImageCache(ImageCache.findOrCreateCache(activity));
}
return mInstance;
}
public void setImageCache(final ImageCache cacheCallback) {
memCache = cacheCallback;
}
/**
* 加载图片
* @param imageView 显示的View
* @param imageInfo 图片信息相关
*/
public void loadImage( ImageView imageView, ImageInfo imageInfo ){
//创建一个图片标识,例如:21albartimg 图片命名和创建规则,请详细查看ImageUtils.java
String tag = ImageUtils.createShortTag(imageInfo) + imageInfo.size;
if( imageInfo.source.equals(SRC_FILE) || imageInfo.source.equals(SRC_LASTFM) || imageInfo.source.equals(SRC_GALLERY)){
clearFromMemoryCache( ImageUtils.createShortTag(imageInfo) );
asyncLoad( tag, imageView, new GetBitmapTask( thumbSize, imageInfo, this, imageView.getContext() ) );
}
if(!setCachedBitmap(imageView, tag)){
asyncLoad( tag, imageView, new GetBitmapTask( thumbSize, imageInfo, this, imageView.getContext() ) );
}
}
/**
* 缓存位图
* @param imageView
* @param tag
* @return
*/
private boolean setCachedBitmap(ImageView imageView, String tag) {
if (unavailable.contains(tag)) {
handleBitmapUnavailable(imageView, tag);
return true;
}
Bitmap bitmap = memCache.get(tag);
if (bitmap == null)
return false;
imageView.setTag(tag);
imageView.setImageBitmap(bitmap);
return true;
}
/**
* 处理位图不可用
* @param imageView
* @param tag
*/
private void handleBitmapUnavailable(ImageView imageView, String tag) {
imageView.setTag(tag);
imageView.setImageDrawable(null);
}
/**
* 加载位图
* @param imageView
* @param bitmap
* @param tag
*/
private void setLoadedBitmap(ImageView imageView, Bitmap bitmap, String tag) {
if (!tag.equals(imageView.getTag()))
return;
final TransitionDrawable transition = new TransitionDrawable(new Drawable[]{
new ColorDrawable(android.R.color.transparent),
new BitmapDrawable(imageView.getResources(), bitmap)
});
imageView.setImageDrawable(transition);
final int duration = imageView.getResources().getInteger(R.integer.image_fade_in_duration);
transition.startTransition(duration);
}
/**
* 异步加载
* @param tag
* @param imageView
* @param task
*/
private void asyncLoad(String tag, ImageView imageView, AsyncTask<String, Integer, Bitmap> task) {
Set<ImageView> pendingImages = pendingImagesMap.get(tag);
if (pendingImages == null) {
pendingImages = Collections.newSetFromMap(new WeakHashMap<ImageView, Boolean>()); // create weak set
pendingImagesMap.put(tag, pendingImages);
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);//可用于并行执行任务的执行
}
pendingImages.add(imageView);
imageView.setTag(tag);
imageView.setImageDrawable(null);
}
@Override
public void bitmapReady(Bitmap bitmap, String tag) {
if (bitmap == null) {
unavailable.add(tag);
}
else
{
memCache.add(tag, bitmap);
}
Set<ImageView> pendingImages = pendingImagesMap.get(tag);
if (pendingImages != null) {
pendingImagesMap.remove(tag);
for (ImageView imageView : pendingImages) {
setLoadedBitmap(imageView, bitmap, tag);
}
}
}
/**
* 清除内存缓存
* @param tag
*/
public void clearFromMemoryCache(String tag){
if (unavailable.contains(tag + SIZE_THUMB)) {
unavailable.remove(tag + SIZE_THUMB);
}
if (pendingImagesMap.get(tag + SIZE_THUMB)!=null){
pendingImagesMap.remove(tag + SIZE_THUMB);
}
if (memCache.get(tag + SIZE_THUMB)!=null){
memCache.remove(tag + SIZE_THUMB);
}
if (unavailable.contains(tag + SIZE_NORMAL)) {
unavailable.remove(tag + SIZE_NORMAL);
}
if (pendingImagesMap.get(tag + SIZE_NORMAL)!=null){
pendingImagesMap.remove(tag + SIZE_NORMAL);
}
if (memCache.get(tag + SIZE_NORMAL)!=null){
memCache.remove(tag + SIZE_NORMAL);
}
}
/**
* 清除所有缓存(内存和磁盘)
*/
public void clearAllCaches(){
try{
ImageUtils.deleteDiskCache(mContext);
memCache.clearMemCache();
}
catch(Exception e){}
}
}
GetBitmapTask.java
public class GetBitmapTask extends AsyncTask<String, Integer, Bitmap> {
private WeakReference<OnBitmapReadyListener> mListenerReference;
private WeakReference<Context> mContextReference;
private ImageInfo mImageInfo;
private int mThumbSize;
public GetBitmapTask( int thumbSize, ImageInfo imageInfo, OnBitmapReadyListener listener, Context context ) {
mListenerReference = new WeakReference<OnBitmapReadyListener>(listener);
mContextReference = new WeakReference<Context>(context);
mImageInfo = imageInfo;
mThumbSize = thumbSize;
}
@Override
protected Bitmap doInBackground(String... ignored) {
Context context = mContextReference.get();
if (context == null) {
return null;
}
//返回图像信息来源于...
File nFile = null;
if( mImageInfo.source.equals(SRC_FILE) && !isCancelled()){
nFile = ImageUtils.getImageFromMediaStore( context, mImageInfo );
}
else if ( mImageInfo.source.equals(SRC_LASTFM) && !isCancelled()){
nFile = ImageUtils.getImageFromWeb( context, mImageInfo );
}
else if ( mImageInfo.source.equals(SRC_GALLERY) && !isCancelled()){
nFile = ImageUtils.getImageFromGallery( context, mImageInfo );
}
else if ( mImageInfo.source.equals(SRC_FIRST_AVAILABLE) && !isCancelled()){
Bitmap bitmap = null;
if( mImageInfo.size.equals( SIZE_NORMAL ) ){
bitmap = ImageUtils.getNormalImageFromDisk( context, mImageInfo );
}
else if( mImageInfo.size.equals( SIZE_THUMB ) ){
bitmap = ImageUtils.getThumbImageFromDisk( context, mImageInfo, mThumbSize );
}
//if we have a bitmap here then its already properly sized
if( bitmap != null ){
return bitmap;
}
if( mImageInfo.type.equals( TYPE_ALBUM ) ){
nFile = ImageUtils.getImageFromMediaStore( context, mImageInfo );
}
if( nFile == null && ( mImageInfo.type.equals( TYPE_ALBUM ) || mImageInfo.type.equals( TYPE_ARTIST ) ) )
nFile = ImageUtils.getImageFromWeb( context, mImageInfo );
}
if( nFile != null ){
//返回正常大小的图
if( mImageInfo.size.equals( SIZE_NORMAL ) )
return BitmapFactory.decodeFile(nFile.getAbsolutePath());
//返回缩略图
return ImageUtils.getThumbImageFromDisk( context, nFile, mThumbSize );
}
return null;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
OnBitmapReadyListener listener = mListenerReference.get();
if(bitmap == null && !isCancelled()){
if(mImageInfo.size.equals(SIZE_THUMB))
bitmap = BitmapFactory.decodeResource(mContextReference.get().getResources(),
R.drawable.no_art_small);
else if(mImageInfo.size.equals(SIZE_NORMAL))
bitmap = BitmapFactory.decodeResource(mContextReference.get().getResources(),
R.drawable.no_art_normal);
}
if (bitmap != null && !isCancelled()) {
if (listener != null) {
listener.bitmapReady(bitmap, ImageUtils.createShortTag(mImageInfo) + mImageInfo.size );
}
}
}
public static interface OnBitmapReadyListener {
public void bitmapReady(Bitmap bitmap, String tag);
}
}
下面看看ImageUtils这个类的格式命名规则
ImageUtils.java
public class ImageUtils {
//图片格式
private static final String IMAGE_EXTENSION = ".img";
private static File getFile(Context context, ImageInfo imageInfo){
return new File(context.getExternalCacheDir(), createShortTag(imageInfo)+IMAGE_EXTENSION);
}
/**
* 从磁盘中获取正常图片
* @param context
* @param imageInfo
* @return
*/
public static Bitmap getNormalImageFromDisk( Context context, ImageInfo imageInfo ){
Bitmap bitmap = null;
File nFile = getFile( context, imageInfo );
if(nFile.exists()){
bitmap = BitmapFactory.decodeFile( nFile.getAbsolutePath() );
}
return bitmap;
}
/**
* 从磁盘中获取指定大小的缩略图片
* @param context
* @param imageInfo
* @param thumbSize
* @return
*/
public static Bitmap getThumbImageFromDisk( Context context, ImageInfo imageInfo, int thumbSize ){
File nFile = getFile( context, imageInfo );
return getThumbImageFromDisk( context, nFile, thumbSize );
}
/**
* 从磁盘中获取指定大小的缩略图片
* @param context
* @param nFile
* @param thumbSize
* @return
*/
public static Bitmap getThumbImageFromDisk( Context context, File nFile, int thumbSize ){
if(!nFile.exists())
return null;
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
try{
BitmapFactory.decodeFile( nFile.getAbsolutePath() , options);
}
catch(Exception e){
}
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > thumbSize || width > thumbSize) {
if (width > height) {
inSampleSize = Math.round((float)height / (float)thumbSize);
} else {
inSampleSize = Math.round((float)width / (float)thumbSize);
}
final float totalPixels = width * height;
final float totalReqPixelsCap = thumbSize * thumbSize * 2;
while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
inSampleSize++;
}
}
options.inSampleSize = inSampleSize;
options.inJustDecodeBounds = false;
Bitmap bitmap = null;
try{
bitmap = BitmapFactory.decodeFile( nFile.getAbsolutePath() , options );
}
catch (Exception e){
}
return bitmap;
}
/**
* 获取相册中的图片
* @param context
* @param imageInfo
* @return
*/
public static File getImageFromGallery( Context context, ImageInfo imageInfo ){
String albumArt = ( imageInfo.type == TYPE_ALBUM ) ? imageInfo.data[3] : imageInfo.data[1];
if(albumArt != null){
try{
File orgFile = new File(albumArt);
File newFile = new File(context.getExternalCacheDir(), createShortTag(imageInfo)+IMAGE_EXTENSION);
InputStream in = new FileInputStream(orgFile);
OutputStream out = new FileOutputStream(newFile);
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0){
out.write(buf, 0, len);
}
in.close();
out.close();
return newFile;
}
catch( Exception e){
}
}
return null;
}
/**
* 获取Web的图片
* @param context
* @param imageInfo
* @return
*/
public static File getImageFromWeb( Context context, ImageInfo imageInfo ) {
String imageUrl = null;
try {
if( imageInfo.type.equals(TYPE_ALBUM) ){
Album image = Album.getInfo(imageInfo.data[1], imageInfo.data[2], LASTFM_API_KEY);
if (image != null) {
imageUrl = image.getLargestImage();
}
}
else if ( imageInfo.type.equals(TYPE_ARTIST) ) {
PaginatedResult<Image> images = Artist.getImages(imageInfo.data[0], 2, 1, LASTFM_API_KEY);
Iterator<Image> iterator = images.getPageResults().iterator();
if (iterator.hasNext()) {
Image image = iterator.next();
imageUrl = image.getLargestImage();
}
}
} catch ( Exception e ) {
return null;
}
if ( imageUrl == null || imageUrl.isEmpty() ) {
return null;
}
File newFile = getFile( context, imageInfo );
ApolloUtils.downloadFile( imageUrl, newFile );
if (newFile.exists()) {
return newFile;
}
return null;
}
/**
* 获取多媒体图片
* @param context
* @param imageInfo
* @return
*/
public static File getImageFromMediaStore( Context context, ImageInfo imageInfo ){
String mAlbum = imageInfo.data[0];
String[] projection = {
BaseColumns._ID, Audio.Albums._ID, Audio.Albums.ALBUM_ART, Audio.Albums.ALBUM
};
Uri uri = Audio.Albums.EXTERNAL_CONTENT_URI;
Cursor cursor = null;
try{
cursor = context.getContentResolver().query(uri, projection, BaseColumns._ID+ "=" + DatabaseUtils.sqlEscapeString(mAlbum), null, null);
}
catch(Exception e){
e.printStackTrace();
}
int column_index = cursor.getColumnIndex(Audio.Albums.ALBUM_ART);
if(cursor.getCount()>0){
cursor.moveToFirst();
String albumArt = cursor.getString(column_index);
if(albumArt != null){
try{
File orgFile = new File(albumArt);
File newFile = new File(context.getExternalCacheDir(), createShortTag(imageInfo)+IMAGE_EXTENSION);
InputStream in = new FileInputStream(orgFile);
OutputStream out = new FileOutputStream(newFile);
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0){
out.write(buf, 0, len);
}
in.close();
out.close();
cursor.close();
return newFile;
}
catch( Exception e){
}
}
}
return null;
}
/**
* 删除磁盘缓存
* @param context
* @throws IOException
*/
public static void deleteDiskCache(Context context) throws IOException {
final File dir = context.getExternalCacheDir();
final File[] files = dir.listFiles();
for (final File file : files) {
if (!file.delete()) {
throw new IOException("failed to delete file: " + file);
}
}
}
/**
* 创建图片标识
* @param imageInf
* @return
*/
public static String createShortTag(ImageInfo imageInf){
String tag = null;
if( imageInf.type.equals( TYPE_ALBUM ) ){
//专辑 id + 专辑后缀
tag = imageInf.data[0] + ALBUM_SUFFIX;
}
else if (imageInf.type.equals( TYPE_ARTIST )){
//艺术家名称 + 艺术家后缀
tag = imageInf.data[0] + ARTIST_SUFFIX;
}
else if (imageInf.type.equals( TYPE_GENRE )){
//流派名称 + 流派后缀
tag = imageInf.data[0] + GENRE_SUFFIX;
}
else if (imageInf.type.equals( TYPE_PLAYLIST )){
//流派名称 + 播放列表后缀
tag = imageInf.data[0] + PLAYLIST_SUFFIX;
}
ApolloUtils.escapeForFileSystem(tag);
return tag;
}
}
【Apollo播放器】图片加载模式,就说到这里,期待后面更多代码分析....
来源:oschina
链接:https://my.oschina.net/u/150252/blog/205394