Android はリソースのメモリ データの場所を見つけます

  Android は、テーマ内の対応する属性の結果、または設定したスタイルの結果を取得します。この記事で GetBag(uint32_t resid, std::vector<uint32_t>& child_resids) と書かれている場合、FindEntry(resid, 0u /*) が渡されます。 density_override / , false / stop_at_first_match /, false / ignore_configuration */) は、ResTable_map_entry 構造体のデータ ポインターを取得します。このポインタは、メモリ内の Bag リソースの場所です。
  リソースを検索するとき、32 ビット整数に基づいてクエリが実行されることがわかっています。この 32 ビット整数は 3 つの部分に分割され、最初の 8 ビットはパッケージ ID、システム リソース パッケージ ID は 01、アプリケーション リソース パッケージ ID は 0x7F、次の 8 ビットはタイプ ID、タイプ ID です。は 1 から始まるため、リソースを検索する場合、配列シーケンスとして使用する場合は、最初に 1 ずつデクリメントする必要があります。最後の 16 ビットは、entry_idx と呼ばれる、対応するタイプのリソース収集プロセスでの出現シーケンスです。
  リソースを検索するプロセスでは、まずパッケージ ID を通じて対応するパッケージを見つけます。パッケージ ID は 0 から増加しないため、見てわかるように、パッケージ ID と対応する配列シーケンスの間のマッピングを維持する必要があります。次のコードから、型 id を渡し、パッケージ内で対応する型を見つけ、最後に、entry_idx を通じてこの型の対応するリソースを見つけます。

資産管理者2

  前回の記事では、AssetManager2 にはアプリケーションのすべてのリソース コンテンツが含まれているとも述べました。このクラスを理解する必要があります

class AssetManager2 {
    
    
	…………
  // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must
  // have a longer lifetime.
  std::vector<const ApkAssets*> apk_assets_;

  // DynamicRefTables for shared library package resolution.
  // These are ordered according to apk_assets_. The mappings may change depending on what is
  // in apk_assets_, therefore they must be stored in the AssetManager and not in the
  // immutable ApkAssets class.
  std::vector<PackageGroup> package_groups_;

  // An array mapping package ID to index into package_groups. This keeps the lookup fast
  // without taking too much memory.
  std::array<uint8_t, std::numeric_limits<uint8_t>::max() + 1> package_ids_;

  // The current configuration set for this AssetManager. When this changes, cached resources
  // may need to be purged.
  ResTable_config configuration_;

  // Cached set of bags. These are cached because they can inherit keys from parent bags,
  // which involves some calculation.
  mutable std::unordered_map<uint32_t, util::unique_cptr<ResolvedBag>> cached_bags_;

  // Cached set of bag resid stacks for each bag. These are cached because they might be requested
  // a number of times for each view during View inspection.
  mutable std::unordered_map<uint32_t, std::vector<uint32_t>> cached_bag_resid_stacks_;

  // Cached set of resolved resource values.
  mutable std::unordered_map<uint32_t, SelectedValue> cached_resolved_values_;
  …………
}  	

  メンバー apk_assets_ は std::vector<const ApkAssets*> であり、ApkAssets が Apk ファイルに対応します。APK ファイルのリソースを解析するとき、APK ファイルは ApkAssets オブジェクトに解析されます。
  package_groups_ は std::vector であり、apk_assets_ によって再処理されます。
  package_ids_ には、パッケージ ID と上記の package_groups_ 内の順序との間のマッピングが格納されます。
  configuration_ は、ResTable_config で記述される現在の AssetManager の構成情報です。変更されると、キャッシュされたリソースを破棄する必要がある場合があります。
  キャッシュに使用されるメンバー名から次の3つのメンバーが分かります。

