gallery3d的源码分析-数据涞源2

来源:互联网 发布:ConcurrentHashMap源码 编辑:程序博客网 时间:2024/05/12 16:05
 花开两朵,各表一枝。上回书讲到了数据cache的一些流程。这回书要说的就是gallery3d刚开始在手机上运行cache数据的流程。
 
如果你切换手机的语言,跟此流程一致。这个在上回书也提到了。
 
首先来看看startNewCacheThread函数:
 
private void startNewCacheThread() {        restartThread(CACHE_THREAD, "CacheRefresh", new Runnable() //开启线程,如果之前有同样的线程已经存在,关闭之前线程        {            public void run() {                refresh(CacheService.this);//cache媒体数据            }        });    }
restartThread函数的源代码如下,不细说。
private static final void restartThread(final AtomicReference<Thread> threadRef, final String name, final Runnable action) {        // Create a new thread.        final Thread newThread = new Thread() {            public void run() {                try {                    action.run();                } finally {                    threadRef.compareAndSet(this, null);                }            }        };        newThread.setName(name);        newThread.start();
        // Interrupt any existing thread.        final Thread existingThread = threadRef.getAndSet(newThread);        if (existingThread != null) {            existingThread.interrupt();        }    }
接下来分析关键的refresh函数:
 
 private final static void refresh(final Context context) {        // First we build the album cache.        // This is the meta-data about the albums / buckets on the SD card.        Log.i(TAG, "Refreshing cache.");        sAlbumCache.deleteAll();         putLocaleForAlbumCache(Locale.getDefault());        final ArrayList<MediaSet> sets = new ArrayList<MediaSet>();        LongSparseArray<MediaSet> acceleratedSets = new LongSparseArray<MediaSet>();        Log.i(TAG, "Building albums.");        final Uri uriImages = Images.Media.EXTERNAL_CONTENT_URI.buildUpon().appendQueryParameter("distinct", "true").build();        final Uri uriVideos = Video.Media.EXTERNAL_CONTENT_URI.buildUpon().appendQueryParameter("distinct", "true").build();        final ContentResolver cr = context.getContentResolver();        try {            final Cursor cursorImages = cr.query(uriImages, BUCKET_PROJECTION_IMAGES, null, null, DEFAULT_BUCKET_SORT_ORDER);//搜索图像            final Cursor cursorVideos = cr.query(uriVideos, BUCKET_PROJECTION_VIDEOS, null, null, DEFAULT_BUCKET_SORT_ORDER);//搜索视频            Cursor[] cursors = new Cursor[2];            cursors[0] = cursorImages;            cursors[1] = cursorVideos;            final SortCursor sortCursor = new SortCursor(cursors, Images.ImageColumns.BUCKET_DISPLAY_NAME, SortCursor.TYPE_STRING,                    true);            try {                if (sortCursor != null && sortCursor.moveToFirst()) {                    sets.ensureCapacity(sortCursor.getCount());                    acceleratedSets = new LongSparseArray<MediaSet>(sortCursor.getCount());                    MediaSet cameraSet = new MediaSet();                    cameraSet.mId = LocalDataSource.CAMERA_BUCKET_ID;//相机的bucket id                    cameraSet.mName = context.getResources().getString(R.string.camera);                    sets.add(cameraSet);                    acceleratedSets.put(cameraSet.mId, cameraSet);//将相机的照片集加入acceleratedSets映射表                    do {                        if (Thread.interrupted()) {                            return;                        }                        long setId = sortCursor.getLong(BUCKET_ID_INDEX);                        MediaSet mediaSet = findSet(setId, acceleratedSets);                        if (mediaSet == null) {//如果在acceleratedSets映射表找不到setId, 那么加入acceleratedSets映射表                            mediaSet = new MediaSet();                            mediaSet.mId = setId;                            mediaSet.mName = sortCursor.getString(BUCKET_NAME_INDEX);                            sets.add(mediaSet);                            acceleratedSets.put(setId, mediaSet);                        }                        mediaSet.mHasImages |= (sortCursor.getCurrentCursorIndex() == 0);                        mediaSet.mHasVideos |= (sortCursor.getCurrentCursorIndex() == 1);                    } while (sortCursor.moveToNext());                    sortCursor.close();                }            } finally {                if (sortCursor != null)                    sortCursor.close();            }            sAlbumCache.put(ALBUM_CACHE_INCOMPLETE_INDEX, sDummyData, 0);//写入dummy data到ALBUM_CACHE_INCOMPLETE_INDEX块,说明目前cache开始            writeSetsToCache(sets);//cache媒体集            Log.i(TAG, "Done building albums.");            // Now we must cache the items contained in every album / bucket.            populateMediaItemsForSets(context, sets, acceleratedSets, false);//将media item归类到sets,并将item信息cache        } catch (Exception e) {            // If the database operation failed for any reason.            ;        }        sAlbumCache.delete(ALBUM_CACHE_INCOMPLETE_INDEX);//删除ALBUM_CACHE_INCOMPLETE_INDEX块,说明目前cache技术    }
 
CAMERA_BUCKET_ID的定义是这样的,其实就是相机目录的hash码。
 
public static final int CAMERA_BUCKET_ID = getBucketId(CAMERA_BUCKET_NAME);
 
    public static final String CAMERA_BUCKET_NAME = Environment.getExternalStorageDirectory().toString() + "/DCIM/" + CAMERA_STRING;//相机目录
 
public static int getBucketId(String path) {        return (path.toLowerCase().hashCode());    }
 
下面是writeSetsToCache函数的功能,就是cache相册信息:
 
private static final void writeSetsToCache(final ArrayList<MediaSet> sets) {        final ByteArrayOutputStream bos = new ByteArrayOutputStream();        final int numSets = sets.size();        final DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(bos, 256));        try {            dos.writeInt(numSets);            for (int i = 0; i < numSets; ++i) {                if (Thread.interrupted()) {                    return;                }                final MediaSet set = sets.get(i);                dos.writeLong(set.mId);//相册id                Utils.writeUTF(dos, set.mName);//相册名称                dos.writeBoolean(set.mHasImages);//是否是图片集                dos.writeBoolean(set.mHasVideos);//是否是视频集            }            dos.flush();            sAlbumCache.put(ALBUM_CACHE_METADATA_INDEX, bos.toByteArray(), 0);//写chunk文件            dos.close();            if (numSets == 0) {                sAlbumCache.deleteAll();                putLocaleForAlbumCache(Locale.getDefault());            }            sAlbumCache.flush();//写index文件        } catch (IOException e) {            Log.e(TAG, "Error writing albums to diskcache.");            sAlbumCache.deleteAll();            putLocaleForAlbumCache(Locale.getDefault());        }    }
关键函数sAlbumCache.put(ALBUM_CACHE_METADATA_INDEX, bos.toByteArray(), 0),我们来看看put函数做了些什么?
 
public void put(long key, byte[] data, long timestamp) {        // Check to see if the record already exists.        Record record = null;        Log.i(TAG, "DiskCache, put key:"+key);        synchronized (mIndexMap) {            record = mIndexMap.get(key);        }        if (record != null && data.length <= record.sizeOnDisk) {//如果记录在mIndexMap已经存在,替换当前记录            // We just replace the chunk.            int currentChunk = record.chunk;            try {            Log.i(TAG, "DiskCache, get chunk file");                RandomAccessFile chunkFile = getChunkFile(record.chunk);//获取chunk文件                if (chunkFile != null) {                    chunkFile.seek(record.offset);                    chunkFile.write(data);                    synchronized (mIndexMap) {                        mIndexMap.put(key, new Record(currentChunk, record.offset, data.length, record.sizeOnDisk, timestamp));//更改mIndexMap的key的记录数据                    }                    return;                }            } catch (Exception e) {                Log.e(TAG, "Unable to read from chunk file");            }        }        //如果记录不存在,将新的chunk添加到当前chunk尾部        // Append a new chunk to the current chunk.        final int chunk = mTailChunk;        final RandomAccessFile chunkFile = getChunkFile(chunk);        if (chunkFile != null) {            try {               //将数据写入chunk文件                final int offset = (int) chunkFile.length();                chunkFile.seek(offset);                chunkFile.write(data);                synchronized (mIndexMap) {                    mIndexMap.put(key, new Record(chunk, offset, data.length, data.length, timestamp));//将新纪录添加到mIndexMap                }                if (offset + data.length > CHUNK_SIZE) {                    ++mTailChunk;                }                if (++mNumInsertions == 64) { // CR: 64 => constant                    // Flush the index file at a regular interval. To avoid                    // writing the entire                    // index each time the format could be changed to an                    // append-only journal with                    // a snapshot generated on exit.                    flush();//写64次后,写索引文件                }            } catch (IOException e) {                Log.e(TAG, "Unable to write new entry to chunk file");            }        } else {            Log.e(TAG, "getChunkFile() returned null");        }    }
 
put函数就是把相册集的信息写入chunk文件。
那继续看sAlbumCache.flush()函数:
 
public void flush() {        if (mNumInsertions != 0) {            mNumInsertions = 0;            writeIndex();        }    }
 
 
 
    private void writeIndex() {        final String indexFilePath = getIndexFilePath();        try {            // Create a temporary file to write the index into.            File tempFile = File.createTempFile("DiskCacheIndex", null);//创建临时索引文件            final FileOutputStream fileOutput = new FileOutputStream(tempFile);            final BufferedOutputStream bufferedOutput = new BufferedOutputStream(fileOutput, 1024);            final DataOutputStream dataOutput = new DataOutputStream(bufferedOutput);            // Write the index header.            final int numRecords = mIndexMap.size();            dataOutput.writeInt(INDEX_HEADER_MAGIC);//索引文件头部的关键字            dataOutput.writeInt(INDEX_HEADER_VERSION);//索引文件头部的关键字            dataOutput.writeShort(mTailChunk);//尾部chunk索引数            dataOutput.writeInt(numRecords);//记录条数            // Write the records.            //将chunk文件信息写入索引文件            for (int i = 0; i < numRecords; ++i) {                final long key = mIndexMap.keyAt(i);                final Record record = mIndexMap.valueAt(i);                dataOutput.writeLong(key);                dataOutput.writeShort(record.chunk);                dataOutput.writeInt(record.offset);                dataOutput.writeInt(record.size);                dataOutput.writeInt(record.sizeOnDisk);                dataOutput.writeLong(record.timestamp);            }            // Close the file.            dataOutput.close();            Log.d(TAG, "Wrote index with " + numRecords + " records.");            // Atomically overwrite the old index file.            tempFile.renameTo(new File(indexFilePath));//将临时索引文件重命名        } catch (IOException e) {            Log.e(TAG, "Unable to write the index file " + indexFilePath);        }    }
 
再回到refresh函数,populateMediaItemsForSets做了哪些事情?
 
private final static void populateMediaItemsForSets(final Context context, final ArrayList<MediaSet> sets,            final LongSparseArray<MediaSet> acceleratedSets, boolean useWhere) {        if (sets == null || sets.size() == 0 || Thread.interrupted()) {            return;        }        Log.i(TAG, "Building items.");        final Uri uriImages = Images.Media.EXTERNAL_CONTENT_URI;        final Uri uriVideos = Video.Media.EXTERNAL_CONTENT_URI;        final ContentResolver cr = context.getContentResolver();        String whereClause = null;        if (useWhere) {            int numSets = sets.size();            StringBuffer whereString = new StringBuffer(Images.ImageColumns.BUCKET_ID + " in (");            for (int i = 0; i < numSets; ++i) {                whereString.append(sets.get(i).mId);                if (i != numSets - 1) {                    whereString.append(",");                }            }            whereString.append(")");            whereClause = whereString.toString();            Log.i(TAG, "Updating dirty albums where " + whereClause);        }        try {            final Cursor cursorImages = cr.query(uriImages, PROJECTION_IMAGES, whereClause, null, DEFAULT_IMAGE_SORT_ORDER);            final Cursor cursorVideos = cr.query(uriVideos, PROJECTION_VIDEOS, whereClause, null, DEFAULT_VIDEO_SORT_ORDER);            final Cursor[] cursors = new Cursor[2];            cursors[0] = cursorImages;            cursors[1] = cursorVideos;            final SortCursor sortCursor = new SortCursor(cursors, Images.ImageColumns.DATE_TAKEN, SortCursor.TYPE_NUMERIC, true);            if (Thread.interrupted()) {                return;            }            try {                if (sortCursor != null && sortCursor.moveToFirst()) {                    final int count = sortCursor.getCount();                    final int numSets = sets.size();                    final int approximateCountPerSet = count / numSets;                    for (int i = 0; i < numSets; ++i) {                        final MediaSet set = sets.get(i);                        set.setNumExpectedItems(approximateCountPerSet);                    }                    do {                        if (Thread.interrupted()) {                            return;                        }                        final MediaItem item = new MediaItem();                        final boolean isVideo = (sortCursor.getCurrentCursorIndex() == 1);                        if (isVideo) {                            populateVideoItemFromCursor(item, cr, sortCursor, CacheService.BASE_CONTENT_STRING_VIDEOS);//获取视频item的信息                        } else {                            populateMediaItemFromCursor(item, cr, sortCursor, CacheService.BASE_CONTENT_STRING_IMAGES);//获取图片item的信息                        }                        final long setId = sortCursor.getLong(MEDIA_BUCKET_ID_INDEX);                        final MediaSet set = findSet(setId, acceleratedSets);                        if (set != null) {                            set.addItem(item);//将item加入相册集                        }                    } while (sortCursor.moveToNext());                }            } finally {                if (sortCursor != null)                    sortCursor.close();            }        } catch (Exception e) {            // If the database operation failed for any reason            ;        }        if (sets.size() > 0) {            writeItemsToCache(sets);//缓存sets的item            Log.i(TAG, "Done building items.");        }    }
 
关键函数writeItemsToCache就是缓存每个set的items。
 
private static final void writeItemsToCache(final ArrayList<MediaSet> sets) {        final int numSets = sets.size();        for (int i = 0; i < numSets; ++i) {            if (Thread.interrupted()) {                return;            }            writeItemsForASet(sets.get(i));        }        sAlbumCache.flush();    }

 

 

writeItemsForASet就是将item的信息写入chunk文件,sAlbumCache.flush就是将chunk文件信息写入index文件。
 
private static final void writeItemsForASet(final MediaSet set) {        final ByteArrayOutputStream bos = new ByteArrayOutputStream();        final DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(bos, 256));        try {            final ArrayList<MediaItem> items = set.getItems();            final int numItems = items.size();            dos.writeInt(numItems);            dos.writeLong(set.mMinTimestamp);            dos.writeLong(set.mMaxTimestamp);            for (int i = 0; i < numItems; ++i) {                MediaItem item = items.get(i);                if (set.mId == LocalDataSource.CAMERA_BUCKET_ID || set.mId == LocalDataSource.DOWNLOAD_BUCKET_ID) {                    // Reverse the display order for the camera bucket - want                    // the latest first.                    item = items.get(numItems - i - 1);                }                dos.writeLong(item.mId);                Utils.writeUTF(dos, item.mCaption);                Utils.writeUTF(dos, item.mMimeType);                dos.writeInt(item.getMediaType());                dos.writeDouble(item.mLatitude);                dos.writeDouble(item.mLongitude);                dos.writeLong(item.mDateTakenInMs);                dos.writeBoolean(item.mTriedRetrievingExifDateTaken);                dos.writeLong(item.mDateAddedInSec);                dos.writeLong(item.mDateModifiedInSec);                dos.writeInt(item.mDurationInSec);                dos.writeInt((int) item.mRotation);                Utils.writeUTF(dos, item.mFilePath);            }            dos.flush();            sAlbumCache.put(set.mId, bos.toByteArray(), 0);            dos.close();        } catch (IOException e) {            Log.e(TAG, "Error writing to diskcache for set " + set.mName);            sAlbumCache.deleteAll();            putLocaleForAlbumCache(Locale.getDefault());        }    }
 
原创粉丝点击