读书人

galler3d的源码分析数据起源

发布时间: 2012-11-22 00:16:41 作者: rapoo

galler3d的源码分析——数据来源

我们这里主要讲本地sd卡的数据,pisaca看情况后续再作分析。

数据操作设计的类包括:CacheService,MediaFeed,LocalDataSource,DiskCache,MediaItem, MediaSet,MediaClustering。

数据操作包括几个方面:寻找媒体源(图片、视频),cache媒体源,将cache数据导出,将cache的媒体渲染到屏幕。

首先是找图片或者视频:

1. 如果当前cache文件不存在,或者当前cache文件记录的语言、国家和变量代码与系统的不一致的场景时,首先将所有cache文件(index和chunk文件)删除,接着将语言、国家和变量代码写入local-album-cachechunk0的文件中。

2. 启动一个线程,寻找图片或者视频,并将相册信息cache。

3. 再启动一个线程,将图片缩略图化,并cache。

4. 如果上一次还有一部分数据cache,执行步骤2和3。

5. 如果这一次有新的媒体文件需要cache,启动一个线程,将新的媒体文件cache。再执行步骤3。

关键的代码如下:

@Override    protected void onHandleIntent(final Intent intent) {        Log.i(TAG, "CacheService, onHandleIntent, Starting CacheService");        if (Environment.getExternalStorageState() == Environment.MEDIA_BAD_REMOVAL) {            sAlbumCache.deleteAll();            putLocaleForAlbumCache(Locale.getDefault());        }        Locale locale = getLocaleForAlbumCache();        if (locale != null && locale.equals(Locale.getDefault())) {            // The cache is in the same locale as the system locale.            if (!isCacheReady(false)) {                // The albums and their items have not yet been cached, we need                // to run the service.            Log.i(TAG, "CacheService, onHandleIntent, start new cache thread");                startNewCacheThread();            } else {            Log.i(TAG, "CacheService, onHandleIntent, start new cache thread for dirty sets");                startNewCacheThreadForDirtySets();            }        } else {            // The locale has changed, we need to regenerate the strings.        Log.i(TAG, "CacheService, onHandleIntent, delete all");            sAlbumCache.deleteAll();            putLocaleForAlbumCache(Locale.getDefault());            startNewCacheThread();        }        if (intent.getBooleanExtra("checkthumbnails", false)) {        Log.i(TAG, "CacheService, onHandleIntent, start new thumbnail thread");            startNewThumbnailThread(this);        } else {            final Thread existingThread = THUMBNAIL_THREAD.getAndSet(null);            if (existingThread != null) {                existingThread.interrupt();            }        }    }

先看这个函数:


private static final Locale getLocaleForAlbumCache() {    Log.i(TAG, "CacheService, getLocaleForAlbumCache");        final byte[] data = sAlbumCache.get(ALBUM_CACHE_LOCALE_INDEX, 0);        if (data != null && data.length > 0) {            ByteArrayInputStream bis = new ByteArrayInputStream(data);            DataInputStream dis = new DataInputStream(bis);            try {                String country = Utils.readUTF(dis);                if (country == null)                    country = "";                String language = Utils.readUTF(dis);                if (language == null)                    language = "";                String variant = Utils.readUTF(dis);                if (variant == null)                    variant = "";                final Locale locale = new Locale(language, country, variant);                dis.close();                bis.close();                return locale;            } catch (IOException e) {                // Could not read locale in cache.                Log.i(TAG, "Error reading locale from cache.");                return null;            }        }        return null;    }


我们看到 sAlbumCache这个变量,这个变量的定义是这样的:

public static final DiskCache sAlbumCache = new DiskCache("local-album-cache");

我们来看看DiskCache的构造函数做了什么?

public DiskCache(String cacheDirectoryName) {        String cacheDirectoryPath = CacheService.getCachePath(cacheDirectoryName);        // Create the cache directory if needed.        File cacheDirectory = new File(cacheDirectoryPath);        if (!cacheDirectory.isDirectory() && !cacheDirectory.mkdirs()) {            Log.e(TAG, "Unable to create cache directory " + cacheDirectoryPath);        }        mCacheDirectoryPath = cacheDirectoryPath;        loadIndex();    }


public static final String getCachePath(final String subFolderName) {        return Environment.getExternalStorageDirectory() + "/Android/data/com.luowei.media/cache/" + subFolderName;    }

原来如此,在sd卡建立了一个目录/Android/data/com.luowei.media/cache/local-album-cache。

建立目录后呢?

private void loadIndex() {        final String indexFilePath = getIndexFilePath();        try {            // Open the input stream.            final FileInputStream fileInput = new FileInputStream(indexFilePath);            final BufferedInputStream bufferedInput = new BufferedInputStream(fileInput, 1024);            final DataInputStream dataInput = new DataInputStream(bufferedInput);            // Read the header.            final int magic = dataInput.readInt();//4            final int version = dataInput.readInt();//4            boolean valid = true;            if (magic != INDEX_HEADER_MAGIC) {                Log.e(TAG, "Index file appears to be corrupt (" + magic + " != " + INDEX_HEADER_MAGIC + "), " + indexFilePath);                valid = false;            }            if (valid && version != INDEX_HEADER_VERSION) {                // Future versions can implement upgrade in this case.                Log.e(TAG, "Index file version " + version + " not supported");                valid = false;            }            if (valid) {                mTailChunk = dataInput.readShort();//2            }            // Read the entries.            if (valid) {                // Parse the index file body into the in-memory map.                final int numEntries = dataInput.readInt();//4                mIndexMap = new LongSparseArray<Record>(numEntries);                synchronized (mIndexMap) {                    for (int i = 0; i < numEntries; ++i) {                        final long key = dataInput.readLong();//8                        final int chunk = dataInput.readShort();//2                        final int offset = dataInput.readInt();//4                        final int size = dataInput.readInt();//4                        final int sizeOnDisk = dataInput.readInt();//4                        final long timestamp = dataInput.readLong();//8                        mIndexMap.append(key, new Record(chunk, offset, size, sizeOnDisk, timestamp));                    }                }            }            dataInput.close();            if (!valid) {                deleteAll();            }        } catch (FileNotFoundException e) {            // If the file does not exist the cache is empty, so just continue.        } catch (IOException e) {            Log.e(TAG, "Unable to read the index file " + indexFilePath);        } finally {            if (mIndexMap == null) {                mIndexMap = new LongSparseArray<Record>();            }        }    }

首先找索引文件,索引文件路径是什么?


 private String getIndexFilePath() {        return mCacheDirectoryPath + INDEX_FILE_NAME;    }


具体路径是 sd卡下/Android/data/com.luowei.media/cache/local-album-cacheindex。如果文件不存在,就只做一件事,将mIndexMap初始化;如果文件存在,读取文件头,如果文件头是00 00 CA FE 00 00 00 02这个格式,说明是标准的索引文件,继续分析此文件,将数据写入mIndexMap映射表。

接着继续分析getLocaleForAlbumCache函数,sAlbumCache.get做了什么?

public byte[] get(long key, long timestamp) {        // Look up the record for the given key.        Record record = null;        synchronized (mIndexMap) {            record = mIndexMap.get(key);        }        if (record != null) {            // Read the chunk from the file.            if (record.timestamp != timestamp) {                return null;            }            try {                RandomAccessFile chunkFile = getChunkFile(record.chunk);                if (chunkFile != null) {                    byte[] data = new byte[record.size];                    chunkFile.seek(record.offset);                    chunkFile.readFully(data);                    return data;                }            } catch (Exception e) {                Log.e(TAG, "Unable to read from chunk file");            }        }        return null;    }

如果mIndexMap没有ALBUM_CACHE_LOCALE_INDEX这个key的数据,返回null;如果有,接着比较。如果记录的时间戳也不一致,返回null。那getChunkFile又有什么呢?

private RandomAccessFile getChunkFile(int chunk) {        RandomAccessFile chunkFile = null;        synchronized (mChunkFiles) {            chunkFile = mChunkFiles.get(chunk);        }        if (chunkFile == null) {            final String chunkFilePath = mCacheDirectoryPath + CHUNK_FILE_PREFIX + chunk;                        Log.i(TAG, "DiskCache, chunkFilePath:"+chunkFilePath);            try {                chunkFile = new RandomAccessFile(chunkFilePath, "rw");            } catch (FileNotFoundException e) {                Log.e(TAG, "Unable to create or open the chunk file " + chunkFilePath);            }            synchronized (mChunkFiles) {                mChunkFiles.put(chunk, chunkFile);            }        }        return chunkFile;    }

mChunkFiles也是一个chunk映射表,local-album-cache对应的chunk文件路径对应为/Android/data/com.luowei.media/cache/local-album-cachechunk0。如果chunk文件不存在,需要建立这个文件,同时将这个文件加入mChunkFiles映射表。

回到getLocaleForAlbumCache这个函数,发现这个chunk文件的头记录语言、地区和变量代码。再返回onHandleIntent函数,如果getLocaleForAlbumCache取回的代码是空的或者跟本机的代码不一致,就需要做三件事情:


1. 将/Android/data/com.luowei.media/cache/local-album-cache目录下所有文件,其实这里面有个bug,请读者自行发现;


2. 创建chunk和index文件,并将语言、地区和变量代码写入chunk文件,这就是putLocaleForAlbumCache函数所作的事情;


3. 开启新的cache线程:startNewCacheThread。

读书人网 >移动开发

热点推荐