Android - 尝试从服务器下载和安装 .apk 时下载失败并出现解析错误

Android - Download Unsuccessful and Parsing Error while trying to download and install .apk from server

目标是在不使用 google Play 商店的情况下更新我的应用程序。我正在尝试从服务器下载 .apk 文件,然后以编程方式安装它。我目前收到一个错误,提示下载不成功,解析包时出错。我在运行时请求了 READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE 权限。我不得不进入平板电脑的设置以授予“安装未知应用程序”的权限。我无法在运行时请求 REQUEST_INSTALL_PACKAGES。

如果我更改文件名并更新 URL 以获取与 .apk 存储在服务器上相同文件夹中的 .txt 并注释掉“.setMimeType()”,我可以下载并查看.txt 文件。

这里有一些代码片段可以提供帮助

gradle:app

android {
    compileSdk 31
    buildToolsVersion "30.0.3"

    defaultConfig {
        applicationId "farrpro.project"
        minSdk 28
        targetSdk 30
    }

AndroidManifest.xml

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
    <uses-permission android:name="android.permission.ACTION_INSTALL_PACKAGE"/>

MainActivity.java


private void hasInstallPermission() { // runs in onCreate() 
        if (getApplicationContext().checkSelfPermission(Manifest.permission.REQUEST_INSTALL_PACKAGES) != PackageManager.PERMISSION_GRANTED) {
            requestPermissions(new String[]{Manifest.permission.REQUEST_INSTALL_PACKAGES}, 1);
        }
    }


void downloadAndInstallUpdate(){ // runs when users accepts request to download update
        String fileName = "update.apk";
    // example of update’s string
        update = “https://myserver.net/update.apk”;

        //set download manager
        DownloadManager.Request request = new DownloadManager.Request(Uri.parse(update));
        request.setTitle(fileName)
                .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
                .setDescription("Downloading")
                //.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI | DownloadManager.Request.NETWORK_MOBILE)
                .setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName)
                .setMimeType("application/vnd.android.package-archive");


        // get download service and enqueue file
        final DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
        final long downloadId = manager.enqueue(request);
        System.out.println("Max Bytes: "+DownloadManager.getMaxBytesOverMobile(this)); //returns null


        //set BroadcastReceiver to install app when .apk is downloaded
        BroadcastReceiver onComplete = new BroadcastReceiver() {
            public void onReceive(Context ctxt, Intent intent) {
                System.out.println("Is download successful: "+ manager.getUriForDownloadedFile(downloadId)); //returns null
                System.out.println("Mime: "+manager.getMimeTypeForDownloadedFile(downloadId)); //returns application/vnd.android.package-archive

                Intent install = new Intent(Intent.ACTION_VIEW);
                File file = new File(ctxt.getExternalFilesDir(null) + fileName);
                Uri downloadedApk = FileProvider.getUriForFile(ctxt, "farrpro.project.provider", file);

                install.setDataAndType(downloadedApk,
                        manager.getMimeTypeForDownloadedFile(downloadId));
                install.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
                        .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                        .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                try {
                    startActivity(install); 
                } catch (ActivityNotFoundException e) {
                    e.printStackTrace();
                    Log.e("TAG", "Error in opening the file!");// this never prints 
                }

                unregisterReceiver(this);
                finish();
            }
        };

        //register receiver for when .apk download is complete
        registerReceiver(onComplete, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
    }

如果您的应用 运行 Android O 或更高版本,您必须允许应用的未知应用来源。

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    if (!context.packageManager.canRequestPackageInstalls()) {
        val intent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES).apply {
            data = Uri.parse(String.format("package:%s", context.packageName))
            addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        }
        context.startActivity(intent)
    }
}

问题是服务器限制了下载类型。更改该设置后,我的代码或多或少地起作用了。我最终需要更改广播接收器中的意图,以便在下载完成后立即安装新的 .apk。以下是我为使发布的代码正常工作所做的更改。

 final long downloadId = manager.enqueue(request);

BroadcastReceiver onComplete = new BroadcastReceiver() {
            public void onReceive(Context ctxt, Intent intent1) {

                Intent intent = new Intent(Intent.ACTION_VIEW);
                intent.setDataAndType(manager.getUriForDownloadedFile(downloadId), "application/vnd.android.package-archive");
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                try {
                    getApplicationContext().startActivity(intent);
                } catch (ActivityNotFoundException e) {
                    e.printStackTrace();
                    Log.e("TAG", "Error in opening the file!");
                }

                unregisterReceiver(this);
                finish();
            }
        };