我想做一个应用程序与Flutter ,可以安装其他APK.当我try 使用flutter_app_installerapp_installer个包安装APK时,手机上会弹出一个消息:"解析该包时出错."

通常,此错误消息表示.apk文件已损坏.然而,使用Android上的文件资源管理器,我可以很好地安装APK.

此外,可能存在许可问题.我的应用程序请求存储权限和安装APK的权限,我批准了这些权限.

如上所述,我已经try 了安装APK的不同Ffltter包.两者都抛出相同的错误消息.

当我故意提供到APK的错误路径时,我没有收到错误消息.我这样做是为了验证到APK的路径是否正确.

我试着在两台安卓设备上运行这个程序.

Flutter 调试控制台中没有显示错误消息,仅在电话上显示.

Here the error message (on a german phone): Error message shown on phone

我试着用谷歌搜索和询问闲聊,但找不到任何有用的建议.我在这一点上被困了一段时间.

这是我的Flutter 翼代码:

import 'dart:io';

import 'package:app_installer/app_installer.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

import 'package:path/path.dart' as path;
import 'package:flutter_app_installer/flutter_app_installer.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // Request runtime permission
  await Permission.storage.request();
  await Permission.requestInstallPackages.request();

  runApp(const MainApp());
}

class MainApp extends StatelessWidget {
  const MainApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: TextButton(
            onPressed: () async {
              await AppInstaller.installApk(
                "storage/emulated/0/Download/test.apk",
              );
            },
            child: const Text('Install App'),
          ),
        ),
      ),
    );
  }
}

这是我的Android Manifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.apk_install_test">
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
    <application
        android:label="apk_install_test"
        android:name="${applicationName}"
        android:icon="@mipmap/ic_launcher">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            <!-- Specifies an Android theme to apply to this Activity as soon as
                 the Android process has started. This theme is visible to the user
                 while the Flutter UI initializes. After that, this theme continues
                 to determine the Window background behind the Flutter UI. -->
            <meta-data
              android:name="io.flutter.embedding.android.NormalTheme"
              android:resource="@style/NormalTheme"
              />
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <!-- Provider -->
        <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>
        <!-- Don't delete the meta-data below.
             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
        <meta-data
            android:name="flutterEmbedding"
            android:value="2" />
    </application>
</manifest>

推荐答案

在三天的痛苦之后,我做到了:)

以下是我所做的: 首先,我try 了每一个安装APK的Flutter 翼插件,但都不起作用. 然后我转向了基于意图的方法,这种方法有更多的问题. OPEN_FILE插件是另一种方式,但我也没弄对.

所以我不得不更深入地挖掘. 在较新的Android版本中,存储权限要严格得多.因此,大多数文件IO不可能使用基本权限.我不得不用Permission.manageExternalStorage.request()来请求MANAGE_EXTERNAL_STORAGE

只有在拥有该权限的情况下,我才能访问下载文件夹.当然,您必须将下载文件夹添加到您的文件_paths.xml,如下所示:<external-path name="external_files_download" path="Download" />

我的应用程序不是要从下载文件夹安装APK,而是应用程序直接下载的APK,这些APK存储在应用程序自己的数据文件夹中.据我所知,这个数据文件夹是一个受保护的文件夹,它禁止其他应用程序(如apk安装程序)读取该文件.

我必须编写这个函数来将APK从内部存储复制到外部存储.只有在那之后,我才能访问并安装它:

/// Copies the given file to the external storage directory and returns the new file
/// Needed, because the app can only install apks from the external storage directory
Future<File> copyFileToExternalStorage(File sourceFile) async {
  String fileName = sourceFile.path.split('/').last; // Extract the file name
  Directory? externalDir = await getExternalStorageDirectory(); // Get external directory

  if (externalDir == null) {
    throw Exception("External storage directory not found");
  }

  File destinationFile = File('${externalDir.path}/$fileName'); // Create new file at destination path

  await sourceFile.copy(destinationFile.path); // Copy source file to destination

  return destinationFile; // Return the new file object
}

有了这些更改,我可以使用pub.dev中的apk安装程序插件.需要注意的是,不同的插件需要在androidManifest.xml中使用不同的提供者名称 因此,请记住将其命名,如插件文档中所述.

Flutter相关问答推荐

Flutter bloc -如何使用BlocBuilder?

Android NFC未收到空的NFC标签

我如何才能动态地将小部件排成一排进行排列呢?

如何在Flutter 中不从getX库中初始化GetxController中的变量

如何组合匹配器

关闭模式,但再次打开时微件条件相同

如何删除使用isscllable时出现的左边距?

在Ffltter中,我已经使用了Ffltter_BLOC Cupit和Pusher,但当获得新数据时,UI没有更新

Flutter GestureDetector单击问题-堆叠外面的按钮不可点击

flatter_localizations错误在Null值上使用了Null判断运算符

Flutter - Stripe:类型List 不是类型Map 的子类型? method_channel_stripe.dart 中的错误:263

带按钮的 Riverpod future Provider

NotifierProvider 每次更新都会重新构建并且值不更新

如何在Flutter中动态绘制一条从A点到B点的线?

我想在文本结束后显示分隔线.它会在第一行、第二行或第三行结束

ListView 后台可见

容器在发布模式下显示灰色框(抖动)

如何在椭圆形图像周围添加边框?Flutter

如何在Flutter 中制作自定义微调器?

这在Flutter 的 pubspec 文件中是什么意思?