安卓AssetManager【一】- 资源的查找过程

发布于:2025-04-14 ⋅ 阅读:(24) ⋅ 点赞:(0)

本文基于安卓11。

安卓应用的资源文件都在编译时通过aapt(frameworks/base/tools/aapt2)工具打包在APK中,安装后保存在userdata分区,当应用需要使用某个资源文件时,通常使用getResources().getString(R.string.name);等方式,R.string.name是一个32位的int类型,由aapt工具生成,保存的是每个资源文件对应的ID,下面就看看安卓是如何通过这个ID获取到对应的数据的。

应用从Java层的AssetManager通过JNI调用到native模块的AssetManager,这里从androidfw模块的AssetManager2开始。

AssetManager2::FindEntry

//AssetManager2.cpp
ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override,
                                         bool /*stop_at_first_match*/,
                                         bool ignore_configuration,
                                         FindEntryResult* out_entry) const {
  if (resource_resolution_logging_enabled_) {
    // Clear the last logged resource resolution.
    ResetResourceResolution();
    last_resolution_.resid = resid;
  }

  // Might use this if density_override != 0.
  ResTable_config density_override_config;

  // Select our configuration or generate a density override configuration.
  const ResTable_config* desired_config = &configuration_;
  if (density_override != 0 && density_override != configuration_.density) {
    density_override_config = configuration_;
    density_override_config.density = density_override;
    desired_config = &density_override_config;
  }

  // Retrieve the package group from the package id of the resource id.
  if (!is_valid_resid(resid)) {
    LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid);
    return kInvalidCookie;
  }

  const uint32_t package_id = get_package_id(resid);
  const uint8_t type_idx = get_type_id(resid) - 1;
  const uint16_t entry_idx = get_entry_id(resid);
  uint8_t package_idx = package_ids_[package_id];
  if (package_idx == 0xff) {
    ANDROID_LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.",
                                             package_id, resid);
    return kInvalidCookie;
  }

  const PackageGroup& package_group = package_groups_[package_idx];
  ApkAssetsCookie cookie = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config,
                                             false /* stop_at_first_match */,
                                             ignore_configuration, out_entry);
  if (UNLIKELY(cookie == kInvalidCookie)) {
    return kInvalidCookie;
  }

  if (!apk_assets_[cookie]->IsLoader()) {
    for (const auto& id_map : package_group.overlays_) {
      auto overlay_entry = id_map.overlay_res_maps_.Lookup(resid);
      if (!overlay_entry) {
        // No id map entry exists for this target resource.
        continue;
      }

      if (overlay_entry.IsTableEntry()) {
        // The target resource is overlaid by an inline value not represented by a resource.
        out_entry->entry = overlay_entry.GetTableEntry();
        out_entry->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
        cookie = id_map.cookie;
        continue;
      }

      FindEntryResult overlay_result;
      ApkAssetsCookie overlay_cookie = FindEntry(overlay_entry.GetResourceId(), density_override,
                                                 false /* stop_at_first_match */,
                                                 ignore_configuration, &overlay_result);
      if (UNLIKELY(overlay_cookie == kInvalidCookie)) {
        continue;
      }

      if (!overlay_result.config.isBetterThan(out_entry->config, desired_config)
          && overlay_result.config.compare(out_entry->config) != 0) {
        // The configuration of the entry for the overlay must be equal to or better than the target
        // configuration to be chosen as the better value.
        continue;
      }

      cookie = overlay_cookie;
      out_entry->entry = std::move(overlay_result.entry);
      out_entry->config = overlay_result.config;
      out_entry->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
      if (resource_resolution_logging_enabled_) {
        last_resolution_.steps.push_back(
            Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result.config.toString(),
                             overlay_result.package_name});
      }
    }
  }

AssetManager2::FindEntry方法接收5个参数,在资源文件中通过resid查找到对应的资源并返回:

  • uint32_t resid:R.java中定义的resid,32位无符号整型,前2位标识package,后2位标识type,最后2位标识entry,例如:0x01040000,0x01表示package,0x04表示type,0x0000标识entry。
  • uint16_t density_override:覆盖屏幕像素密度参数,通常为0u,也就是不覆盖。
  • bool stop_at_first_match:通常为false。
  • bool ignore_configuration:通常为false。
  • FindEntryResult* out_entry:查找的结果。