パッケージグループ

  PackageGroup については、まだ話し合う必要があります。その構造を見てください

  // A collection of configurations and their associated ResTable_type that match the current
  // AssetManager configuration.
  struct FilteredConfigGroup {
    
    
      std::vector<const TypeSpec::TypeEntry*> type_entries;
  };

  // Represents an single package.
  struct ConfiguredPackage {
    
    
      // A pointer to the immutable, loaded package info.
      const LoadedPackage* loaded_package_;

      // A mutable AssetManager-specific list of configurations that match the AssetManager's
      // current configuration. This is used as an optimization to avoid checking every single
      // candidate configuration when looking up resources.
      ByteBucketArray<FilteredConfigGroup> filtered_configs_;
  };

  // Represents a Runtime Resource Overlay that overlays resources in the logical package.
  struct ConfiguredOverlay {
    
    
      // The set of package groups that overlay this package group.
      IdmapResMap overlay_res_maps_;

      // The cookie of the overlay assets.
      ApkAssetsCookie cookie;
  };
  
  // Represents a logical package, which can be made up of many individual packages. Each package
  // in a PackageGroup shares the same package name and package ID.
  struct PackageGroup {
    
    
      // The set of packages that make-up this group.
      std::vector<ConfiguredPackage> packages_;

      // The cookies associated with each package in the group. They share the same order as
      // packages_.
      std::vector<ApkAssetsCookie> cookies_;

      // Runtime Resource Overlays that overlay resources in this package group.
      std::vector<ConfiguredOverlay> overlays_;

      // A library reference table that contains build-package ID to runtime-package ID mappings.
      std::shared_ptr<DynamicRefTable> dynamic_ref_table = std::make_shared<DynamicRefTable>();
  };

  各 PackageGroup はパッケージ ID に対応します。複数のパッケージのリソースが含まれる場合があり、packages_ のタイプは std::vector で、ConfiguredPackage の各インスタンスはパッケージのリソースに対応します。これらのパッケージの id 値は同じです。
  cookies_ は、対応する ConfiguredPackage がどの APK からのものかを記述するために使用されます。その順序は、packages_ の順序に対応します。
  overlays_ は、実行中のリソース マスクである RRO に関連しています。
  Dynamic_ref_table これは、構築パッケージ ID から実行パッケージ ID へのマッピングを含むライブラリ参照テーブルです。
  ConfiguredPackage 構造体をもう一度見ると、loaded_pa​​ckage_ と filtered_configs_ という 2 つのメンバーがあります。
  loaded_pa​​ckage_ は LoadedPackage* で、パッケージ内のリソース情報を指します。
  そして、filtered_configs_ は最適化のためのもので、現在の構成情報に従って、一致するすべてのリソースがそれに入れられます。このようにして、一致しないリソース情報を照会する必要はありません。filtered_configs_ は ByteBucketArray タイプであり、内部の順序は (typeid -1) であり、リソースの各タイプに対応します。後で呼び出すときの便宜上、当面はリソースの最適化割り当てと呼ぶことにします。

FindEntry()

  FindEntry() について説明します。以下のセクションを読んでください。

