Files
react-native/packages
hublot b675667a47 Replace RCTLocalAssetImageLoader to RCTBundleAssetImageLoader (#37232)
Summary:
From the video below, we can see that the UI thread has dropped many frames, and it would become worse if there are multiple images.

If an image is located in the sandbox of the disk, we cannot load it using `RCTLocalAssetImageLoader` because `RCTLocalAssetImageLoader.requiresScheduling` is set to true, which loads the data on the UI thread and causes main thread stuttering. This will affect libraries such as `react-native-code-push` and others that save images to the sandbox of the disk.

Therefore, we should replace `RCTLocalAssetImageLoader.canLoadImageURL` from `RCTIsLocalAssetURL(url)` to `RCTIsBundleAssetURL(url)`. Similarly, we should rename the entire `RCTLocalAssetImageLoader` file with `RCTBundleAssetImageLoader`, which ignores images in the disk sandbox. And finally these images will be loaded from `NSURLRequest`, and our UI thread will run smoothly again.

https://user-images.githubusercontent.com/20135674/236368418-8933a2c6-549c-40d3-a551-81b492fe41d5.mp4

## Changelog:

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

[ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests
-->

[IOS] [Breaking] - Replace `RCTLocalAssetImageLoader` to `RCTBundleAssetImageLoader`

Pull Request resolved: https://github.com/facebook/react-native/pull/37232

Test Plan:
Test Code:
```javascript
constructor(props) {
  super(props)
  this.state = {
    bundle_image: require('./large_image.png'),
    sandbox_image: '',
    source: null,
    isLoading: false,
  }
}

render() {
  console.log('render', this.state)
  return (
    <View style={{ flex: 1, padding: 50, backgroundColor: 'white'}}>
      <View style={{ flexDirection: 'row', alignItems: 'center', height: 70}}>
      {
        [{ title: 'Save Image To SandBox', onPress: () => {
          let image = Image.resolveAssetSource(this.state.bundle_image)
          console.log(image.uri)
          this.setState({ isLoading: true })
          RNFetchBlob.config({ fileCache: true, appendExt: "png" }).fetch("GET", image.uri).then(response => {
            let path = response.path()
            path = /^file:\/\//.test(path) ? path : 'file://' + path
            console.log(path)
            this.state.sandbox_image = path
          }).finally(() => this.setState({ isLoading: false }))
        }}, { title: 'Load From SandBox', onPress: () => {
          this.setState({ source: { uri: this.state.sandbox_image } })
        }}, { title: 'Clear', onPress: () => {
          this.setState({
              source: null,
              isLoading: false
          })
        }}, { title: 'Load From Bundle', onPress: () => {
          this.setState({ source: this.state.bundle_image })
        }}].map((item, index) => {
          return (
              <Pressable
                  key={index}
                  style={{ height: '100%', justifyContent: 'center', flex: 1, borderWidth: 1, borderColor: 'black', marginLeft: index > 0 ? 15 : 0 }}
                  onPress={item.onPress}
              >
                  <Text style={{ textAlign: 'center' }}>{item.title}</Text>
              </Pressable>
          )
        })
      }
      </View>
      <ActivityIndicator style={{ marginTop: 10 }} animating={this.state.isLoading} />
      <Image
        key={`${this.state.source}`}
        style={{ marginTop: 20, width: 200, height: 200 }}
        source={this.state.source}
        onProgress={({ nativeEvent }) => console.log(nativeEvent)}
        onLoadStart={() => this.setState({ isLoading: true })}
        onLoadEnd={() => this.setState({ isLoading: false })}
      />
    </View>
  )
}

```
It needs to be tested in three environments: [Simulator_Debug, RealDevice_Debug, RealDevice_Release]

1. Open `Perf Monitor` (RealDevice_Release can be skipped)
2. Click `Save Image to SandBox`
3. Wait for the loading to end and click `Load From SandBox`
4. Verify that the image can be loaded successfully
5. Verify that the `UI thread` keeps `60 FPS` (RealDevice_Release can be skipped)
6. Click `Clear`
7. Repeat steps [3, 4, 5, 6] several times
8. Click `Load From Bundle` to verify that the bundle image can be loaded successfully

Simulator_Debug

https://user-images.githubusercontent.com/20135674/236369344-ee1b8ff1-2d49-49f3-a322-d973f4adf3e7.mp4

RealDevice_Debug

https://user-images.githubusercontent.com/20135674/236369356-fe440b2b-f72a-49be-b63c-b4bf709dac8c.mp4

RealDevice_Release

https://user-images.githubusercontent.com/20135674/236369365-8a6a5c2f-09ad-4c90-b6bd-41e8a5e3aa7f.mp4

Reviewed By: rshest

Differential Revision: D46441513

Pulled By: dmytrorykun

fbshipit-source-id: 652febd4147dbff6c1ceef03d84ce125b8c66770
2023-07-20 10:55:54 -07:00
..