Android install apk mit Intent.VIEW_ACTION funktioniert nicht mit Dateianbieter

73

Meine App verfügt über eine automatische Aktualisierungsfunktion, mit der eine APK heruntergeladen werden kann. Wenn der Download abgeschlossen ist, wird eine Intent.VIEW_ACTION zum Öffnen der App und zum Installieren der heruntergeladenen APK durch den Benutzer angezeigt

         Uri uri = Uri.parse("file://" + destination);
         Intent install = new Intent(Intent.ACTION_VIEW);
        install.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        install.setDataAndType(uri,
            manager.getMimeTypeForDownloadedFile(downloadId));
        activity.startActivity(install);

Dies funktioniert hervorragend für alle Geräte <24

Mit Android 24 dürfen wir anscheinend keine Absichten mehr mit file: /// beginnen, und nach einigem googeln wurde empfohlen, A File Provider zu verwenden

neuer Code:

Intent install = new Intent(Intent.ACTION_VIEW);
    install.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    install.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    Uri apkUri = FileProvider.getUriForFile(AutoUpdate.this,
        BuildConfig.APPLICATION_ID + ".provider", file);
    install.setDataAndType(apkUri,
        manager.getMimeTypeForDownloadedFile(downloadId));
    activity.startActivity(install);

Jetzt activity.startActivity (installieren); wirft einen Fehler

Es wurde keine Aktivität für Intent gefunden {act = android.intent.action.VIEW dat = content: //com.xxxx.xx.provider/MyFolder/Download/MyApkFile.apk typ = application / vnd.android.package-archive flg = 0x4000000}

Kann ich den APK-Viewer in Android 7 (24) auf irgendeine Weise öffnen?

Johny19
quelle
3
Verwenden Sie ACTION_INSTALL_PACKAGE. Das hat bei mir ab der Juni-Ausgabe der N Developer Preview funktioniert .
CommonsWare
2
Keine Aktivität gefunden, um Intent zu handhaben {act = android.intent.action.INSTALL_PACKAGE ...
Johny19

1
Ich habe keine Probleme damit ACTION_INSTALL_PACKAGE, FileProviderdie APK zu verwenden, wenn ich sie auf einem Nexus 9 mit Android 7.0 (Build NRD90M) teste. Verglichen mit Ihrem Intent, abgesehen von dem Unterschied zwischen Aktionszeichenfolgen, verwende ich nicht FLAG_ACTIVITY_CLEAR_TOPund ich verwende setData()eher als setDataAndType().
CommonsWare

Woher bekommst du "Datei"? das letzte Argument ingetUriForFile
Lenin Raj Rajasekaran

Antworten:


165

Nach vielen Versuchen konnte ich dieses Problem lösen, indem ich verschiedene Absichten für etwas niedrigeres als Nougat erstellte, indem ich mit dem FileProvider eine Installationsabsicht mit Android-Versionen erstellte, bevor Nougat den Fehler verursacht:

ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.INSTALL_PACKAGE dat=content://XXX.apk flg=0x1 }

Bei Verwendung eines normalen Uri unter Android erzeugt Nougat den folgenden Fehler:

FileUriExposedException: file:///XXX.apk exposed beyond app through Intent.getData()

Meine Lösung, die für mich mit Android N auf dem Emulator und einem Telefon mit Android M funktioniert.

File toInstall = new File(appDirectory, appName + ".apk");
Intent intent;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    Uri apkUri = FileProvider.getUriForFile(activity, BuildConfig.APPLICATION_ID + ".fileprovider", toInstall);
    intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
    intent.setData(apkUri);
    intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
} else {
    Uri apkUri = Uri.fromFile(toInstall);
    intent = new Intent(Intent.ACTION_VIEW);
    intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
activity.startActivity(intent);

UPDATE FÜR Android Nougat 7.1:

Sie müssen auch die Berechtigung REQUEST_INSTALL_PACKAGES in Ihrem Manifest hinzufügen. Es ist ab Api Level 23 (Android 6.0 Marshmallow) verfügbar und ab Level 25 (Android 7.1 Nougat) erforderlich.

AKTUALISIEREN:

Denken Sie daran, die Berechtigungen zum Lesen und Schreiben in den externen Speicher anzufordern, wenn sich die zu installierende Datei im externen Speicher befindet. Und auch, um einen korrekten FileProvider für Android Nougat und höher einzurichten.

Überprüfen Sie zunächst, ob Sie über eine Schreibberechtigung verfügen, indem Sie canReadWriteExternal()unten anrufen . Wenn nicht, rufen Sie requestPermission()vorher an:

private static final int REQUEST_WRITE_PERMISSION = 786;

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    if (requestCode == REQUEST_WRITE_PERMISSION && grantResults[0] == PackageManager.PERMISSION_GRANTED)
        Toast.makeText(this, "Permission granted", Toast.LENGTH_LONG).show();
}

private void requestPermission() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
        requestPermissions(new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_WRITE_PERMISSION);
}