AssetManager2::FindEntry方法主要执行了3个步骤:

  1. 初始化desired_config变量,desired_configResTable_config类型,ResTable_config从十几个维度定义了一个特定的资源配置,比如:MCC 和 MNC、语言、布局方向、最小宽度、可用宽度和高度、屏幕尺寸、屏幕宽高比等参数,具体可参考About app Resources,还定义了match()isBetterThan()compare()等方法判断资源和配置是否匹配,desired_config一开始被赋值为configuration_,也就是当前AssetManager的资源配置值,如果需要覆盖屏幕像素密度参数,desired_configdensity参数被重新赋值;拆解resid,分别得到package_idtype_idxentry_idx,一个应用可以获取多个package的资源,比如自己APK中的资源,还有系统的公共资源,其保存在framework-res.apk中,对于resid: 0x01040000,其中package_id: 0x1, type_idx: 0x3, entry_idx: 0,通过package_id找到PackageGroup对象,继续在package范围内查找对应的资源。

    ResTable_config

  2. 调用AssetManager2::FindEntryInternal,在package内部开始查找。

  3. 如果这个Asset不是被动态加载的,继续查找overlay资源。

AssetManager2::FindEntryInternal

//AssetManager2.cpp
ApkAssetsCookie AssetManager2::FindEntryInternal(const PackageGroup& package_group,
                                                 uint8_t type_idx, uint16_t entry_idx,
                                                 const ResTable_config& desired_config,
                                                 bool /*stop_at_first_match*/,
                                                 bool ignore_configuration,
                                                 FindEntryResult* out_entry) const {
  ApkAssetsCookie best_cookie = kInvalidCookie;
  const LoadedPackage* best_package = nullptr;
  const ResTable_type* best_type = nullptr;
  const ResTable_config* best_config = nullptr;
  ResTable_config best_config_copy;
  uint32_t best_offset = 0u;
  uint32_t type_flags = 0u;

  Resolution::Step::Type resolution_type = Resolution::Step::Type::NO_ENTRY;
  std::vector<Resolution::Step> resolution_steps;

  // If desired_config is the same as the set configuration, then we can use our filtered list
  // and we don't need to match the configurations, since they already matched.
  const bool use_fast_path = !ignore_configuration && &desired_config == &configuration_;

  const size_t package_count = package_group.packages_.size();
  for (size_t pi = 0; pi < package_count; pi++) {
    const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi];
    const LoadedPackage* loaded_package = loaded_package_impl.loaded_package_;
    ApkAssetsCookie cookie = package_group.cookies_[pi];

    // If the type IDs are offset in this package, we need to take that into account when searching
    // for a type.
    const TypeSpec* type_spec = loaded_package->GetTypeSpecByTypeIndex(type_idx);
    if (UNLIKELY(type_spec == nullptr)) {
      continue;
    }

    // If the package is an overlay or custom loader,
    // then even configurations that are the same MUST be chosen.
    const bool package_is_loader = loaded_package->IsCustomLoader();
    type_flags |= type_spec->GetFlagsForEntryIndex(entry_idx);
    
    if (use_fast_path) {
      const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx];
      const std::vector<ResTable_config>& candidate_configs = filtered_group.configurations;
      const size_t type_count = candidate_configs.size();
      for (uint32_t i = 0; i < type_count; i++) {
        const ResTable_config& this_config = candidate_configs[i];

        // We can skip calling ResTable_config::match() because we know that all candidate
        // configurations that do NOT match have been filtered-out.
        if (best_config == nullptr) {
          resolution_type = Resolution::Step::Type::INITIAL;
        } else if (this_config.isBetterThan(*best_config, &desired_config)) {
          resolution_type = (package_is_loader) ? Resolution::Step::Type::BETTER_MATCH_LOADER
                                                : Resolution::Step::Type::BETTER_MATCH;
        } else if (package_is_loader && this_config.compare(*best_config) == 0) {
          resolution_type = Resolution::Step::Type::OVERLAID_LOADER;
        } else {
          if (resource_resolution_logging_enabled_) {
            resolution_type = (package_is_loader) ? Resolution::Step::Type::SKIPPED_LOADER
                                                  : Resolution::Step::Type::SKIPPED;
            resolution_steps.push_back(Resolution::Step{resolution_type,
                                                        this_config.toString(),
                                                        &loaded_package->GetPackageName()});
          }
          continue;
        }

        // The configuration matches and is better than the previous selection.
        // Find the entry value if it exists for this configuration.
        const ResTable_type* type = filtered_group.types[i];
        const uint32_t offset = LoadedPackage::GetEntryOffset(type, entry_idx);
        if (offset == ResTable_type::NO_ENTRY) {
          if (resource_resolution_logging_enabled_) {
            if (package_is_loader) {
              resolution_type = Resolution::Step::Type::NO_ENTRY_LOADER;
            } else {
              resolution_type = Resolution::Step::Type::NO_ENTRY;
            }
            resolution_steps.push_back(Resolution::Step{resolution_type,
                                                        this_config.toString(),
                                                        &loaded_package->GetPackageName()});
          }
          continue;
        }

        best_cookie = cookie;
        best_package = loaded_package;
        best_type = type;
        best_config = &this_config;
        best_offset = offset;

        if (resource_resolution_logging_enabled_) {
          last_resolution_.steps.push_back(Resolution::Step{resolution_type,
                                                            this_config.toString(),
                                                            &loaded_package->GetPackageName()});
        }
      }
    } else {
      // This is the slower path, which doesn't use the filtered list of configurations.
      // Here we must read the ResTable_config from the mmapped APK, convert it to host endianness
      // and fill in any new fields that did not exist when the APK was compiled.
      // Furthermore when selecting configurations we can't just record the pointer to the
      // ResTable_config, we must copy it.
      const auto iter_end = type_spec->types + type_spec->type_count;
      for (auto iter = type_spec->types; iter != iter_end; ++iter) {
        ResTable_config this_config{};

        if (!ignore_configuration) {
          this_config.copyFromDtoH((*iter)->config);
          if (!this_config.match(desired_config)) {
            continue;
          }

          if (best_config == nullptr) {
            resolution_type = Resolution::Step::Type::INITIAL;
          } else if (this_config.isBetterThan(*best_config, &desired_config)) {
            resolution_type = (package_is_loader) ? Resolution::Step::Type::BETTER_MATCH_LOADER
                                                  : Resolution::Step::Type::BETTER_MATCH;
          } else if (package_is_loader && this_config.compare(*best_config) == 0) {
            resolution_type = Resolution::Step::Type::OVERLAID_LOADER;
          } else {
            continue;
          }
        }

        // The configuration matches and is better than the previous selection.
        // Find the entry value if it exists for this configuration.
        const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, entry_idx);
        if (offset == ResTable_type::NO_ENTRY) {
          continue;
        }

        best_cookie = cookie;
        best_package = loaded_package;
        best_type = *iter;
        best_config_copy = this_config;
        best_config = &best_config_copy;
        best_offset = offset;

        if (ignore_configuration) {
          // Any configuration will suffice, so break.
          break;
        }

        if (resource_resolution_logging_enabled_) {
          last_resolution_.steps.push_back(Resolution::Step{resolution_type,
                                                            this_config.toString(),
                                                            &loaded_package->GetPackageName()});
        }
      }
    }
  }

  if (UNLIKELY(best_cookie == kInvalidCookie)) {
    return kInvalidCookie;
  }

  const ResTable_entry* best_entry = LoadedPackage::GetEntryFromOffset(best_type, best_offset);
  if (UNLIKELY(best_entry == nullptr)) {
    return kInvalidCookie;
  }

  out_entry->entry = ResTable_entry_handle::unmanaged(best_entry);
  out_entry->config = *best_config;
  out_entry->type_flags = type_flags;
  out_entry->package_name = &best_package->GetPackageName();
  out_entry->type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1);
  out_entry->entry_string_ref =
          StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index);
  out_entry->dynamic_ref_table = package_group.dynamic_ref_table.get();

  return best_cookie;
}

