You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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 Promise in 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/Bitmap can't be sent directly to JS, so we convert icons to Base64 strings with a data URI prefix—this lets React Native's Image component render them directly.
  • Structured Data: Using WritableMap and WritableArray ensures 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) != 0 skips 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.
  • 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.

内容的提问来源于stack exchange,提问作者Gagan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 07:56:59