private boolean canReadWriteExternal() {
    return Build.VERSION.SDK_INT < Build.VERSION_CODES.M ||
            ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED;
}

Hier ist ein Beispiel eines Dateianbieters für den Download-Ordner im externen Speicher. AndroidManifest.xml :

<application ... >
    ...

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

resources / xml / filepaths.xml :

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_download" path="Download"/>
</paths>

Wenn beim Installieren der .apk-Datei eine Fehlermeldung angezeigt wird, die besagt, dass beim Parsen des Pakets ein Problem vorliegt. Möglicherweise haben Sie nicht nach der Lese- / Schreibberechtigung gefragt, oder die Datei, die Sie installieren möchten, ist nicht vorhanden oder beschädigt.


3
Mein Problem war, dass ich setFlagszweimal anrief, was im Grunde das erste Flag zurücksetzt. Ich musste addFlagstattdessen verwenden.
Lenin Raj Rajasekaran

1
Ich habe das getan, aber es wird die Fehlermeldung "Es gibt ein Problem beim Parsen des Pakets" angezeigt. Wenn ich ein öffentliches Verzeichnis verwende, ist der Fehler behoben, aber leider muss ich ein privates verwenden.
Kuri

33
Tolle Antwort - danke! Erwähnenswert ist, dass Sie ab Oreo Ihrem Manifest auch die Berechtigung REQUEST_INSTALL_PACKAGES hinzufügen müssen . Ansonsten scheitert es nur lautlos.
Jasongiss

5
In Android Oreo benötigen Sie: if (Build.VERSION.SDK_INT> = Build.VERSION_CODES.O && (! GetPackageManager (). CanRequestPackageInstalls ())) {startActivityForResult (new Intent (Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES .format ("package:% s", getPackageName ())), 1234); }
jpussacq
2
Machen Sie sich eine Notiz <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />erforderlich für Android N (Nougot)
Akash Amin

Antworten:

13

Ich hatte dieses Problem beim Aufrufen der Startaktivität. Nachdem ich meine aktuelle Aktivität angehalten hatte, kam sie plötzlich zurück und rief onResume auf. als wäre nichts passiert. Mein Problem war mit dieser Erlaubnis im Manifest:

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

Das hat fast niemand erwähnt. Also denk daran. in sdk> = 24 müssen Sie den Provider verwenden, da eine Absicht erforderlich ist, die mit file: /// unter sdk 24 beginnt. Sie sollten uri beginnend mit content: /// angeben. Deshalb benötigen wir den Dateianbieter für sdk 24 und höher. Ich glaube nicht, dass ich dafür Codes schreiben muss, da @just_user die richtige Antwort geschrieben hat.

Hossein Karami
quelle
6

Dies könnte das Problem sein, das Sie haben

intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 

in deinem Beispiel sollte es sein

install.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

as install ist der Name der Absicht.

Rob Francis
quelle
Hat etwas für Sie geklappt? Ich kann meine Datei uri nicht mit der E-Mail-Anwendung teilen.
Sunita
2
Ich habe von file: /// auf content: // umgestellt und hatte seit einiger Zeit Probleme beim Öffnen der Dateien vom Dateianbieter. Dies hat mein Problem behoben.
Gebühren71
2

Sie sollten beachten, dass Sie für API <24 Folgendes verwenden müssen:

        setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive")

anstatt Daten einzustellen und separat einzugeben:

data = Uri.fromFile(apkFile)
    type = "application/vnd.android.package-archive"

Andernfalls erhalten Sie ActivityNotFoundException

MohammadReza
quelle
Nur immer verwenden, DataAndTypedann gibt es kein Problem.
Pierre