PackageGroup结构体变量std::vector<ConfiguredPackage> packages_;是一个数组,保存了多个ConfiguredPackage引用,ConfiguredPackage定义了2个变量:const LoadedPackage* loaded_package_;表示一个apk资源;ByteBucketArray<FilteredConfigGroup> filtered_configs_;保存着匹配着当前AssetManager的配置ResTable_config,以空间换时间,节省后续查找的时间。

ConfiguredPackage

Package

先遍历packages_数组,得到每个ConfiguredPackageloaded_package_LoadedPackage定义了一个apk的资源配置:

LoadedPackage

  • ResStringPool type_string_pool_ : 存储资源类型的名称。例如,资源的类型可能是 stringdrawablelayout 等。
  • ResStringPool key_string_pool_ : 存储具体资源条目的名称(也就是键)。例如,strings.xml 中的每个 <string> 元素的名称(如 "app_name")。
  • std::string package_name_ : 包名。
  • int package_id_ : package_id对应resid的前2位,如0x01040000,package_id就是0x01。
  • int type_id_offset_ : 内存偏移量,一般为0。
  • ByteBucketArray<TypeSpecPtr> type_specs_ : TypeSpecPtr是结构体TypeSpec的指针(using TypeSpecPtr = util::unique_cptr<TypeSpec>;),TypeSpec定义了每一个Type(资源类型),一个Type的所有Entry都存储在同一个内存块中。
  • ByteBucketArray<uint32_t> resource_ids_ : resource_ids_描述了每个Type的Entry数量,比如string类型有100个,drawable类型有80个,代码形式表现为resource_ids_[typeIndex_] = entry_count

