Android插件换肤之AssetManager相关源码分析

学习自

https://www.jianshu.com/p/5eac94143bd6


本文可不看 没啥意义


AssetManager.mObject是native层的AssetManager

     /**
     * Create a new AssetManager containing only the basic system assets.
     * Applications will not generally use this method, instead retrieving the
     * appropriate asset manager with {@link Resources#getAssets}.    Not for
     * use by applications.
     * {@hide}
     */
    public AssetManager() {
        synchronized (this) {
            if (DEBUG_REFS) {
                mNumRefs = 0;
                incRefsLocked(this.hashCode());
            }
            init(false);
            if (localLOGV) Log.v(TAG, "New asset manager: " + this);
            ensureSystemAssets();
        }
    }

   // ndk的源码路径
   // frameworks/base/core/jni/android_util_AssetManager.cpp
   // frameworks/base/libs/androidfw/AssetManager.cpp
   private native final void init(boolean isSystem); 


对应的native的方法为

static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem)
{
    if (isSystem) {
        verifySystemIdmaps();
    }
    //  AssetManager.cpp
    AssetManager* am = new AssetManager();
    if (am == NULL) {
        jniThrowException(env, "java/lang/OutOfMemoryError", "");
        return;
    }

    am->addDefaultAssets();

    ALOGV("Created AssetManager %p for Java object %p\n", am, clazz);
    env->SetLongField(clazz, gAssetManagerOffsets.mObject, reinterpret_cast<jlong>(am));
}

bool AssetManager::addDefaultAssets()
{
    const char* root = getenv("ANDROID_ROOT");
    LOG_ALWAYS_FATAL_IF(root == NULL, "ANDROID_ROOT not set");

    String8 path(root);
    // framework/framework-res.apk  
    // 初始化的时候会去加载系统的framework-res.apk资源
    // 也就是说我们为什么能加载系统的资源如颜色、图片、文字等等
    path.appendPath(kSystemAssets);

    return addAssetPath(path, NULL);
}


addAssetPath

bool AssetManager::addAssetPath(const String8& path, int32_t* cookie)//path就是我们zip的路径
{
    asset_path ap;

    // 省略一些校验代码

    // judge cache
    for (size_t i=0; i<mAssetPaths.size(); i++) {
        if (mAssetPaths[i].path == ap.path) {
            if (cookie) {
                *cookie = static_cast<int32_t>(i+1);
            }
            return true;
        }
    }

    // 检查路径是否有一个androidmanifest . xml
    Asset* manifestAsset = const_cast<AssetManager*>(this)->openNonAssetInPathLocked(
            kAndroidManifest, Asset::ACCESS_BUFFER, ap);
    if (manifestAsset == NULL) {
        // 如果不包含任何资源
        delete manifestAsset;
        return false;
    }
    delete manifestAsset;
    // 添加 
    mAssetPaths.add(ap);

    // 新路径总是补充到最后
    if (cookie) {
        *cookie = static_cast<int32_t>(mAssetPaths.size());
    }

    if (mResources != NULL) {
        appendPathToResTable(ap);
    }

    return true;
}