base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry(
    uint32_t resid, uint16_t density_override, bool stop_at_first_match,
    bool ignore_configuration) const {
    
    
    …………
      // 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;
  }

   パラメータ resid は照会するリソース ID、density_override は満たすべき画面密度、0 の場合は要件なし、stop_at_first_match は最初の一致が見つかった場合に停止するかどうか、ignore_configuration は設定を無視します。
   最初のコードは、パラメータdensity_overrideを処理するものです。density_overrideが0ではなく、現在のAssetManager2構成configuration_の画面密度と一致しない場合は、desired_configの画面密度をdensity_overrideに設定します。他の構成にはconfiguration_がかかります。
  次のコードを見てください。

  …………
  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 (UNLIKELY(package_idx == 0xff)) {
    
    
    ANDROID_LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.",
                                             package_id, resid);
    return base::unexpected(std::nullopt);
  }

  const PackageGroup& package_group = package_groups_[package_idx];
  auto result = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config,
                                  stop_at_first_match, ignore_configuration);  

  このブロックは、package_id、type_idx、entry_idx を取得するために resid の処理を​​開始します。これについては以前に説明しました。対応する値はそれぞれシフト演算を通じて取得されます。type_idx はシフト演算後のマイナス 1 演算です。typeid は 1 から始まるので。後で配列のシーケンスを検索するため、1 を減算する必要があります。
  次に、対応する PackageGroup を見つける方法は、対応するパッケージ ID の順序が package_ids_ に格納され、対応する package_group が package_groups_[package_idx] を通じて取得されます。
  次に、FindEntryInternal()を呼び出して、FindEntryResult オブジェクトの結果を取得します。以下でこれについて説明してから、コードを見てみましょう。

	…………
  if (!stop_at_first_match && !ignore_configuration && !apk_assets_[result->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.IsInlineValue()) {
    
    
        // The target resource is overlaid by an inline value not represented by a resource.
        result->entry = overlay_entry.GetInlineValue();
        result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
        result->cookie = id_map.cookie;
			…………
        continue;
      }

      auto overlay_result = FindEntry(overlay_entry.GetResourceId(), density_override,
                                      false /* stop_at_first_match */,
                                      false /* ignore_configuration */);
      if (UNLIKELY(IsIOError(overlay_result))) {
    
    
        return base::unexpected(overlay_result.error());
      }
      if (!overlay_result.has_value()) {
    
    
        continue;
      }

      if (!overlay_result->config.isBetterThan(result->config, desired_config)
          && overlay_result->config.compare(result->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;
      }
      result->cookie = overlay_result->cookie;
      result->entry = overlay_result->entry;
      result->config = overlay_result->config;
      result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
		…………
    }
  }
	…………

  return result;
}	

  このコードは主に RRO の存在について扱っているため、詳細は説明しませんが、FindEntry() を呼び出して結果を返していることがわかります。
  FindEntry()が完了しました。

ResTable_type

  FindEntryInternal() について話す前に、検索時に密接に関連する ResTable_type について話す必要があります。

/**
 * Header that appears at the front of every data chunk in a resource.
 */
struct ResChunk_header
{
    
    
    // Type identifier for this chunk.  The meaning of this value depends
    // on the containing chunk.
    uint16_t type;

    // Size of the chunk header (in bytes).  Adding this value to
    // the address of the chunk allows you to find its associated data
    // (if any).
    uint16_t headerSize;

    // Total size of this chunk (in bytes).  This is the chunkSize plus
    // the size of any data associated with the chunk.  Adding this value
    // to the chunk allows you to completely skip its contents (including
    // any child chunks).  If this value is the same as chunkSize, there is
    // no data associated with the chunk.
    uint32_t size;
};		
		…………
struct ResTable_type
{
    
    
    struct ResChunk_header header;

    enum {
    
    
        NO_ENTRY = 0xFFFFFFFF
    };
    
    // The type identifier this chunk is holding.  Type IDs start
    // at 1 (corresponding to the value of the type bits in a
    // resource identifier).  0 is invalid.
    uint8_t id;
    
    enum {
    
    
        // If set, the entry is sparse, and encodes both the entry ID and offset into each entry,
        // and a binary search is used to find the key. Only available on platforms >= O.
        // Mark any types that use this with a v26 qualifier to prevent runtime issues on older
        // platforms.
        FLAG_SPARSE = 0x01,
    };
    uint8_t flags;

    // Must be 0.
    uint16_t reserved;
    
    // Number of uint32_t entry indices that follow.
    uint32_t entryCount;

    // Offset from header where ResTable_entry data starts.
    uint32_t entriesStart;

    // Configuration this collection of entries is designed for. This must always be last.
    ResTable_config config;
};

  ResTable_type データ構造は、メモリ内のデータ内容に対応します。
  ヘッダーは ResChunk_header 構造体です。ResChunk_header は、各データ テーブルの先頭のコンテンツです。ResChunk_header の type はテーブル タイプを表し、headerSize は ResTable_type データ構造のメモリ サイズ、size は ResTable_type データ構造 + 関連データ コンテンツのサイズです。
  ResTable_type の ID はタイプ ID です。カウントは1から始まります。
  ResTable_typeのentryCountは、このタイプのリソースのエントリ数を表し、
  ResTable_typeのentryStartは、データの先頭から構造体の先頭までのオフセットです。
  ResTable_type の config は、このタイプの関連構成情報です。
  以下の図を見てください。これは、メモリ内の構造に関連するデータのレイアウトです。
