mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Generate keep.xml to prevent resource shrinking on Android (#50620)
Summary: On Android, when resource shrinking is enabled, all resources added by Metro may be removed as react-native is accessing resources based on strings rather than references, so AGP can't see its usage. Example output of `android/app/build/outputs/mapping/release/resources.txt `when `shrinkResources` is enabled. ``` raw/__node_modules_expo_vectoricons_build_vendor_reactnativevectoricons_fonts_materialcommunityicons : reachable=false drawable/__common_assets_haptics_icon : reachable=false ``` It’s a coincidence that most of the resources are currently working, as many file names begin with strings that already exist in the String Pool. For example, `node_modules...` is flagged as used because 'node' is present in the String Pool, causing it to be whitelisted. However, this does not guarantee that the same will apply to all files - especially in a monorepo setup, where paths are significantly different. For example * `__node_modules_expo_vectoricons_build_vendor_reactnativevectoricons_fonts_materialcommunityicons` * `__common_assets_haptics_icon` To prevent that behavior, metro during assets export should create `keep.xml` listing all resources generated by metro. https://developer.android.com/build/shrink-code#keep-resources We have already made a similar change in expo cli: https://github.com/expo/expo/pull/35465 ## Changelog: [ANDROID][ADDED] - Generate keep.xml to prevent resource shrinking Pull Request resolved: https://github.com/facebook/react-native/pull/50620 Test Plan: 1. Enable resource shrinking in RNTester by adding this to `android.buildTypes.release` to `packages/rn-tester/android/app/build.gradle.kts` ```gradle isMinifyEnabled = true isShrinkResources = true ``` 2. Use some resources in playground, for example: ```diff diff --git a/packages/rn-tester/js/examples/Playground/RNTesterPlayground.js b/packages/rn-tester/js/examples/Playground/RNTesterPlayground.js index 9dbacb99701..9ac9c231f3f 100644 --- a/packages/rn-tester/js/examples/Playground/RNTesterPlayground.js +++ b/packages/rn-tester/js/examples/Playground/RNTesterPlayground.js @@ -11,16 +11,14 @@ import type {RNTesterModuleExample} from '../../types/RNTesterTypes'; -import RNTesterText from '../../components/RNTesterText'; import * as React from 'react'; import {StyleSheet, View} from 'react-native'; +import {Header} from "react-native/Libraries/NewAppScreen"; function Playground() { return ( <View style={styles.container}> - <RNTesterText> - Edit "RNTesterPlayground.js" to change this file - </RNTesterText> + <Header /> </View> ); } ``` 3. Build app using `hermesRelease` variant 4. See Playground screen | Before | After | |---|---| | <img width="488" alt="Zrzut ekranu 2025-04-10 o 12 17 53" src="https://github.com/user-attachments/assets/24fcaa7b-6ddb-4ba6-9fe5-65c27bcbc931" /> | <img width="488" alt="Zrzut ekranu 2025-04-10 o 12 15 58" src="https://github.com/user-attachments/assets/09a1ce77-be26-4571-a4b8-c466bf19e026" /> | 5. Inspect `packages/rn-tester/android/app/build/outputs/mapping/hermesRelease/resources.txt` | Before | After | |---|---| | `drawable/_reactnative_libraries_newappscreen_components_logo : reachable=false` | `drawable/_reactnative_libraries_newappscreen_components_logo : reachable=true` | Reviewed By: cortinico Differential Revision: D72960028 Pulled By: huntie fbshipit-source-id: df725fa2ea50150cd67687a97986976ffbbb5b40
This commit is contained in:
committed by
Facebook GitHub Bot
parent
748183ded0
commit
864833fca9
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
* @oncall react_native
|
||||
*/
|
||||
|
||||
import type {AssetData} from 'metro/src/Assets';
|
||||
|
||||
import assetPathUtils from './assetPathUtils';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
async function createKeepFileAsync(
|
||||
assets: $ReadOnlyArray<AssetData>,
|
||||
outputDirectory: string,
|
||||
): Promise<void> {
|
||||
if (!assets.length) {
|
||||
return;
|
||||
}
|
||||
const assetsList = [];
|
||||
for (const asset of assets) {
|
||||
const prefix = assetPathUtils.drawableFileTypes.has(asset.type)
|
||||
? 'drawable'
|
||||
: 'raw';
|
||||
assetsList.push(
|
||||
`@${prefix}/${assetPathUtils.getResourceIdentifier(asset)}`,
|
||||
);
|
||||
}
|
||||
const keepPath = path.join(outputDirectory, 'raw/keep.xml');
|
||||
const content = `<resources xmlns:tools="http://schemas.android.com/tools" tools:keep="${assetsList.join(',')}" />\n`;
|
||||
await fs.promises.mkdir(path.dirname(keepPath), {recursive: true});
|
||||
await fs.promises.writeFile(keepPath, content);
|
||||
}
|
||||
|
||||
export default createKeepFileAsync;
|
||||
Reference in New Issue
Block a user