Android Assets打开调用过程源码分析

一、Android读取Assets中图片并显示

private Bitmap getImageFromAssetsFile(String fileName)
{
    Bitmap image = null;
    AssetManager am = getResources().getAssets();
    if (am == null)
    {
        return null;
    }
    try
    {
        InputStream is = am.open(fileName);
        image = BitmapFactory.decodeStream(is);
        is.close();
    }
    catch (IOException e)
    {
        e.printStackTrace();
    }
    return image;
}
首先是获取AssetManager,然后调用其函数open,打开对应的资源文件

二、open函数追朔(Java层)

AssetManager.java

public final InputStream open(String fileName) throws IOException {
      return open(fileName, ACCESS_STREAMING); //ACCESS_STREAMING = 2
}

open—>

public final InputStream open(String fileName, int accessMode) throws IOException {
synchronized (this) {
   if (!mOpen) {
       throw new RuntimeException("Assetmanager has been closed");
   }
   long asset = openAsset(fileName, accessMode);
   if (asset != 0) {
   AssetInputStream res = new AssetInputStream(asset);
   incRefsLocked(res.hashCode());
   return res;
 }
}
  throw new FileNotFoundException("Asset file: " + fileName);
}

openAsset–>

private native final long openAsset(String fileName, int accessMode);

三、openAsset函数追朔(Native层)

android_util_AssetManager.cpp

static jlong android_content_AssetManager_openAsset(JNIEnv* env, jobject clazz,jstring fileName, jint mode)
{
    AssetManager* am = assetManagerForJavaObject(env, clazz);
    if (am == NULL) {
        return 0;
    }
    ALOGV("openAsset in %p (Java object %p)\n", am, clazz);
    ScopedUtfChars fileName8(env, fileName);
    if (fileName8.c_str() == NULL) {
        jniThrowException(env, "java/lang/IllegalArgumentException", "Empty file name");
        return -1;
    }
    if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM
        && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) {
        jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode");
        return -1;
    }
    Asset* a = am->open(fileName8.c_str(), (Asset::AccessMode)mode);
    if (a == NULL) {
        jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
        return -1;
    }
    //printf("Created Asset Stream: %p\n", a);
    return reinterpret_cast(a);
}

/*
 * Open an asset.
 *
 * The data could be in any asset path. Each asset path could be:
 *  - A directory on disk.
 *  - A Zip archive, uncompressed or compressed.
 *
 * If the file is in a directory, it could have a .gz suffix, meaning it is compressed.
 *
 * We should probably reject requests for "illegal" filenames, e.g. those
 * with illegal characters or "../" backward relative paths.
 */
Asset* AssetManager::open(const char* fileName, AccessMode mode)
{
    AutoMutex _l(mLock); //加锁
    LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager");
    String8 assetName(kAssetsRoot);
    assetName.appendPath(fileName);
    /*
     * For each top-level asset path, search for the asset.
     */
    size_t i = mAssetPaths.size();
    while (i > 0) {
        i--;
        ALOGV("Looking for asset '%s' in '%s'\n",
                assetName.string(), mAssetPaths.itemAt(i).path.string());
        Asset* pAsset = openNonAssetInPathLocked(assetName.string(), mode, mAssetPaths.itemAt(i));
        if (pAsset != NULL) {
            return pAsset != kExcludedAsset ? pAsset : NULL;
        }
    }
    return NULL;
}

/*
 * Open a non-asset file as if it were an asset, searching for it in the
 * specified app.
 *
 * Pass in a NULL values for "appName" if the common app directory should
 * be used.
 */
Asset* AssetManager::openNonAssetInPathLocked(const char* fileName, AccessMode mode,const asset_path& ap)
{
    Asset* pAsset = NULL;
    /* look at the filesystem on disk */
    if (ap.type == kFileTypeDirectory) {
        String8 path(ap.path);
        path.appendPath(fileName);
        pAsset = openAssetFromFileLocked(path, mode);  //处理非压缩文件
        if (pAsset == NULL) {
            /* try again, this time with ".gz" */
            path.append(".gz");
            pAsset = openAssetFromFileLocked(path, mode); //处理非压缩文件失败,加上后缀gz,继续处理
        }
        if (pAsset != NULL) {
            //printf("FOUND NA '%s' on disk\n", fileName);
            pAsset->setAssetSource(path);
        }
    /* look inside the zip file */
    } else {
        String8 path(fileName);
        /* check the appropriate Zip file */
        ZipFileRO* pZip = getZipFileLocked(ap);
        if (pZip != NULL) {
            //printf("GOT zip, checking NA '%s'\n", (const char*) path);
            ZipEntryRO entry = pZip->findEntryByName(path.string());
            if (entry != NULL) {
                //printf("FOUND NA in Zip file for %s\n", appName ? appName : kAppCommon);
                pAsset = openAssetFromZipLocked(pZip, entry, mode, path); //处理zip文件
                pZip->releaseEntry(entry);
            }
        }
        if (pAsset != NULL) {
            /* create a "source" name, for debug/display */
            pAsset->setAssetSource(
                    createZipSourceNameLocked(ZipSet::getPathName(ap.path.string()), String8(""),
                                                String8(fileName)));
        }
    }
    return pAsset;
}