ResTable_type メモリの説明

メモリ内の ResTable_type 関連データのレイアウト
  ResTable_type が先頭に表示されます。これに関連するエントリのオフセット値であるentryCount整数が続きます。これらのオフセット値はentriesStartを基準にしています。検索時には、まず typeidx を通じて対応する ResTable_type を取得し、次に、entryidx を通じてオフセット値を取得し、次にオフセット値を通じて特定のデータ内容を取得します。

  図からも通常リソースとバッグリソースに分かれていることがわかります。通常のリソースは、文字列型などの比較的単純なリソースです。バッグ リソースは、スタイル タイプ リソースなどのより複雑なリソースであり、xml ファイルに項目属性コンテンツの多くの項目が含まれます。
  ResTable_map_entry および ResTable_map データ構造は、テーマ内の対応する属性の結果、または Android で設定したスタイルの結果を取得します。
  ResTable_type は、リソースの特定の構成に関連しています。ドローアブル タイプと同様に、mdpi と hdpi は 2 つの ResTable_type で表されます。

   FindEntryInternal() と言って開始します

FindEntryInternal()

   このメソッドは、リソースのコアを見つけるためのメソッドであり、リソース検索を処理するときに構成を比較する方法、最適化された filtered_configs_ をいつ使用するか、メモリ内の対応するリソースを見つける方法について説明します。
   次に、コードを確認するか、セクションごとに確認します。

base::expected<FindEntryResult, NullOrIOError> 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) const {
    
    
  const bool logging_enabled = resource_resolution_logging_enabled_;
  ApkAssetsCookie best_cookie = kInvalidCookie;
  const LoadedPackage* best_package = nullptr;
  incfs::verified_map_ptr<ResTable_type> best_type;
  const ResTable_config* best_config = nullptr;
  uint32_t best_offset = 0U;
  uint32_t type_flags = 0U;

  std::vector<Resolution::Step> resolution_steps;

  // If `desired_config` is not the same as the set configuration or the caller will accept a value
  // from any configuration, then we cannot use our filtered list of types since it only it contains
  // types matched to the set configuration.
  const bool use_filtered = !ignore_configuration && &desired_config == &configuration_;    

   パラメータはすべて FindEntry() から渡されるため、これについては説明しません。
  いくつかの変数が定義されており、best_cookie はリソースがどの ApkAssets に由来するかを表し、best_package はパッケージに対応する LoadedPackage のリソースを表し、best_type はどの ResTable_type に由来するかを表し、best_config はどの構成に由来するかを表し、best_offset はresource は、前の ResTable_type 構造に関連しており、type_flags は、リソースによって影響を受ける構成要素を表します。
  また、最適化された構成リソースを使用する変数 use_filtered も定義します (前述したように、ConfiguredPackage の filtered_configs_ を使用して検索します)。この変数が設定を無視せず、現在の AssetManager2 設定情報とまったく同じ設定が見つかると期待される場合、最適化された設定リソース情報を使用して、対応するリソースを検索します。
  次の段落

  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_;
    const 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;
    }

    // Allow custom loader packages to overlay resource values with configurations equivalent to the
    // current best configuration.
    const bool package_is_loader = loaded_package->IsCustomLoader();

    auto entry_flags = type_spec->GetFlagsForEntryIndex(entry_idx);
    if (UNLIKELY(!entry_flags.has_value())) {
    
    
      return base::unexpected(entry_flags.error());
    }
    type_flags |= entry_flags.value();

    const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx];
    const size_t type_entry_count = (use_filtered) ? filtered_group.type_entries.size()
                                                   : type_spec->type_entries.size();

  サイクルに入り始めます。これは、同じパッケージ ID を持つすべてのパッケージです。
  まず、LoadedPackage型のloaded_pa​​ckage を取得し、次にその GetTypeSpecByTypeIndex(type_idx) メソッドを呼び出して、TypeSpec型 type_spec を取得します。loaded_pa​​ckage には、解析された APK ファイルのリソース テーブル (resources.arsc) 内のすべてのリソース情報が含まれています。取得されたtype_specは、typeidxに対応するタイプを含む構成情報およびリソースデータ情報を指す。その構造を見てください