bool AssetManager::appendPathToResTable(const asset_path& ap) const {
    // skip those ap's that correspond to system overlays
    if (ap.isSystemOverlay) {
        return true;
    }

    Asset* ass = NULL;
    ResTable* sharedRes = NULL;
    bool shared = true;
    bool onlyEmptyResources = true;
    MY_TRACE_BEGIN(ap.path.string());
    // 资源覆盖机制,暂不考虑
    Asset* idmap = openIdmapLocked(ap);
    size_t nextEntryIdx = mResources->getTableCount();
    ALOGV("Looking for resource asset in '%s'\n", ap.path.string());
    // 资源包路径不是一个文件夹,那就是一个apk文件了
    if (ap.type != kFileTypeDirectory) {
        // 对于app来说,第一次执行时,肯定为0,因为mResources刚创建,还没对其操作
        // 下面的分支 指挥在参数是系统资源包路径时,才执行,
        // 而且系统资源包路径是首次被解析的
        // 第二次执行appendPathToResTable,nextEntryIdx就不会为0了
        if (nextEntryIdx == 0) {
            // mAssetPaths中存储的第一个资源包路径是系统资源的路径,
            // 即framework-res.apk的路径,它在zygote启动时已经加载了
            // 可以通过mZipSet.getZipResourceTable获得其ResTable对象
            sharedRes = const_cast<AssetManager*>(this)->
                mZipSet.getZipResourceTable(ap.path);
            // 对于APP来说,肯定不为NULL
            if (sharedRes != NULL) {
                // 得到系统资源包路径中resources.arsc个数
                nextEntryIdx = sharedRes->getTableCount();
            }
        }
        // 当参数是mAssetPaths中除第一个以外的其他资源资源包路径,
        // 比如app自己的资源包路径时,走下面的逻辑
        if (sharedRes == NULL) {
            // 检查该资源包是否被其他进程加载了,这与ZipSet数据结构有关,后面在详细介绍
            ass = const_cast<AssetManager*>(this)->
                mZipSet.getZipResourceTableAsset(ap.path);
            // 对于app自己的资源包来说,一般都会都下面的逻辑
            if (ass == NULL) {
                ALOGV("loading resource table %s\n", ap.path.string());
                // 创建Asset对象,就是打开resources.arsc
                ass = const_cast<AssetManager*>(this)->
                    openNonAssetInPathLocked("resources.arsc",
                                             Asset::ACCESS_BUFFER,
                                             ap);
                if (ass != NULL && ass != kExcludedAsset) {
                    ass = const_cast<AssetManager*>(this)->
                        mZipSet.setZipResourceTableAsset(ap.path, ass);
                }
            }
            // 只有在zygote启动时,才会执行下面的逻辑
            // 为系统资源创建 ResTable,并加入到mZipSet里。
            if (nextEntryIdx == 0 && ass != NULL) {
                // If this is the first resource table in the asset
                // manager, then we are going to cache it so that we
                // can quickly copy it out for others.
                ALOGV("Creating shared resources for %s", ap.path.string());
                // 创建ResTable对象,并把前面与resources.arsc关联的Asset对象,加入到这个ResTabl中
                sharedRes = new ResTable();
                sharedRes->add(ass, idmap, nextEntryIdx + 1, false);
                sharedRes = const_cast<AssetManager*>(this)->
                    mZipSet.setZipResourceTable(ap.path, sharedRes);
            }
        }
    } else {
        ALOGV("loading resource table %s\n", ap.path.string());
        ass = const_cast<AssetManager*>(this)->
            openNonAssetInPathLocked("resources.arsc",
                                     Asset::ACCESS_BUFFER,
                                     ap);
        shared = false;
    }

    if ((ass != NULL || sharedRes != NULL) && ass != kExcludedAsset) {
        ALOGV("Installing resource asset %p in to table %p\n", ass, mResources);
        // 系统资源包时
        if (sharedRes != NULL) {
            ALOGV("Copying existing resources for %s", ap.path.string());
            mResources->add(sharedRes);
        } else {
            // 非系统资源包时,将与resources.arsc关联的Asset对象加入到Restable中
            // 此过程会解析resources.arsc文件。
            ALOGV("Parsing resources for %s", ap.path.string());
            mResources->add(ass, idmap, nextEntryIdx + 1, !shared);
        }
        onlyEmptyResources = false;

        if (!shared) {
            delete ass;
        }
    } else {
        mResources->addEmpty(nextEntryIdx + 1);
    }

    if (idmap != NULL) {
        delete idmap;
    }
    MY_TRACE_END();

    return onlyEmptyResources;
}

resources.arsc这玩意是资源索引,我们找到资源,都是靠他

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值