本人博客原文
modified : frameworks / base / media / java / android / media / MediaScanner . javamodified : frameworks / base / media / java / android / media / MiniThumbFile . java
/**@@ -85,16 +89,34 @@ public class MiniThumbFile {/*add 1 on google's version.*//*this version add check code for thumbdata.*/private static final int MINI_THUMB_DATA_FILE_VERSION = 3 + 1 ;- public static final int BYTES_PER_MINTHUMB = 17000 ;+ public static final int BYTES_PER_MINTHUMB = 17000 ;private static final int HEADER_SIZE = 1 + 8 + 4 + 8 ;private Uri mUri ;- private RandomAccessFile mMiniThumbFile ;+ Map < Long , RandomAccessFile > mMiniThumbFilesMap = Collections . synchronizedMap ( new HashMap < Long , RandomAccessFile >( 5 ));private FileChannel mChannel ;private ByteBuffer mBuffer ;private static Hashtable < String , MiniThumbFile > sThumbFiles =new Hashtable < String , MiniThumbFile >();private static java . util . zip . Adler32 sChecker = new Adler32 ();private static final long UNKNOWN_CHECK_CODE = - 1 ;+ final static char SEPERATE_CHAR = '_' ;+ public static String [] parseFileName ( String fileName )+ {+ String str = fileName ;+ if ( fileName == null )+ return null ;+ int index = fileName . lastIndexOf ( "/" );+ if ( index !=- 1 )+ {+ str = fileName . substring ( index + 1 );+ }+ String strs []= str . split ( SEPERATE_CHAR + "" );+ if ( strs == null || strs . length != 4 ||(! str . startsWith ( ".thumbdata" )))+ {+ return null ;+ }+ return strs ;+ }/*** We store different types of thumbnails in different files. To remain backward compatibility,* we should hashcode of content://media/external/images/media remains the same.@@ -105,7 +127,18 @@ public class MiniThumbFile {}sThumbFiles.clear();}-+ private final static long getMiniThumbFileId(long id)+ {+ return (id>>8);+ }+ public final static int getMiniThumbDataFileBlockNum()+ {+ return (1<<8);+ }+ private final static long getPositionInMiniThumbFile(long id)+ {+ return (id&0xff)*BYTES_PER_MINTHUMB;+ }public static synchronized MiniThumbFile instance(Uri uri) {String type = uri.getPathSegments().get(1);MiniThumbFile file = sThumbFiles.get(type);@@ -120,10 +153,11 @@ public class MiniThumbFile {}private String randomAccessFilePath(int version) {+ String type = mUri.getPathSegments().get(1);String directoryName =Environment.getExternalStorageDirectory().toString() + "/DCIM/.thumbnails";- return directoryName + "/.thumbdata" + version + "-" + mUri.hashCode();+ return directoryName + "/.thumbdata" + version + SEPERATE_CHAR + mUri.hashCode()+SEPERATE_CHAR+type;}/**@@ -131,18 +165,18 @@ public class MiniThumbFile {* @param uri the Uri same as instance(Uri uri).* @return*/- public static String getThumbdataPath ( Uri uri ) {+ /*public static String getThumbdataPath(Uri uri) {String type = uri.getPathSegments().get(1);Uri thumbFileUri = Uri.parse("content://media/external/" + type + "/media");String directoryName = Environment.getExternalStorageDirectory().toString()+ "/DCIM/.thumbnails";- String path = directoryName + "/.thumbdata" + MINI_THUMB_DATA_FILE_VERSION + "-" + thumbFileUri.hashCode();+ String path = directoryName + "/.thumbdata" + MINI_THUMB_DATA_FILE_VERSION +SEPERATE_CHAR + thumbFileUri.hashCode()+SEPERATE_CHAR+type;if (LOG) Log.i(TAG, "getThumbdataPath(" + uri + ") return " + path);return path;- }+ }*/- private void removeOldFile () {- String oldPath = randomAccessFilePath ( MINI_THUMB_DATA_FILE_VERSION - 1 );+ private void removeOldMiniThumbDataFile ( long miniThumbDataFileId ) {+ String oldPath = getMiniThumbDataFilePath ( MINI_THUMB_DATA_FILE_VERSION - 1 , miniThumbDataFileId );File oldFile = new File ( oldPath );if ( oldFile . exists ()) {try {@@ - 152 , 35 + 186 , 43 @@ public class MiniThumbFile {}}}-- private RandomAccessFile miniThumbDataFile () {- if ( mMiniThumbFile == null ) {- removeOldFile ();- String path = randomAccessFilePath ( MINI_THUMB_DATA_FILE_VERSION );- File directory = new File ( path ). getParentFile ();- if (! directory . isDirectory ()) {- if (! directory . mkdirs ()) {- Log . e ( TAG , "Unable to create .thumbnails directory "- + directory . toString ());- }- }+ final private String getMiniThumbDataFilePath ( int version , long miniThumbDataFileId )+ {+ return randomAccessFilePath ( version )+ SEPERATE_CHAR + miniThumbDataFileId ;+ }+ private RandomAccessFile miniThumbDataFile ( final long miniThumbDataFileId ) {+ return miniThumbDataFile ( miniThumbDataFileId , false );+ }+ private RandomAccessFile miniThumbDataFile ( final long miniThumbDataFileId , boolean onlyRead ) {+ RandomAccessFile miniThumbFile = mMiniThumbFilesMap . get ( miniThumbDataFileId );+ String path = getMiniThumbDataFilePath ( MINI_THUMB_DATA_FILE_VERSION , miniThumbDataFileId );File f = new File ( path );+ if ( miniThumbFile == null ||! f . exists ()) {+ if ( onlyRead )+ return null ;+ removeOldMiniThumbDataFile ( miniThumbDataFileId );+ File directory = new File ( path ). getParentFile ();+ if (! directory . isDirectory ()) {+ if (! directory . mkdirs ()) {+ Log . e ( TAG , "Unable to create .thumbnails directory "+ + directory . toString ());+ }+ }+try {- mMiniThumbFile = new RandomAccessFile ( f , "rw" );+ miniThumbFile = new RandomAccessFile ( f , "rw" );} catch ( IOException ex ) {// Open as read-only so we can at least read the existing// thumbnails.try {- mMiniThumbFile = new RandomAccessFile ( f , "r" );+ miniThumbFile = new RandomAccessFile ( f , "r" );} catch ( IOException ex2 ) {}}- if ( mMiniThumbFile != null ) {- mChannel = mMiniThumbFile . getChannel ();- }+ mMiniThumbFilesMap . put ( miniThumbDataFileId , miniThumbFile );}- return mMiniThumbFile ;+ return miniThumbFile ;}public MiniThumbFile ( Uri uri ) {@@ - 189 , 10 + 231 , 19 @@ public class MiniThumbFile {}public synchronized void deactivate () {- if ( mMiniThumbFile != null ) {+ if ( mMiniThumbFilesMap != null ) {+ Set < Long > keySet = mMiniThumbFilesMap . keySet ();try {- mMiniThumbFile . close ();- mMiniThumbFile = null ;+ for ( Long key : keySet )+ {+ RandomAccessFile file = mMiniThumbFilesMap . get ( key );+ if ( file != null )+ {+ mMiniThumbFilesMap . put ( key , null );+ file . close ();+ }+ }+ mMiniThumbFilesMap . clear ();} catch ( IOException ex ) {// ignore exception}@@ -205,18 +256,20 @@ public class MiniThumbFile {// check the mini thumb file for the right data. Right is// defined as having the right magic number at the offset// reserved for this "id".- RandomAccessFile r = miniThumbDataFile ();+ final long miniThumbDataFileId = getMiniThumbFileId ( id );+ RandomAccessFile r = miniThumbDataFile ( miniThumbDataFileId , true );if ( r != null ) {- long pos = id * BYTES_PER_MINTHUMB ;+ FileChannel channel = r . getChannel ();+ long pos = getPositionInMiniThumbFile ( id );FileLock lock = null ;try {mBuffer . clear ();mBuffer . limit ( 1 + 8 );- lock = mChannel . lock ( pos , 1 + 8 , true );+ lock = channel . lock ( pos , 1 + 8 , true );// check that we can read the following 9 bytes// (1 for the "status" and 8 for the long)- if ( mChannel . read ( mBuffer , pos ) == 9 ) {+ if ( channel . read ( mBuffer , pos ) == 9 ) {mBuffer . position ( 0 );if ( mBuffer . get () == 1 ) {return mBuffer . getLong ();@@ - 242 , 10 + 295 , 12 @@ public class MiniThumbFile {public synchronized void saveMiniThumbToFile ( byte [] data , long id , long magic )throws IOException {- RandomAccessFile r = miniThumbDataFile ();+ final long miniThumbDataFileId = getMiniThumbFileId ( id );+ RandomAccessFile r = miniThumbDataFile ( miniThumbDataFileId );if ( r == null ) return ;- long pos = id * BYTES_PER_MINTHUMB ;+ final long pos = getPositionInMiniThumbFile ( id );+ FileChannel channel = r . getChannel ();FileLock lock = null ;try {if ( data != null ) {@@ - 266 , 14 + 321 , 13 @@ public class MiniThumbFile {check = sChecker . getValue ();}mBuffer . putLong ( check );- if ( LOG ) Log . i ( TAG , "saveMiniThumbToFile(" + id + ") flag=1, magic="- + magic + ", length=" + data . length + ", check=" + check );-mBuffer . put ( data );mBuffer . flip ();- lock = mChannel . lock ( pos , BYTES_PER_MINTHUMB , false );- mChannel . write ( mBuffer , pos );+ lock = channel . lock ( pos , BYTES_PER_MINTHUMB , false );+ channel . write ( mBuffer , pos );+ if ( LOG ) Log . i ( TAG , "saveMiniThumbToFile(" + id + ") flag=1, magic="+ + magic + ", length=" + data . length + ", check=" + check );}} catch ( IOException ex ) {Log . e ( TAG , "couldn't save mini thumbnail data for "@@ - 319 , 27 + 373 , 29 @@ public class MiniThumbFile {* @return* /public synchronized byte[] getMiniThumbFromFile(long id, byte [] data, ThumbResult result) {- RandomAccessFile r = miniThumbDataFile();+ final long miniThumbDataFileId=getMiniThumbFileId(id);+ RandomAccessFile r = miniThumbDataFile(miniThumbDataFileId,true);if (r == null) return null;- long pos = id * BYTES_PER_MINTHUMB;+ long pos = getPositionInMiniThumbFile(id);+ FileChannel channel=r.getChannel();FileLock lock = null;try {mBuffer.clear();- lock = mChannel.lock(pos, BYTES_PER_MINTHUMB, true);- int size = mChannel.read(mBuffer, pos);+ lock = channel.lock(pos, BYTES_PER_MINTHUMB, true);+ int size = channel.read(mBuffer, pos);if (size > 1 + 8 + 4 + 8) { / / flag , magic , length , check codemBuffer . position ( 0 );byte flag = mBuffer . get ();long magic = mBuffer . getLong ();int length = mBuffer . getInt ();long check = mBuffer . getLong ();- if ( LOG ) Log . i ( TAG , "getMiniThumbFromFile(" + id + ") flag=" + flag- + ", magic=" + magic + ", length=" + length + ", check=" + check );-long newCheck = UNKNOWN_CHECK_CODE ;if ( size >= 1 + 8 + 4 + 8 + length && data . length >= length ) {mBuffer . get ( data , 0 , length );+ Log . i ( TAG , " success to getMiniThumbFromFile(" + id + ") flag=" + flag+ + ", magic=" + magic + ", length=" + length + ", check=" + check );synchronized ( sChecker ) {sChecker . reset ();sChecker . update ( data , 0 , length );
@@ - 1224 , 14 + 1224 , 14 @@ public class MediaScannerc . close ();}- if ( videoCount != 0 ) {+ /*if (videoCount != 0) {String fullPathString = MiniThumbFile.getThumbdataPath(mVideoThumbsUri);existingFiles.remove(fullPathString);}if (imageCount != 0) {String fullPathString = MiniThumbFile.getThumbdataPath(mThumbsUri);existingFiles.remove(fullPathString);- }+ }*/for ( String fileToDelete : existingFiles ) {if ( LOG )Log . v ( TAG , "fileToDelete is " + fileToDelete );@@ - 1247 , 7 + 1247 , 163 @@ public class MediaScannere . printStackTrace ();}}+ /*[robin_20120511*/+ private void pruneDeadMiniThumbnailFiles () {+ HashSet < String > existingFiles = new HashSet < String >();+ String directory = Environment . getExternalStorageDirectory (). toString () + "/DCIM/.thumbnails" ;+ File file = new File ( directory );+ String [] files = ( file ). list ();+ if ( files == null ) {+ files = new String [ 0 ];+ }+ for ( int i = 0 ; i < files . length ; i ++) {+ String fullPathString = directory + "/" + files [ i ];+ existingFiles . add ( fullPathString );+ }+ try {+ Cursor c = mMediaProvider . query (+ mThumbsUri ,+ new String [] { "_data" },+ null ,+ null ,+ null );+ if ( null != c ) {+ if ( c . moveToFirst ()) {+ do {+ String fullPathString = c . getString ( 0 );+ existingFiles . remove ( fullPathString );+ } while ( c . moveToNext ());+ }+ c . close ();+ }+ c = mMediaProvider . query (+ mVideoThumbsUri ,+ new String [] { "_data" },+ null ,+ null ,+ null );+ if ( null != c ) {+ if ( c . moveToFirst ()) {+ do {+ String fullPathString = c . getString ( 0 );+ existingFiles . remove ( fullPathString );+ } while ( c . moveToNext ());+ }+ c . close ();+ }+ String strs [];+ long fileSN ;+ long id0 ;+ long id1 ;+ int blockNum = MiniThumbFile . getMiniThumbDataFileBlockNum ();+ final String selection_IMAGE = " " + Images . Thumbnails . IMAGE_ID + ">=? AND " + Images . Thumbnails . IMAGE_ID + "<?" ;+ final String sortOrder_IMAGE = Images . Thumbnails . IMAGE_ID ;+ final String selection_VIDEO = " " + Video . Thumbnails . VIDEO_ID + ">=? AND " + Video . Thumbnails . VIDEO_ID + "<?" ;+ final String sortOrder_VIDEO = Video . Thumbnails . VIDEO_ID ;+ HashSet < String > fileSet = new HashSet < String >( existingFiles );+ for ( String fileToDelete : fileSet ) {+ strs = MiniThumbFile . parseFileName ( fileToDelete );+ if ( strs == null || strs . length != 4 )+ {+ existingFiles . remove ( fileToDelete );+ continue ;+ }+ if ( "images" . equals ( strs [ 2 ])|| "video" . equals ( strs [ 2 ]))+ {+ fileSN = Long . parseLong ( strs [ 3 ]);+ id0 = blockNum * fileSN ;+ id1 = blockNum *( fileSN + 1 );+ final String [] selectionArgs = new String []{ id0 + "" , id1 + "" };+ if ( "images" . equals ( strs [ 2 ]))+ {++ c = mMediaProvider . query (+ mThumbsUri ,+ new String [] { Images . Thumbnails . IMAGE_ID },+ selection_IMAGE ,+ selectionArgs ,+ sortOrder_IMAGE );+ if ( null != c ) {+ if ( c . getCount ()> 0 ) {++ existingFiles . remove ( fileToDelete );+ }+ c . close ();+ }+ }+ else if ( "video" . equals ( strs [ 2 ]))+ {+ c = mMediaProvider . query (+ mVideoThumbsUri ,+ new String [] { Video . Thumbnails . VIDEO_ID },+ selection_VIDEO ,+ selectionArgs ,+ sortOrder_VIDEO );+ if ( null != c ) {+ if ( c . getCount ()> 0 ) {+ existingFiles . remove ( fileToDelete );+ }+ c . close ();+ }+ }+ }+ else+ {+ existingFiles . remove ( fileToDelete );+ }+ }+ for ( String fileToDelete : existingFiles ) {+ if ( LOG )+ Log . v ( TAG , "fileToDelete is " + fileToDelete );+ try {+ ( new File ( fileToDelete )). delete ();+ } catch ( SecurityException ex ) {+ ex . printStackTrace ();+ }+ }+ file = new File ( directory );+ long freeDiskSpape =( file . getUsableSpace ()>> 20 );+ /*+ * when the free Disk is very low(<100M),delete all MiniThumbFile+ */+ if ( freeDiskSpape < 100 )+ {+ files = ( file ). list ();+ if ( files == null ) {+ files = new String [ 0 ];+ }+ for ( int i = 0 ; i < files . length ; i ++) {+ String fullPathString = directory + "/" + files [ i ];+ existingFiles . add ( fullPathString );+ }+ fileSet = new HashSet < String >( existingFiles );+ for ( String fileToDelete : fileSet ) {+ strs = MiniThumbFile . parseFileName ( fileToDelete );+ if ( strs == null || strs . length != 4 )+ {+ existingFiles . remove ( fileToDelete );+ continue ;+ }+ }+ for ( String fileToDelete : existingFiles ) {+ if ( LOG )+ Log . v ( TAG , "Memeroy is very Low.delete MiniThumbFile:" + fileToDelete );+ try {+ ( new File ( fileToDelete )). delete ();+ } catch ( SecurityException ex ) {+ ex . printStackTrace ();+ }+ }++ }+ Log . v ( TAG , "pruneDeadMiniThumbnailFiles... " + c );+ } catch ( RemoteException e ) {+ /* We will soon be killed...*/+ e . printStackTrace ();+ }+ }+ /*robin_20120511]*/private void postscan ( String [] directories ) throws RemoteException {Iterator < FileCacheEntry > iterator = mFileCache . values (). iterator ();@@ - 1306 , 8 + 1462 , 12 @@ public class MediaScannerif (( mOriginalCount == 0 || mOriginalVideoCount == 0 )&& mImagesUri . equals ( Images . Media . getContentUri ( "external" ))) {pruneDeadThumbnailFiles ();+ } /*[robin_20120511*/+ else if ( mImagesUri . equals ( Images . Media . getContentUri ( "external" )))+ {+ pruneDeadMiniThumbnailFiles ();}-+ /*robin_20120511]*//* allow GC to clean up*/mPlayLists = null ;mFileCache = null ;@@ - 1399 , 7 + 1559 , 12 @@ public class MediaScannerlong prune = System . currentTimeMillis ();pruneDeadThumbnailFiles ();if ( LOG ) Log . d ( TAG , "mtkPostscan: pruneDeadThumbnailFiles takes " + ( System . currentTimeMillis () - prune ) + "ms." );+ } /*[robin_20120511*/+ else if ( mImagesUri . equals ( Images . Media . getContentUri ( "external" )))+ {+ pruneDeadMiniThumbnailFiles ();}+ /*robin_20120511]*/// allow GC to clean upmPlayLists = null;