// TypeSpec is going to be immediately proceeded by
// an array of Type structs, all in the same block of memory.
struct TypeSpec {
    
    
  struct TypeEntry {
    
    
    incfs::verified_map_ptr<ResTable_type> type;

    // Type configurations are accessed frequently when setting up an AssetManager and querying
    // resources. Access this cached configuration to minimize page faults.
    ResTable_config config;
  };

  // Pointer to the mmapped data where flags are kept. Flags denote whether the resource entry is
  // public and under which configurations it varies.
  incfs::verified_map_ptr<ResTable_typeSpec> type_spec;

  std::vector<TypeEntry> type_entries;
	…………
};  

  その type_spec は incfs::verified_map_ptr<ResTable_typeSpec> タイプで、エントリが影響を受ける構成が含まれます。entryidx を通じて、対応するリソースが影響を受けることを示すフラグを取得できます。type_entries は TypeEntry のコレクションであり、各 TypeEntry オブジェクトには incfs::verified_map_ptr<ResTable_type> タイプが含まれます。ResTable_type については前に説明しました。
  次にコードを確認し、type_spec->GetFlagsForEntryIndex(entry_idx) を呼び出して、リソース構成に影響するフラグを取得します。複数のパッケージがある場合は、影響を受けるすべてのフラグが収集されます。
  次に、前の変数 use_filtered に従って、最適化された構成によって収集されたリソースを使用してリソースを見つけることができるかどうかが判断されます。
  最適な構成リソース コレクションを使用する場合は、filtered_group.type_entries を取得します。それ以外の場合は、type_spec->type_entries を取得して、対応するタイプのリソースの数である type_entry_count の数値を取得します。filtered_group.type_entries には現在の構成を満たすすべてのタイプ リソースが含まれ、type_spec->type_entries にはすべてのタイプ リソースが含まれます。したがって、filtered_group.type_entries を使用すると、不要な比較オーバーヘッドを大幅に節約できますが、これはパラメータに応じて判断する必要があります。
  次に、コードを見てみましょう。

    for (size_t i = 0; i < type_entry_count; i++) {
    
    
      const TypeSpec::TypeEntry* type_entry = (use_filtered) ? filtered_group.type_entries[i]
                                                             : &type_spec->type_entries[i];

      // We can skip calling ResTable_config::match() if the caller does not care for the
      // configuration to match or if we're using the list of types that have already had their
      // configuration matched.
      const ResTable_config& this_config = type_entry->config;
      if (!(use_filtered || ignore_configuration || this_config.match(desired_config))) {
    
    
        continue;
      }
		…………
      // The configuration matches and is better than the previous selection.
      // Find the entry value if it exists for this configuration.
      const auto& type = type_entry->type;
      const auto offset = LoadedPackage::GetEntryOffset(type, entry_idx);
      …………
      best_cookie = cookie;
      best_package = loaded_package;
      best_type = type;
      best_config = &this_config;
      best_offset = offset.value();
      …………
      // Any configuration will suffice, so break.
      if (stop_at_first_match) {
    
    
        break;
      }
    }
  }            

  タイプリソースの数が取得されると、ループが開始されます。まず、前述したように、type_entry (TypeSpec::TypeEntry*) を取得します。次に、その構成 this_config を取得します。
  次に判定があり、3 つの条件がすべて false であれば、そのまま次のサイクル比較に入ります。use_filtered は最適化された構成リソースを使用すること、ignore_configuration は構成を無視すること、3 番目は this_config.match(desired_config) です。これは、このリソースの現在の構成が予想される構成と一致しています。これら 3 つの条件のいずれも満たされない場合、現在のタイプのリソースは破棄され、次のタイプのリソースが直接比較されます。どちらかが満たされた場合は、下方向に実行します。
  次に、前述の ResTable_type ポインターであるタイプを取得し、LoadedPackage::GetEntryOffset(type,entry_idx) を通じてオフセットを取得します。これは、ResTable_type について説明したときに前述したエントリのオフセットです。
  下に進むと、適切な結果が見つかったことを意味し、best_cookie、best_package、best_type、best_config、および best_offset を割り当てます。
  stop_at_first_match パラメータが true に設定されているかどうかを確認します。設定されている場合は、ループの最初の層から抜け出します。設定されていない場合は、2 層サイクルが終了するまでサイクルを続けます。
  2 層ループの内容が完了しました。引き続きコードを見てください。

	…………
  auto best_entry_result = LoadedPackage::GetEntryFromOffset(best_type, best_offset);
  …………
  const incfs::map_ptr<ResTable_entry> best_entry = *best_entry_result;
  …………
  const auto entry = GetEntryValue(best_entry.verified());
  …………
  return FindEntryResult{
    
    
    .cookie = best_cookie,
    .entry = *entry,
    .config = *best_config,
    .type_flags = type_flags,
    .package_name = &best_package->GetPackageName(),
    .type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1),
    .entry_string_ref = StringPoolRef(best_package->GetKeyStringPool(),
                                      best_entry->key.index),
    .dynamic_ref_table = package_group.dynamic_ref_table.get(),
  };
}

  この最後のコード部分が処理結果です。
  LoadedPackage::GetEntryFromOffset(best_type, best_offset) は、ResTable_entry ポインター best_entry_result を取得します。これは、上で説明した ResTable_type データ構造です。entriesStart に対する相対的なオフセットを通じて、ResTable_entry の位置が見つかります。
  次に、GetEntryValue(best_entry.verified()) を通じて EntryValue タイプのエントリを取得します。関連するコードを見てください。