ByteBucketArray是模板类,数据结构是数组,ByteBucketArray<TypeSpecPtr>就是TypeSpecPtr类型的数组。

回到AssetManager2::FindEntryInternal方法,在遍历packages_数组后,通过LoadedPackagetype_idx参数得到它指向的TypeSpec对象,loaded_package->GetTypeSpecByTypeIndex(type_idx);

Type

对于每种Type(资源类型)都有多种配置,比如drawable资源有drawabledrawable-hdpidrawable-ldrtl-hdpidrawable-ldrtl-mdpi等,要找到匹配的资源类型,需要比较desired_config和每个Type关联的配置是否匹配,可以通过上文提到的ResTable_config提供的match()isBetterThan()compare()等方法判断。

TypeSpec定义了一个Type(资源类型)结构体。注意命名后缀Spec,Spec是Specification**(规范/规格)** 的缩写,它一般用于表示某种 定义、描述或元数据,而不是实际的数据本身,可以理解为在package和type中的一个抽象类型。ResTable_type(在 types[0] 数组中)才是实际的资源类型数据,它存储了不同配置下的资源内容,type_count表示一个package存在多个的Type(资源类型),比如有layout, drawable,那么type_count的值就是2。

ResTable_Type

ResTable_type才是实际的资源类型数据,定义了identryCountconfigentriesStart等关键数据。

  • header : 头文件。
  • id : type_id对应resid的三四位,如0x01040000,id就是0x04。
  • entryCount : entry数量。
  • entriesStart : 从header数据开始,ResTable_entry 数据的偏移量。
  • config : 特定的资源配置。
  • FLAG_SPARSE : 是否分散保存。

回到AssetManager2::FindEntryInternal方法,遍历TypeSpec的每个ResTable_type对象,比较其关联的config,得到和desired_config最匹配的ResTable_type

找到了匹配的Type后,下一步就是通过Type找到Entry,通过传递过来的entry_idx参数:

const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, entry_idx);

LoadedPackage::GetEntryOffset方法的作用是获取指定资源条目(entry)在资源类型(type_chunk)中的偏移量,首先判断ResTable_type是否有FLAG_SPARSE标志表示分散保存,如果是分散保存,则需要通过二分搜索找到对应的entry,否则简单多了,找到头文件之后的内存指针,后面的内存保存着uint32_t类型的偏移量数据。

//LoadedArsc.cpp
uint32_t LoadedPackage::GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index) {
    const size_t offsets_offset = dtohs(type_chunk->header.headerSize);
    const uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>(
    reinterpret_cast<const uint8_t*>(type_chunk) + offsets_offset);
  	return dtohl(entry_offsets[entry_index]);
}
  1. reinterpret_cast<const uint8_t*>(type_chunk) 先将结构体指针重新解释为字节指针,type_chunk 是指向 ResTable_type 结构的指针,而结构体指针一般会按结构体的大小和对齐方式对齐,这对于我们后续的内存访问和计算偏移并不方便,通过将 type_chunk 转换为 uint8_t*,我们将其视为一个字节数组,这样就可以按 字节 访问内存。

  2. reinterpret_cast<const uint8_t*>(type_chunk) + offsets_offsettype_chunk*指针位置偏移offsets_offset个字节。

  3. reinterpret_cast<const uint32_t*>(reinterpret_cast<const uint8_t*>(type_chunk) + offsets_offset)将其重新解释为uint32_t*类型的数据,在内存中,偏移量通常是以 4 字节为单位存储的。entry_offsets 存储了一组 偏移量,每个偏移量对应一个资源条目(例如,资源ID,图像,字符串等)。entry_offsets[entry_index] 便是 资源条目对应的偏移量

  4. dtohl(entry_offsets[entry_index])将网络字节序(大端字节序)转换为主机字节序(小端字节序)。

