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

 花开两朵,各表一枝。上回书讲到了数据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());
        }
    }
 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值