React Native新手求助:获取第三方已安装应用图标并传递至JS
Hey there! Let's work through this problem together. It looks like you're hitting two main hurdles: correctly fetching app icons on Android, and getting that data over to your React Native JS layer. Let's break this down step by step.
1. Fix the Native Module for Async Data & Icon Conversion
Your current getNonSystemApps method is synchronous and only returns a list of strings (likely package names), but we need to adjust it to:
- Use async logic (via Promises) to avoid blocking the UI thread
- Convert app icons to a format JS can render (Base64 strings)
- Pass structured data (app name, package name, icon) to JS
Here's a complete, corrected native module implementation:
import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableNativeArray; import com.facebook.react.bridge.WritableNativeMap; import java.io.ByteArrayOutputStream; import java.util.List; public class AppInfoModule extends ReactContextBaseJavaModule { private final ReactApplicationContext reactContext; public AppInfoModule(ReactApplicationContext reactContext) { super(reactContext); this.reactContext = reactContext; } @Override public String getName() { return "AppInfoModule"; // Name JS will use to call this module } @ReactMethod public void getNonSystemAppsWithIcons(Promise promise) { try { PackageManager packageManager = reactContext.getPackageManager(); List<PackageInfo> packages = packageManager.getInstalledPackages(0); WritableArray appList = new WritableNativeArray(); for (PackageInfo pkg : packages) { // Skip system apps if ((pkg.applicationInfo.flags & android.content.pm.ApplicationInfo.FLAG_SYSTEM) != 0) { continue; } WritableMap appData = new WritableNativeMap(); appData.putString("packageName", pkg.packageName); appData.putString("appName", pkg.applicationInfo.loadLabel(packageManager).toString()); // Convert app icon to Base64 for JS rendering Drawable iconDrawable = pkg.applicationInfo.loadIcon(packageManager); Bitmap iconBitmap = ((BitmapDrawable) iconDrawable).getBitmap(); ByteArrayOutputStream stream = new ByteArrayOutputStream(); iconBitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); byte[] iconBytes = stream.toByteArray(); String iconBase64 = android.util.Base64.encodeToString(iconBytes, android.util.Base64.DEFAULT); appData.putString("iconBase64", "data:image/png;base64," + iconBase64); appList.pushMap(appData); } promise.resolve(appList); // Send successful data to JS } catch (Exception e) { promise.reject("APP_INFO_ERROR", "Failed to fetch app details", e); // Pass errors to JS } } }
Next, register the module in a custom package:
import com.facebook.react.ReactPackage; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.uimanager.ViewManager; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class AppInfoPackage implements ReactPackage { @Override public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) { List<NativeModule> modules = new ArrayList<>(); modules.add(new AppInfoModule(reactContext)); return modules; } @Override public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) { return Collections.emptyList(); } }
Add the package to your MainApplication.java:
@Override protected List<ReactPackage> getPackages() { List<ReactPackage> packages = new PackageList(this).getPackages(); packages.add(new AppInfoPackage()); // Register our custom module return packages; }
2. Call the Native Method from Your React Native Component
Now you can fetch and display the app data in JS using the native module:
import { NativeModules, Image, ScrollView, View, Text, useEffect, useState } from 'react-native'; const { AppInfoModule } = NativeModules; const AppListScreen = () => { const [apps, setApps] = useState([]); useEffect(() => { fetchAppData(); }, []); const fetchAppData = async () => { try { const appData = await AppInfoModule.getNonSystemAppsWithIcons(); setApps(appData); } catch (error) { console.error('Error loading app data:', error); } }; return ( <ScrollView style={{ padding: 10 }}> {apps.map((app, index) => ( <View key={index} style={{ flexDirection: 'row', alignItems: 'center', paddingVertical: 8, borderBottomWidth: 1, borderBottomColor: '#eee' }} > <Image source={{ uri: app.iconBase64 }} style={{ width: 40, height: 40, borderRadius: 8 }} /> <View style={{ marginLeft: 12 }}> <Text style={{ fontSize: 16, fontWeight: '500' }}>{app.appName}</Text> <Text style={{ fontSize: 12, color: '#666', marginTop: 4 }}>{app.packageName}</Text> </View> </View> ))} </ScrollView> ); }; export default AppListScreen;
Key Fixes & Explanations
- Async Promise-Based Flow: We use
Promisein the native method to handle async operations (fetching app data can take time) and ensure data is properly passed to JS. - Icon Conversion: Android
Drawable/Bitmapcan't be sent directly to JS, so we convert icons to Base64 strings with a data URI prefix—this lets React Native'sImagecomponent render them directly. - Structured Data: Using
WritableMapandWritableArrayensures the native data is formatted correctly for the React Native bridge to convert into JS objects/arrays. - System App Filter: The check
(pkg.applicationInfo.flags & FLAG_SYSTEM) != 0skips system apps, which aligns with your goal of getting non-system apps.
Troubleshooting Tips
- If no data reaches JS:
- Double-check the module name in
getName()matches what you use in JS (AppInfoModule). - Verify the custom package is added to
MainApplication.java. - Check Android Logcat for native exceptions—any errors will be passed to JS via
promise.reject, but Logcat will show full details.
- Double-check the module name in
- If icons don't render:
- Ensure the Base64 string starts with
data:image/png;base64,(this tells React Native it's a valid data URI). - Test with a smaller compression quality (e.g., 80 instead of 100) if you hit memory issues with large icons.
- Ensure the Base64 string starts with
内容的提问来源于stack exchange,提问作者Gagan
相关产品推荐
相关产品推荐