/*
 * Try to open an asset from a file on disk.
 *
 * If the file is compressed with gzip, we seek to the start of the
 * deflated data and pass that in (just like we would for a Zip archive).
 *
 * For uncompressed data, we may already have an mmap()ed version sitting
 * around.  If so, we want to hand that to the Asset instead.
 *
 * This returns NULL if the file doesn't exist, couldn't be opened, or
 * claims to be a ".gz" but isn't.
 */
Asset* AssetManager::openAssetFromFileLocked(const String8& pathName,
    AccessMode mode)
{
    Asset* pAsset = NULL;
    if (strcasecmp(pathName.getPathExtension().string(), ".gz") == 0) {
        //printf("TRYING '%s'\n", (const char*) pathName);
        pAsset = Asset::createFromCompressedFile(pathName.string(), mode);
    } else {
        //printf("TRYING '%s'\n", (const char*) pathName);
        pAsset = Asset::createFromFile(pathName.string(), mode);
    }
    return pAsset;
}

Asset.cpp

/*
 * Create a new Asset from a file on disk.  There is a fair chance that
 * the file doesn't actually exist.
 *
 * We can use "mode" to decide how we want to go about it.
 */
/*static*/ Asset* Asset::createFromFile(const char* fileName, AccessMode mode)
{
    _FileAsset* pAsset;
    status_t result;
    off_t length;
    int fd;
    fd = open(fileName, O_RDONLY | O_BINARY);  //到这里时就调用了open函数,参数为rb
    if (fd < 0)
        return NULL;
    /*
     * Under Linux, the lseek fails if we actually opened a directory.  To
     * be correct we should test the file type explicitly, but since we
     * always open things read-only it doesn't really matter, so there's
     * no value in incurring the extra overhead of an fstat() call.
     */
    length = lseek(fd, 0, SEEK_END);
    if (length < 0) { ::close(fd); return NULL; } (void) lseek(fd, 0, SEEK_SET); pAsset = new _FileAsset; result = pAsset->openChunk(fileName, fd, 0, length);
    if (result != NO_ERROR) {
        delete pAsset;
        return NULL;
    }
    pAsset->mAccessMode = mode;
    return pAsset;
}

/*
 * Operate on a chunk of an uncompressed file.
 *
 * Zero-length chunks are allowed.
 */
status_t _FileAsset::openChunk(const char* fileName, int fd, off_t offset, size_t length)
{
    assert(mFp == NULL);    // no reopen
    assert(mMap == NULL);
    assert(fd >= 0);
    assert(offset >= 0);
    /*
     * Seek to end to get file length.
     */
    off_t fileLength;
    fileLength = lseek(fd, 0, SEEK_END);
    if (fileLength == (off_t) -1) {
        // probably a bad file descriptor
        LOGD("failed lseek (errno=%d)\n", errno);
        return UNKNOWN_ERROR;
    }
    if ((off_t) (offset + length) > fileLength) {
        LOGD("start (%ld) + len (%ld) > end (%ld)\n",(long) offset, (long) length, (long) fileLength);
        return BAD_INDEX;
    }
    /* after fdopen, the fd will be closed on fclose() */
    mFp = fdopen(fd, "rb");   //这里是转换为文件描述指针,第二个参数固定为rb,注意这里的坑
    if (mFp == NULL)
        return UNKNOWN_ERROR;
    mStart = offset;
    mLength = length;
    assert(mOffset == 0);
    /* seek the FILE* to the start of chunk */
    if (fseek(mFp, mStart, SEEK_SET) != 0) {
        assert(false);
    }
    mFileName = fileName != NULL ? strdup(fileName) : NULL;
    
    return NO_ERROR;
}

声明:
本文属于原创,转载请注明来自tasfa.cn ,如有问题请联系 root#tasfa.cn

《Android Assets打开调用过程源码分析》有2个想法

发表评论

电子邮件地址不会被公开。

You must enable javascript to see captcha here!