From d121cfcbb45cb9ca734e702861f0bdd422999cf5 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Mon, 13 Feb 2012 13:27:02 -0800 Subject: [PATCH] Further optimize media scanner. Inserts of directories can be done in bulk as long as they're inserted before the files contained within. Extend MediaInserter to accommodate giving priority treatment to directories. Bulk deleting of entries can be further sped up (by a factor of ~3 in my tests) by deleting entries in database order. Switch the file cache to use LinkedHashMap instead of HashMap to allow iterating over the cache in database order. Also use bindArgs to allow for better caching of sql statements. Change-Id: Ieb9ffc4e866c6cd505bf795eb80ff5d03ffc56bd --- media/java/android/media/MediaInserter.java | 33 +++++++++++--- media/java/android/media/MediaScanner.java | 48 ++++++++++++--------- 2 files changed, 55 insertions(+), 26 deletions(-) diff --git a/media/java/android/media/MediaInserter.java b/media/java/android/media/MediaInserter.java index e92c710a3edd8..7fcbffa06eb16 100644 --- a/media/java/android/media/MediaInserter.java +++ b/media/java/android/media/MediaInserter.java @@ -34,6 +34,8 @@ import java.util.List; public class MediaInserter { private final HashMap> mRowMap = new HashMap>(); + private final HashMap> mPriorityRowMap = + new HashMap>(); private IContentProvider mProvider; private int mBufferSizePerUri; @@ -44,26 +46,45 @@ public class MediaInserter { } public void insert(Uri tableUri, ContentValues values) throws RemoteException { - List list = mRowMap.get(tableUri); + insert(tableUri, values, false); + } + + public void insertwithPriority(Uri tableUri, ContentValues values) throws RemoteException { + insert(tableUri, values, true); + } + + private void insert(Uri tableUri, ContentValues values, boolean priority) throws RemoteException { + HashMap> rowmap = priority ? mPriorityRowMap : mRowMap; + List list = rowmap.get(tableUri); if (list == null) { list = new ArrayList(); - mRowMap.put(tableUri, list); + rowmap.put(tableUri, list); } list.add(new ContentValues(values)); if (list.size() >= mBufferSizePerUri) { - flush(tableUri); + flushAllPriority(); + flush(tableUri, list); } } public void flushAll() throws RemoteException { + flushAllPriority(); for (Uri tableUri : mRowMap.keySet()){ - flush(tableUri); + List list = mRowMap.get(tableUri); + flush(tableUri, list); } mRowMap.clear(); } - private void flush(Uri tableUri) throws RemoteException { - List list = mRowMap.get(tableUri); + private void flushAllPriority() throws RemoteException { + for (Uri tableUri : mPriorityRowMap.keySet()){ + List list = mPriorityRowMap.get(tableUri); + flush(tableUri, list); + } + mPriorityRowMap.clear(); + } + + private void flush(Uri tableUri, List list) throws RemoteException { if (!list.isEmpty()) { ContentValues[] valuesArray = new ContentValues[list.size()]; valuesArray = list.toArray(valuesArray); diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index 69ca58bfe3c59..52d31c749e07a 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -59,6 +59,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.Locale; /** @@ -374,7 +375,7 @@ public class MediaScanner // hashes file path to FileCacheEntry. // path should be lower case if mCaseInsensitivePaths is true - private HashMap mFileCache; + private LinkedHashMap mFileCache; private ArrayList mPlayLists; @@ -922,16 +923,15 @@ public class MediaScanner } } - // new file, insert it - // We insert directories immediately to ensure they are in the database - // before the files they contain. - // Otherwise we can get duplicate directory entries in the database - // if one of the media FileInserters is flushed before the files table FileInserter - // Also, we immediately insert the file if the rowId of the inserted file is - // needed. - if (inserter == null || needToSetSettings || - entry.mFormat == MtpConstants.FORMAT_ASSOCIATION) { + // New file, insert it. + // Directories need to be inserted before the files they contain, so they + // get priority when bulk inserting. + // If the rowId of the inserted file is needed, it gets inserted immediately, + // bypassing the bulk inserter. + if (inserter == null || needToSetSettings) { result = mMediaProvider.insert(tableUri, values); + } else if (entry.mFormat == MtpConstants.FORMAT_ASSOCIATION) { + inserter.insertwithPriority(tableUri, values); } else { inserter.insert(tableUri, values); } @@ -1029,7 +1029,7 @@ public class MediaScanner String[] selectionArgs = null; if (mFileCache == null) { - mFileCache = new HashMap(); + mFileCache = new LinkedHashMap(); } else { mFileCache.clear(); } @@ -1151,7 +1151,8 @@ public class MediaScanner } static class MediaBulkDeleter { - StringBuilder idList = new StringBuilder(); + StringBuilder whereClause = new StringBuilder(); + ArrayList whereArgs = new ArrayList(100); IContentProvider mProvider; Uri mBaseUri; @@ -1161,19 +1162,26 @@ public class MediaScanner } public void delete(long id) throws RemoteException { - if (idList.length() != 0) { - idList.append(","); + if (whereClause.length() != 0) { + whereClause.append(","); } - idList.append(id); - if (idList.length() > 1024) { + whereClause.append("?"); + whereArgs.add("" + id); + if (whereArgs.size() > 100) { flush(); } } public void flush() throws RemoteException { - int numrows = mProvider.delete(mBaseUri, MediaStore.MediaColumns._ID + " IN (" + - idList.toString() + ")", null); - //Log.i("@@@@@@@@@", "rows deleted: " + numrows); - idList.setLength(0); + int size = whereArgs.size(); + if (size > 0) { + String [] foo = new String [size]; + foo = whereArgs.toArray(foo); + int numrows = mProvider.delete(mBaseUri, MediaStore.MediaColumns._ID + " IN (" + + whereClause.toString() + ")", foo); + //Log.i("@@@@@@@@@", "rows deleted: " + numrows); + whereClause.setLength(0); + whereArgs.clear(); + } } }