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

安卓设备如何获取所有非基础已安装APK的大小与URI并解决传输后无法安装问题

Solution: Get Full APK Size (Including Splits) & Fix Installation After Transfer

Alright, let's break down your problem and fix it step by step. Your current code only pulls data for the base APK, but modern apps (distributed via App Bundles) use split APKs—these are required for the app to run, which is why transferring just the base APK fails. Google Files Go shows the full size because it accounts for all split APKs tied to the app.

1. Fetch All APKs (Base + Splits) for Size & URIs

Starting from Android 10 (API 29), PackageInfo.splitSourceDirs gives you paths to all split APKs. For older versions, you can use ApplicationInfo.splitSourceDirs. Here's how to modify your Kotlin code to collect all APK files:

Updated Kotlin Code

try {
    packages = ArrayList()
    val packageManager = requireContext().packageManager
    // Add GET_SIGNATURES if you need to verify app integrity later
    val installedPackages = packageManager.getInstalledPackages(PackageManager.GET_META_DATA or PackageManager.GET_SIGNATURES)
    
    for (packageInfo in installedPackages) {
        val appInfo = packageInfo.applicationInfo
        // Skip system apps (adjust this if you want to include them)
        if (appInfo.flags and ApplicationInfo.FLAG_SYSTEM != 1) {
            val appLabel = appInfo.loadLabel(packageManager).toString()
            val versionName = packageInfo.versionName ?: "Unknown"
            val packageName = packageInfo.packageName

            // Collect all APK files: base + splits
            val allApkFiles = mutableListOf<File>()
            // Add base APK
            appInfo.publicSourceDir?.let { allApkFiles.add(File(it)) }
            // Add all split APKs
            packageInfo.splitSourceDirs?.forEach { splitPath ->
                allApkFiles.add(File(splitPath))
            }

            // Calculate total app size (sum of all APKs)
            val totalSize = allApkFiles.sumOf { it.length() }

            // Update your AppModel to handle multiple APKs (see below)
            val appModel = AppModel(
                friendlyName = appLabel,
                appInfo = appInfo,
                version = versionName,
                packageName = packageName,
                allApkFiles = allApkFiles,
                totalSize = totalSize
            )
            installedList.add(appModel)
            packageName.takeIf { it.isNotEmpty() }?.let { packages.add(it) }
        }
    }
} catch (ex: Exception) {
    ex.printStackTrace()
}

Adjust AppModel for Multiple APKs

Modify your Java AppModel to store all APK files and their URIs, plus the total size:

public class AppModel {
    public String friendlyName;
    public String version;
    public String packageName;
    public long totalSize;
    public List<File> allApkFiles;
    public List<Uri> allApkUris;
    public static final String MIME_TYPE = "application/vnd.android.package-archive";
    // Keep your other existing fields (appInfo, shareableFile, etc.)

    public AppModel(String friendlyName, ApplicationInfo appInfo, String version, String packageName, List<File> allApkFiles, long totalSize) {
        this.friendlyName = friendlyName;
        this.version = version;
        this.packageName = packageName;
        this.allApkFiles = allApkFiles;
        this.totalSize = totalSize;
        this.appInfo = appInfo;

        // Generate secure URIs (we'll fix the URI issue next)
        this.allApkUris = new ArrayList<>();
        for (File file : allApkFiles) {
            // Temporary: Replace this with FileProvider later
            this.allApkUris.add(Uri.fromFile(file));
        }

        // Update your shareableFile logic to account for all APKs if needed
    }

    // Add getters/setters as needed
}

2. Fix Installation After Transfer

Two issues cause installation failures:

  • Uri.fromFile() triggers FileUriExposedException on Android 7.0+ (API 24) because it exposes raw file paths.
  • Split APKs must be installed together with the base APK—you can't install them individually via a standard intent.

Fix 1: Use FileProvider for Secure URIs

First, register a FileProvider in your AndroidManifest.xml:

<application>
    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="${applicationId}.fileprovider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths" />
    </provider>
</application>

Create res/xml/file_paths.xml to define accessible paths:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Allow access to the app's installed APK files -->
    <files-path name="apk_files" path="." />
    <!-- Uncomment if you need to access APKs on external storage -->
    <!-- <external-path name="external_apks" path="." /> -->
</paths>

Now update the URI generation in AppModel to use FileProvider:

// Replace Uri.fromFile(file) with this:
Uri apkUri = FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", file);
// Grant temporary read permission to the receiving app
context.grantUriPermission(receiverPackageName, apkUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);

Fix 2: Install All APKs with PackageInstaller

To install split APKs, you need to use the PackageInstaller API (not a standard install intent). Here's a sample implementation:

public void installAllApks(Context context, List<File> apkFiles) {
    PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
    PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(
            PackageInstaller.SessionParams.MODE_FULL_INSTALL);
    
    int sessionId = 0;
    try {
        sessionId = packageInstaller.createSession(sessionParams);
        PackageInstaller.Session session = packageInstaller.openSession(sessionId);

        // Write all APKs to the installation session
        for (File apkFile : apkFiles) {
            try (OutputStream outputStream = session.openWrite(apkFile.getName(), 0, apkFile.length())) {
                Files.copy(apkFile.toPath(), outputStream);
                session.fsync(outputStream);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        // Submit the installation request
        Intent resultIntent = new Intent(context, InstallResultReceiver.class);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(
                context, 0, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        session.commit(pendingIntent.getIntentSender());
        session.close();
    } catch (IOException e) {
        e.printStackTrace();
        packageInstaller.abandonSession(sessionId);
    }
}

// BroadcastReceiver to handle installation results
public static class InstallResultReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE);
        String message = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
        
        switch (status) {
            case PackageInstaller.STATUS_SUCCESS:
                Toast.makeText(context, "App installed successfully!", Toast.LENGTH_SHORT).show();
                break;
            default:
                Toast.makeText(context, "Installation failed: " + message, Toast.LENGTH_LONG).show();
                break;
        }
    }
}

Don't forget to add the required permission to your manifest:

<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

Final Notes

  • Always request runtime permissions for accessing storage if you're dealing with APKs in external directories.
  • For Android 11+ (API 30), you may need to use MANAGE_EXTERNAL_STORAGE if accessing APKs outside your app's sandbox, but for installed app APKs, the above method should work without it.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 13:02:34