好了,现在确定了ResTable_type对象,和Entry的偏移量,接下来就是查找Entry对象了。

Entry

查找获取Entry对象:const ResTable_entry* best_entry = LoadedPackage::GetEntryFromOffset(best_type, best_offset);,其中best_type是最匹配的Type,ResTable_type对象,best_offset是Entry的偏移量。

//LoadedArsc.cpp
const ResTable_entry* LoadedPackage::GetEntryFromOffset(const ResTable_type* type_chunk,
                                                        uint32_t offset) {
  return reinterpret_cast<const ResTable_entry*>(reinterpret_cast<const uint8_t*>(type_chunk) +
                                                 offset + dtohl(type_chunk->entriesStart));
}

type_chunk*指针位置偏移offset个字节,然后加上type_chunk->entriesStart偏移量,就是ResTable_entry对象的内存数据了。

内存结构如下:

ResTable_type trunk

ResTable_type->header.headerSize表示ResTable_type结构体的大小,ResTable_type->entriesStart表示ResTable_type结构体和Entry offSets的大小。

回到LoadedPackage::GetEntryFromOffset,将对应的内存数据重新解释为ResTable_entry类型。

ResTable_entry

ResTable_entry定义了每个资源条目的数据结构。

  • size : 数据大小,字节数。
  • flags : FLAG_COMPLEX 或 FLAG_PUBLIC 或 FLAG_WEAK。
  • key : ResStringPool_ref类型,已一个uint32_t类型的index指向stringPool中的某个string,这里保存了每个entry对应的的值。

总结一下,ResTable_config从十几个维度定义了一个特定的资源配置,先是通过package_idx确定了PackageGroup,遍历PackageGroup中的所有LoadedPackage,然后通过type_idx找到LoadedPackage中保存的TypeSpec对象,比较desired_configTypeSpec关联的ResTable_config,找到最匹配的TypeSpec,最后通过entry_idx找到对应的内存地址,转换解释为ResTable_entry对象。

返回结果

回到AssetManager2::FindEntryInternal,找到了对应的Entry后通过FindEntryResult对象返回。

//AssetManager2.cpp
  const ResTable_entry* best_entry = LoadedPackage::GetEntryFromOffset(best_type, best_offset);

  out_entry->entry = ResTable_entry_handle::unmanaged(best_entry);
  out_entry->config = *best_config;
  out_entry->type_flags = type_flags;
  out_entry->package_name = &best_package->GetPackageName();
  out_entry->type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1);
  out_entry->entry_string_ref =
          StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index);
  out_entry->dynamic_ref_table = package_group.dynamic_ref_table.get();

FindEntryResult

FindEntryResultentryResTable_entry_handle类型,共享指针指向best_entry

FindEntryResult指针保存结果返回给JNI调用者。

//AssetManager2.cpp
ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag,
                                           uint16_t density_override, Res_value* out_value,
                                           ResTable_config* out_selected_config,
                                           uint32_t* out_flags) const {
    FindEntryResult entry;
  	ApkAssetsCookie cookie = FindEntry(resid, density_override, false /* stop_at_first_match */,
                                     false /* ignore_configuration */, &entry);
    const ResTable_entry* table_entry = *entry.entry;
    const Res_value* device_value = reinterpret_cast<const Res_value*>(
    reinterpret_cast<const uint8_t*>(table_entry) + dtohs(table_entry->size));
  	out_value->copyFrom_dtoh(*device_value);
    return cookie;
}

reinterpret_cast<const Res_value*>(reinterpret_cast<const uint8_t*>(table_entry) + dtohs(table_entry->size))ResTable_entry内存地址后size个字节的数据重新解释装换为Res_value对象,size的值是ResTable_entry结构体的内存大小。

