Fetch API在Android真机失效但模拟器正常的问题排查求助
Hey there! Let's tackle your issue where the app runs smoothly on the Android Studio emulator but shows a blank screen with a loading spinner on a real device. We'll cover the key warning you noticed and the most likely network-related culprits behind this mismatch.
First: Fix the "Unique Key" Warning (Critical for Stable Rendering)
Your hunch about the key warning is valid—while it might seem like a minor issue, missing or non-unique keys can break FlatList rendering behavior, which could contribute to your blank screen. Looking at your code:
keyExtractor={(item, index) => {item.title}}
There are two problems here:
- The arrow function doesn’t return anything (curly braces without a
returnstatement mean no value is output, so keys end up asundefined). - Using
item.titleisn’t reliable—titles might repeat, which violates the "unique key" requirement for list items.
Replace it with this, using the unique item.id field from your API response (always prefer unique identifiers over text fields):
keyExtractor={(item) => item.id.toString()}
This ensures every list item has a stable, unique key, helping React Native render the list correctly and efficiently.
Second: Fix the Real Device Network Block (Most Likely Cause of the Blank Screen)
Emulators often have looser network restrictions than real Android devices. Your API uses http (not https), and starting with Android 9 (API level 28), Google blocks cleartext (non-HTTPS) traffic by default. This means your real device can’t connect to your API at all, leaving the loading spinner stuck indefinitely.
Quick Fix: Allow Cleartext Traffic
Open your Android project's AndroidManifest.xml (located at android/app/src/main/AndroidManifest.xml) and add android:usesCleartextTraffic="true" to the <application> tag:
<application ... android:usesCleartextTraffic="true">
Recommended Long-Term Fix: Switch to HTTPS
For production apps, it’s best to migrate your API to use HTTPS. This avoids security risks and ensures compatibility with future Android versions that may enforce stricter network rules.
Third: Improve Error Handling to Debug Better
Right now, your fetch call’s catch block only logs errors to the console (which you can’t view on a real device). Let’s update it to stop the loading spinner and alert the user when something goes wrong:
.catch((error) => { console.log(error); this.setState({ isFetching: false }); // Stop the loading spinner Alert.alert('Oops!', 'Failed to load announcements. Check your network connection.'); });
This will confirm if the issue is network-related and prevent the loading state from running forever.
Fourth: Fix Your Search Function (Avoid Data Loss)
Your current search logic overwrites the original data state, which means after searching, refreshing the app will load filtered data instead of the full list. Let’s fix this by storing the original dataset:
- Update your state to include an
originalDatafield:
constructor(props) { super(props) this.state = { data: [], originalData: [], // Store the full API response here isFetching: false, value: '' // Uncomment this—it's required for the search input }; }
- Save the full dataset when loading data from the API:
.then(({ data }) => { this.setState({ data, originalData: data }); // Save both filtered and original data this.setState({ isFetching: false }); })
- Modify the search function to filter from
originalData:
searchFilterFunction = text => { this.setState({ value: text }); const newData = this.state.originalData.filter(item => { const itemData = `${item.title.toUpperCase()} ${item.department.toUpperCase()}`; const textData = text.toUpperCase(); return itemData.includes(textData); }); this.setState({ data: newData }); };
Final Steps to Test
- Apply all the fixes above.
- Rebuild your APK (use
./gradlew assembleReleaseor Android Studio’s build menu). - Install the new APK on your real device.
With these changes, your app should load data correctly on both the emulator and real device, and the search functionality will work reliably too.
内容的提问来源于stack exchange,提问作者Atiffaniq

