Skip to content

Most visited

Recently visited

navigation
MediaBrowserService / src / com.example.android.mediabrowserservice /

AlbumArtCache.java

1
/*
2
 * Copyright (C) 2014 The Android Open Source Project
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
 
17
package com.example.android.mediabrowserservice;
18
 
19
import android.graphics.Bitmap;
20
import android.os.AsyncTask;
21
import android.util.LruCache;
22
 
23
import com.example.android.mediabrowserservice.utils.BitmapHelper;
24
import com.example.android.mediabrowserservice.utils.LogHelper;
25
 
26
import java.io.IOException;
27
 
28
/**
29
 * Implements a basic cache of album arts, with async loading support.
30
 */
31
public final class AlbumArtCache {
32
    private static final String TAG = LogHelper.makeLogTag(AlbumArtCache.class);
33
 
34
    private static final int MAX_ALBUM_ART_CACHE_SIZE = 12*1024*1024;  // 12 MB
35
    private static final int MAX_ART_WIDTH = 800;  // pixels
36
    private static final int MAX_ART_HEIGHT = 480;  // pixels
37
 
38
    // Resolution reasonable for carrying around as an icon (generally in
39
    // MediaDescription.getIconBitmap). This should not be bigger than necessary, because
40
    // the MediaDescription object should be lightweight. If you set it too high and try to
41
    // serialize the MediaDescription, you may get FAILED BINDER TRANSACTION errors.
42
    private static final int MAX_ART_WIDTH_ICON = 128;  // pixels
43
    private static final int MAX_ART_HEIGHT_ICON = 128;  // pixels
44
 
45
    private static final int BIG_BITMAP_INDEX = 0;
46
    private static final int ICON_BITMAP_INDEX = 1;
47
 
48
    private final LruCache<String, Bitmap[]> mCache;
49
 
50
    private static final AlbumArtCache sInstance = new AlbumArtCache();
51
 
52
    public static AlbumArtCache getInstance() {
53
        return sInstance;
54
    }
55
 
56
    private AlbumArtCache() {
57
        // Holds no more than MAX_ALBUM_ART_CACHE_SIZE bytes, bounded by maxmemory/4 and
58
        // Integer.MAX_VALUE:
59
        int maxSize = Math.min(MAX_ALBUM_ART_CACHE_SIZE,
60
            (int) (Math.min(Integer.MAX_VALUE, Runtime.getRuntime().maxMemory()/4)));
61
        mCache = new LruCache<String, Bitmap[]>(maxSize) {
62
            @Override
63
            protected int sizeOf(String key, Bitmap[] value) {
64
                return value[BIG_BITMAP_INDEX].getByteCount()
65
                    + value[ICON_BITMAP_INDEX].getByteCount();
66
            }
67
        };
68
    }
69
 
70
    public Bitmap getBigImage(String artUrl) {
71
        Bitmap[] result = mCache.get(artUrl);
72
        return result == null ? null : result[BIG_BITMAP_INDEX];
73
    }
74
 
75
    public Bitmap getIconImage(String artUrl) {
76
        Bitmap[] result = mCache.get(artUrl);
77
        return result == null ? null : result[ICON_BITMAP_INDEX];
78
    }
79
 
80
    public void fetch(final String artUrl, final FetchListener listener) {
81
        // WARNING: for the sake of simplicity, simultaneous multi-thread fetch requests
82
        // are not handled properly: they may cause redundant costly operations, like HTTP
83
        // requests and bitmap rescales. For production-level apps, we recommend you use
84
        // a proper image loading library, like Glide.
85
        Bitmap[] bitmap = mCache.get(artUrl);
86
        if (bitmap != null) {
87
            LogHelper.d(TAG, "getOrFetch: album art is in cache, using it", artUrl);
88
            listener.onFetched(artUrl, bitmap[BIG_BITMAP_INDEX], bitmap[ICON_BITMAP_INDEX]);
89
            return;
90
        }
91
        LogHelper.d(TAG, "getOrFetch: starting asynctask to fetch ", artUrl);
92
 
93
        new AsyncTask<Void, Void, Bitmap[]>() {
94
            @Override
95
            protected Bitmap[] doInBackground(Void[] objects) {
96
                Bitmap[] bitmaps;
97
                try {
98
                    Bitmap bitmap = BitmapHelper.fetchAndRescaleBitmap(artUrl,
99
                        MAX_ART_WIDTH, MAX_ART_HEIGHT);
100
                    Bitmap icon = BitmapHelper.scaleBitmap(bitmap,
101
                        MAX_ART_WIDTH_ICON, MAX_ART_HEIGHT_ICON);
102
                    bitmaps = new Bitmap[] {bitmap, icon};
103
                    mCache.put(artUrl, bitmaps);
104
                } catch (IOException e) {
105
                    return null;
106
                }
107
                LogHelper.d(TAG, "doInBackground: putting bitmap in cache. cache size=" +
108
                    mCache.size());
109
                return bitmaps;
110
            }
111
 
112
            @Override
113
            protected void onPostExecute(Bitmap[] bitmaps) {
114
                if (bitmaps == null) {
115
                    listener.onError(artUrl, new IllegalArgumentException("got null bitmaps"));
116
                } else {
117
                    listener.onFetched(artUrl,
118
                        bitmaps[BIG_BITMAP_INDEX], bitmaps[ICON_BITMAP_INDEX]);
119
                }
120
            }
121
        }.execute();
122
    }
123
 
124
    public static abstract class FetchListener {
125
        public abstract void onFetched(String artUrl, Bitmap bigImage, Bitmap iconImage);
126
        public void onError(String artUrl, Exception e) {
127
            LogHelper.e(TAG, e, "AlbumArtFetchListener: error while downloading " + artUrl);
128
        }
129
    }
130
}
This site uses cookies to store your preferences for site-specific language and display options.

Get the latest Android developer news and tips that will help you find success on Google Play.

* Required Fields

Hooray!

Browse this site in ?

You requested a page in , but your language preference for this site is .

Would you like to change your language preference and browse this site in ? If you want to change your language preference later, use the language menu at the bottom of each page.

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.