public Drawable getCachedPreview(int contentRowID) { if (LOG) Log.v(TAG, "getCachedPreview(" + contentRowID + ") mPreviewStatus=" + mAllPreviewStatus); initTask(); registerObserver(); if (mAllPreviewStatus == TYPE_NEED_LOAD) { loadPreviewStatus(); } if (mAllPreviewStatus == TYPE_LOADED_NO_PREVIEW) { return mDefaultDrawable; } // MyDrawable cachedDrawable = null; synchronized (sCachedPreview) { cachedDrawable = sCachedPreview.get(contentRowID); } // when sdcard exists, load or reload the preview if (mMounted && (cachedDrawable == null || cachedDrawable.type == TYPE_NEED_LOAD)) { // add priority prioritySeed++; synchronized (mTaskQueue) { // check is processing or not // current request is not in queue. boolean isProcessing = false; if (currentRequest != null) { synchronized (currentRequest) { if (currentRequest.rowId == contentRowID) { isProcessing = true; } } } if (LOG) Log.v(TAG, "getCachedPreview() isProcessing=" + isProcessing); if (!isProcessing) { // check is in request queue or not. TaskParams oldRequest = null; for (TaskParams one : mTaskQueue) { if (one.rowId == contentRowID) { oldRequest = one; break; } } if (LOG) Log.i(TAG, "getCachedPreview() oldRequest=" + oldRequest); if (oldRequest == null) { // not in cache and not in request MyDrawable temp = new MyDrawable(null, TYPE_NEED_LOAD); synchronized (sCachedPreview) { cachedDrawable = sCachedPreview.get(contentRowID); if (cachedDrawable == null) { sCachedPreview.put(contentRowID, temp); } cachedDrawable = temp; } TaskParams task = new TaskParams(contentRowID, mListener, -prioritySeed); mTaskQueue.add(task); mTaskHandler.sendEmptyMessage(TASK_REQUEST_NEW); } else { // not in cache, but in request oldRequest.priority = -prioritySeed; // just update priority if (mTaskQueue.remove(oldRequest)) { mTaskQueue.add(oldRequest); // re-order the queue } } } else { // do nothing } } if (LOG) Log.v(TAG, "getCachedPreview() async load the drawable for " + contentRowID); } Drawable result = null; if (cachedDrawable == null || cachedDrawable.type != TYPE_LOADED_HAS_PREVIEW) { result = mDefaultDrawable; } else { result = cachedDrawable.drawable; } if (LOG) Log.v( TAG, "getCachedPreview() mPreviewStatus=" + mAllPreviewStatus + ", cachedDrawable=" + cachedDrawable + ", return " + result); return result; }
/*package*/ static class CachedPreview extends Handler { private final HashMap<Integer, MyDrawable> sCachedPreview = new HashMap<Integer, MyDrawable>(); private Context mContext; private ContentResolver mCr; private BitmapDrawable mDefaultDrawable; private boolean mIsSGMode; private Uri mUri; private int mUsage; private boolean mMounted; private int mAllPreviewStatus = TYPE_NEED_LOAD; private Boolean mRegisted = false; private MyContentObserver mContentObserver; private MyBroadcastReceiver mSdCardReceiver; private int mIconWidth; private int mIconHeight; private DrawableStateListener mListener; // asyc request media for fast the calling thread private Handler mTaskHandler; private static Looper sLooper; // priority queue for async request private static final PriorityQueue<TaskParams> mTaskQueue = new PriorityQueue<TaskParams>(10, TaskParams.getComparator()); private static final int TASK_REQUEST_DONE = 1; private static final int TASK_REQUEST_NEW = 2; public CachedPreview( Context context, BitmapDrawable defaultDrawable, boolean isSGMode, int usage, DrawableStateListener listener) { mContext = context; mCr = mContext.getContentResolver(); mDefaultDrawable = defaultDrawable; Bitmap icon = mDefaultDrawable.getBitmap(); mIconWidth = icon.getWidth(); mIconHeight = icon.getHeight(); mUsage = usage; mIsSGMode = isSGMode; if (mIsSGMode) { mUri = MBBMSStore.SG.PreviewData.CONTENT_URI; } else { mUri = MBBMSStore.ESG.Media.CONTENT_URI; } mListener = listener; if (LOG) Log.v(TAG, "CachedPreview(" + isSGMode + ", " + usage + ")"); } private void registerObserver() { // Here I register the ContentObserver and BroadcasetReceiver. // These can check the database's update and sdcard's state. // So, when there's no sdcard or database has no preview infos, // getCachedPreview() will be faster. synchronized (mRegisted) { if (!mRegisted) { mContentObserver = new MyContentObserver(null); mSdCardReceiver = new MyBroadcastReceiver(); mCr.registerContentObserver(mUri, true, mContentObserver); IntentFilter filter1 = new IntentFilter(Intent.ACTION_MEDIA_EJECT); filter1.addDataScheme("file"); mContext.registerReceiver(mSdCardReceiver, filter1); IntentFilter filter2 = new IntentFilter(Intent.ACTION_MEDIA_MOUNTED); filter2.addDataScheme("file"); mContext.registerReceiver(mSdCardReceiver, filter2); mRegisted = true; mMounted = isExternalStorageReady(); if (LOG) Log.v(TAG, "registerObserver() regist observers! mMounted=" + mMounted); } } if (LOG) Log.v(TAG, "registerObserver() mRegisted=" + mRegisted + ", mMounted=" + mMounted); } @Override public void handleMessage(Message msg) { if (LOG) Log.v(TAG, "mCallingHandler.handleMessage(" + msg + ")"); if (msg.what == TASK_REQUEST_DONE && (msg.obj instanceof TaskParams)) { TaskParams result = (TaskParams) msg.obj; if (result.listener != null) { result.listener.onChanged(result.rowId, result.drawable); } } } private boolean initTask; private long prioritySeed; private TaskParams currentRequest; private void initTask() { if (LOG) Log.v(TAG, "initTask() initTask=" + initTask + ", prioritySeed=" + prioritySeed); if (initTask) return; prioritySeed = 0; synchronized (CachedPreview.class) { if (sLooper == null) { HandlerThread t = new HandlerThread( "cached-preview-thread", android.os.Process.THREAD_PRIORITY_BACKGROUND); t.start(); sLooper = t.getLooper(); } } mTaskHandler = new Handler(sLooper) { @Override public void handleMessage(Message msg) { if (LOG) Log.v(TAG, "mTaskHandler.handleMessage(" + msg + ")"); if (msg.what == TASK_REQUEST_NEW) { synchronized (mTaskQueue) { currentRequest = mTaskQueue.poll(); } if (currentRequest == null) { Log.w(TAG, "wrong request, has request but no task params."); return; } // recheck the drawable is exists or not. int contentRowID = currentRequest.rowId; MyDrawable cachedDrawable = null; synchronized (sCachedPreview) { cachedDrawable = sCachedPreview.get(contentRowID); } if (cachedDrawable == null) { Log.w(TAG, "cached drawable was delete. may for clear."); return; } // when sdcard exists, load or reload the preview if (mMounted && cachedDrawable.type == TYPE_NEED_LOAD) { Bitmap tempBitmap = getPreview(contentRowID); if (tempBitmap != null) { tempBitmap = Bitmap.createScaledBitmap(tempBitmap, mIconWidth, mIconHeight, true); cachedDrawable.set(new FastBitmapDrawable(tempBitmap), TYPE_LOADED_HAS_PREVIEW); } else { cachedDrawable.set(null, TYPE_LOADED_NO_PREVIEW); } } currentRequest.drawable = cachedDrawable; Message done = CachedPreview.this.obtainMessage(TASK_REQUEST_DONE); done.obj = currentRequest; done.sendToTarget(); if (LOG) Log.v(TAG, "mTaskHandler.handleMessage() send done. " + currentRequest); } } }; initTask = true; } private void clearTask() { if (LOG) Log.v(TAG, "clearTask() initTask=" + initTask); if (initTask) { prioritySeed = 0; removeMessages(TASK_REQUEST_DONE); synchronized (mTaskQueue) { mTaskQueue.clear(); } mTaskHandler.removeMessages(TASK_REQUEST_NEW); mTaskHandler = null; } initTask = false; } public Drawable getCachedPreview(int contentRowID) { if (LOG) Log.v(TAG, "getCachedPreview(" + contentRowID + ") mPreviewStatus=" + mAllPreviewStatus); initTask(); registerObserver(); if (mAllPreviewStatus == TYPE_NEED_LOAD) { loadPreviewStatus(); } if (mAllPreviewStatus == TYPE_LOADED_NO_PREVIEW) { return mDefaultDrawable; } // MyDrawable cachedDrawable = null; synchronized (sCachedPreview) { cachedDrawable = sCachedPreview.get(contentRowID); } // when sdcard exists, load or reload the preview if (mMounted && (cachedDrawable == null || cachedDrawable.type == TYPE_NEED_LOAD)) { // add priority prioritySeed++; synchronized (mTaskQueue) { // check is processing or not // current request is not in queue. boolean isProcessing = false; if (currentRequest != null) { synchronized (currentRequest) { if (currentRequest.rowId == contentRowID) { isProcessing = true; } } } if (LOG) Log.v(TAG, "getCachedPreview() isProcessing=" + isProcessing); if (!isProcessing) { // check is in request queue or not. TaskParams oldRequest = null; for (TaskParams one : mTaskQueue) { if (one.rowId == contentRowID) { oldRequest = one; break; } } if (LOG) Log.i(TAG, "getCachedPreview() oldRequest=" + oldRequest); if (oldRequest == null) { // not in cache and not in request MyDrawable temp = new MyDrawable(null, TYPE_NEED_LOAD); synchronized (sCachedPreview) { cachedDrawable = sCachedPreview.get(contentRowID); if (cachedDrawable == null) { sCachedPreview.put(contentRowID, temp); } cachedDrawable = temp; } TaskParams task = new TaskParams(contentRowID, mListener, -prioritySeed); mTaskQueue.add(task); mTaskHandler.sendEmptyMessage(TASK_REQUEST_NEW); } else { // not in cache, but in request oldRequest.priority = -prioritySeed; // just update priority if (mTaskQueue.remove(oldRequest)) { mTaskQueue.add(oldRequest); // re-order the queue } } } else { // do nothing } } if (LOG) Log.v(TAG, "getCachedPreview() async load the drawable for " + contentRowID); } Drawable result = null; if (cachedDrawable == null || cachedDrawable.type != TYPE_LOADED_HAS_PREVIEW) { result = mDefaultDrawable; } else { result = cachedDrawable.drawable; } if (LOG) Log.v( TAG, "getCachedPreview() mPreviewStatus=" + mAllPreviewStatus + ", cachedDrawable=" + cachedDrawable + ", return " + result); return result; } private Bitmap getPreview(int contentRowID) { Bitmap bitmap = null; if (mIsSGMode) { bitmap = MBBMSStore.SG.ContentPreviewData.getPreviewBitmap(mCr, contentRowID, mUsage, null); } else { bitmap = MBBMSStore.ESG.ContentMedia.getMediaBitmap(mCr, contentRowID, mUsage, null); } if (LOG) Log.v(TAG, "getPreview() bitmap=" + bitmap); return bitmap; } public void clearCachedPreview() { synchronized (mRegisted) { if (mRegisted) { mCr.unregisterContentObserver(mContentObserver); mContentObserver = null; mContext.unregisterReceiver(mSdCardReceiver); mSdCardReceiver = null; mAllPreviewStatus = TYPE_NEED_LOAD; mRegisted = false; } } synchronized (sCachedPreview) { sCachedPreview.clear(); } clearTask(); } private void loadPreviewStatus() { Cursor cursor = null; try { cursor = mCr.query(mUri, new String[] {"count(*)"}, null, null, null); if (cursor != null && cursor.moveToFirst()) { int count = cursor.getInt(0); if (count > 0) { mAllPreviewStatus = TYPE_LOADED_HAS_PREVIEW; } else { mAllPreviewStatus = TYPE_LOADED_NO_PREVIEW; } } } catch (Exception e) { e.printStackTrace(); // ignore it. } finally { if (cursor != null) { cursor.close(); } } if (LOG) Log.v(TAG, "loadPreviewStatus() mPreviewStatus=" + mAllPreviewStatus); } private class MyContentObserver extends ContentObserver { public MyContentObserver(Handler handler) { super(handler); } @Override public void onChange(boolean selfChange) { if (LOG) Log.v(TAG, "mContentObserver.onChange(" + selfChange + ")"); if (mAllPreviewStatus != TYPE_NEED_LOAD) { // we do not delete drawable until we refresh the whole SG or ESG info // which is be operated in MainScreen Activity. // So here do not need to consider delete case. Set<Integer> keys = sCachedPreview.keySet(); for (Integer key : keys) { MyDrawable drawable = sCachedPreview.get(key); if (drawable.type == TYPE_LOADED_NO_PREVIEW) { drawable.type = TYPE_NEED_LOAD; } } mAllPreviewStatus = TYPE_NEED_LOAD; } } } private class MyBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); String path = intent.getData().getPath(); // only observe sdcard with default path: "/mnt/sdcard" String externalPath = Environment.getExternalStorageDirectory().getPath(); if (Intent.ACTION_MEDIA_EJECT.equals(action) && externalPath.equalsIgnoreCase(path)) { mMounted = false; } else if (Intent.ACTION_MEDIA_MOUNTED.equals(action) && externalPath.equalsIgnoreCase(path)) { mMounted = true; } if (LOG) Log.v(TAG, "onReceive(" + intent + ") mMounted=" + mMounted); } }; /*package*/ static final int TYPE_NEED_LOAD = 0; /*package*/ static final int TYPE_LOADED_NO_PREVIEW = 1; /*package*/ static final int TYPE_LOADED_HAS_PREVIEW = 2; /*package*/ class MyDrawable { int type; Drawable drawable; public MyDrawable(Drawable idrawable, int itype) { type = itype; drawable = idrawable; } public void set(Drawable idrawable, int itype) { type = itype; drawable = idrawable; } @Override public String toString() { return new StringBuilder() .append("MyDrawable(type=") .append(type) .append(", drawable=") .append(drawable) .append(")") .toString(); } } public interface DrawableStateListener { public void onChanged(int rowId, MyDrawable drawable); } public static class TaskParams { int rowId; MyDrawable drawable; DrawableStateListener listener; long priority; public TaskParams(int rowId, DrawableStateListener listener, long priority) { this.rowId = rowId; this.listener = listener; this.priority = priority; } @Override public String toString() { return new StringBuilder() .append("TaskInput(rowId=") .append(rowId) .append(", listener=") .append(listener) .append(", drawable=") .append(drawable) .append(")") .toString(); } static Comparator<TaskParams> getComparator() { return new Comparator<TaskParams>() { public int compare(TaskParams r1, TaskParams r2) { if (r1.priority != r2.priority) { return (r1.priority < r2.priority) ? -1 : 1; } return 0; } }; } } }