using EntryValue = std::variant<Res_value, incfs::verified_map_ptr<ResTable_map_entry>>;

base::expected<EntryValue, IOError> GetEntryValue(
    incfs::verified_map_ptr<ResTable_entry> table_entry) {
    
    
  const uint16_t entry_size = dtohs(table_entry->size);

  // Check if the entry represents a bag value.
  if (entry_size >= sizeof(ResTable_map_entry) &&
      (dtohs(table_entry->flags) & ResTable_entry::FLAG_COMPLEX)) {
    
    
    const auto map_entry = table_entry.convert<ResTable_map_entry>();
    if (!map_entry) {
    
    
      return base::unexpected(IOError::PAGES_MISSING);
    }
    return map_entry.verified();
  }

  // The entry represents a non-bag value.
  const auto entry_value = table_entry.offset(entry_size).convert<Res_value>();
  if (!entry_value) {
    
    
    return base::unexpected(IOError::PAGES_MISSING);
  }
  Res_value value;
  value.copyFrom_dtoh(entry_value.value());
  return value;
}

  EntryValue の可能な値のタイプは Res_value または incfs::verified_map_ptr<ResTable_map_entry> であることがわかります。リソースが Bag リソースの場合は incfs::verified_map_ptr<ResTable_map_entry> になり、通常のリソースの場合は incfs::verified_map_ptr<ResTable_map_entry> になります。 Res_value になります。また、ResTable_entryクラスのメンバ変数flagにResTable_entry::FLAG_COMPLEXがある場合、Bagリソースとなることがわかります。
  上記の最後のコードをもう一度見て、最後に結果として FindEntryResult 構造体をアセンブルして返します。そのメンバー type_string_ref とentry_string_ref を見てください。type_string_ref は、LoadedPackage の Type Type に対応する ResStringPool であり、entry_string_ref は、LoadedPackage の Key に対応する ResStringPool です。そして、メンバーのdynamic_ref_tableは、対応するpackage_group.dynamic_ref_tableを受け取ります。
  以上でFindEntry()関数は終了です。

おすすめ

転載: blog.csdn.net/q1165328963/article/details/124838192
おすすめ