ResTable_entry trunk

Res_value类定义:

Res_value

JNI的NativeGetResourceValue方法将CPP中的Res_value对象转换为Java中的TypedValue对象:

//android_util_AssetManager.cpp
static jint NativeGetResourceValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid,
                                   jshort density, jobject typed_value,
                                   jboolean resolve_references) {
    ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
  	Res_value value;
  	ResTable_config selected_config;
  	uint32_t flags;
  	ApkAssetsCookie cookie =
      	assetmanager->GetResource(static_cast<uint32_t>(resid), false /*may_be_bag*/,
                                static_cast<uint16_t>(density), &value, &selected_config, &flags);
    uint32_t ref = static_cast<uint32_t>(resid);
    return CopyValue(env, cookie, value, ref, flags, &selected_config, typed_value);
}

CopyValue将Res_value对象数据复制给TypedValue对象:

//android_util_AssetManager.cpp
static jint CopyValue(JNIEnv* env, ApkAssetsCookie cookie, const Res_value& value, uint32_t ref,
                      uint32_t type_spec_flags, ResTable_config* config, jobject out_typed_value) {
  env->SetIntField(out_typed_value, gTypedValueOffsets.mType, value.dataType);
  env->SetIntField(out_typed_value, gTypedValueOffsets.mAssetCookie,
                   ApkAssetsCookieToJavaCookie(cookie));
  env->SetIntField(out_typed_value, gTypedValueOffsets.mData, value.data);
  env->SetObjectField(out_typed_value, gTypedValueOffsets.mString, nullptr);
  env->SetIntField(out_typed_value, gTypedValueOffsets.mResourceId, ref);
  env->SetIntField(out_typed_value, gTypedValueOffsets.mChangingConfigurations, type_spec_flags);
  if (config != nullptr) {
    env->SetIntField(out_typed_value, gTypedValueOffsets.mDensity, config->density);
  }
  return static_cast<jint>(ApkAssetsCookieToJavaCookie(cookie));
}
  • gTypedValueOffsets.mType : TypedValue.type
  • gTypedValueOffsets.mData : TypedValue.data

Java层的AssetManager通过TypedValue找到entry对应的值。

//AssetManger.java
	boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue,
            boolean resolveRefs) {
        final int cookie = nativeGetResourceValue(
                    mObject, resId, (short) densityDpi, outValue, resolveRefs);
        if (outValue.type == TypedValue.TYPE_STRING) {
                outValue.string = getPooledStringForCookie(cookie, outValue.data);
        }
    }

判断type是否为ypedValue.TYPE_STRING,如果是的话getPooledStringForCookie(cookie, outValue.data);,cookie是上文AssetManager2::FindEntryInternal方法返回的best_cookieusing ApkAssetsCookie = int32_t;一个整型变量关联一个LoadedPackage,相当于LoadedPackage的索引。

//AssetManger.java
	CharSequence getPooledStringForCookie(int cookie, int id) {
        // Cookies map to ApkAssets starting at 1.
        return getApkAssets()[cookie - 1].getStringFromPool(id);
    }

通过cookie索引到具体的ApkAssets对象,然后通过outValue.data在字符串池中获取对应的值。

getApkAssets()返回一个ApkAssets[]数组,一个应用可能有多个ApkAssets,比如下面的应用com.example.myapplication,3个systemApkAssets和1个mUserApkAssets,返回的ApkAssets[]大小为4。

01-01 07:24:10.137  1143  1283 D AssetManager: AssetManager systemApkAssets: 0, /system/framework/framework-res.apk
01-01 07:24:10.137  1143  1283 D AssetManager: AssetManager systemApkAssets: 1, /vendor/overlay/framework-res__auto_generated_rro_vendor.apk
01-01 07:24:10.137  1143  1283 D AssetManager: AssetManager systemApkAssets: 2, /system/product/overlay/framework-res__auto_generated_rro_product.apk
01-01 07:24:10.137  1143  1283 D AssetManager: AssetManager mUserApkAssets: 0, /data/app/~~YZ0ivppt_GbGfdxqixf45Q==/com.example.myapplication-_rJfRy7PAecqI4zoHuVdbA==/base.apk


网站公告

今日签到

点亮在社区的每一天
去签到